LearnOpenGL 笔记 - 入门 04 你好,三角形
系列文章目录
- LearnOpenGL 笔记 - 入门 01 OpenGL
- LearnOpenGL 笔记 - 入门 02 创建窗口
- LearnOpenGL 笔记 - 入门 03 你好,窗口
文章目录
- 系列文章目录
- 前言
- 你好,三角形
- 顶点输入
- 顶点着色器(Vertex Shader)
- 编译着色器
- 片段着色器(Fragment Shader)
- 着色器程序
- 链接顶点属性
- 顶点数组对象(VAO)
- 三角形!
- 元素缓冲对象
前言
- 原文链接:你好,三角形
- 本文代码:2_1_hello_triangle.cpp
本文难度较大,学习曲线突然陡峭了起来。但没有关系,我将以一个初学者的视角来讲述自己的理解,帮助你学习 VAO、VBO、EBO、Shader 等概念。首先,仍然先以知识点列表的形式总结全文。
你好,三角形
- 先记住三个单词
- 顶点数组对象:Vertex Array Object,VAO
- 顶点缓冲对象:Vertex Buffer Object,VBO
- 元素缓冲对象:Element Buffer Object,EBO 或 索引缓冲对象 Index Buffer Object,IBO
- OpenGL 中所有事物都在 3D 空间,而屏幕是 2D。3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线
- 图形渲染管线分为两个主要部分
- 3D坐标转换为2D坐标
- 2D坐标转变为实际的有颜色的像素
- 图形渲染管线可分为几个阶段,每个阶段将会把前一个阶段的输出作为输入。这些阶段容易并行执行。显卡中的核心,运行着每个阶段的小程序,这些小程序被叫做着色器(Shader)
- 有些阶段的 Shader 可以有开发者来编写。OpenGL 着色器是用 OpenGL 着色器语言(OpenGL Shading Language, GLSL)写成的
- 下图为渲染管线的每个阶段的抽象展示。蓝色部分可以输入自定义的 shader
- 管线的第一部分是顶点着色器(Vertex Shader),它输入一个顶点。作用是把 3D 坐标转为另一种 3D 坐标(后面会解释),同时顶点着色器允许我们对顶点属性进行一些基本处理。
- 图元装配(Primitive Assembly)阶段将顶点着色器输出的所有顶点作为输入(如果是GL_POINTS,那么就是一个顶点),并所有的点装配成指定图元的形状;本节例子中是一个三角形。
- 图元装配阶段的输出会传递给几何着色器(Geometry Shader)。几何着色器把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状。例子中,它生成了另一个三角形。
- 几何着色器的输出会被传入光栅化阶段(Rasterization Stage),这里它会把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。
- 片段着色器的主要目的是计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。
- 最终的对象将会被传到最后一个阶段,我们叫做Alpha测试和混合(Blending)阶段。这个阶段检测片段的对应的深度(和模板(Stencil))值(后面会讲),用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。这个阶段也会检查alpha值(alpha值定义了一个物体的透明度)并对物体进行混合(Blend)。所以,即使在片段着色器中计算出来了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜色也可能完全不同。
- 在现代OpenGL中,我们必须定义至少一个顶点着色器和一个片段着色器。
顶点输入
- 绘制图形需要顶点,OpenGL 中顶点采用 3D 坐标,即 x,y 和 z,范围在 [-1, 1] 之间,我们称这个范围叫 标准化设备坐标(Normalized Device Coordinates)。在范围外的点不会显示。
float vertices[] = {-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f,0.0f, 0.5f, 0.0f
};
- 标准化设备坐标通过
glViewport
函数进行视口变化,转换为屏幕空间坐标。 vertices
中的点现在存放在内存中(你的代码内存中),我们需要将它们送至顶点着色器。顶点着色器会在显存中开辟一块空间来存放这些点。同时你要告诉 OpenGL 如何解释这些数据。- 顶点缓冲对象(Vertex Buffer Objects, VBO)负责管理这个显存。
unsigned int VBO;
glGenBuffers(1, &VBO);
- 使用
glGenBuffers
创建 VBO glBindBuffer(GL_ARRAY_BUFFER, VBO);
将这个 VBO 绑定到 OpenGL Context 中的顶点缓冲对象。glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
将内存中数据复制到显存中- GL_STATIC_DRAW :数据不会或几乎不会改变。
- GL_DYNAMIC_DRAW:数据会被改变很多。
- GL_STREAM_DRAW :数据每次绘制时都会改变。
顶点着色器(Vertex Shader)
- 现代OpenGL需要我们至少设置一个顶点和一个片段着色器。使用 GLSL(OpenGL Shading Language)编写顶点着色器。
#version 330 core
layout (location = 0) in vec3 aPos;void main()
{gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
#version 330 core
声明版本和核心模式。OpenGL 3.3以及和更高版本中,GLSL版本号和OpenGL的版本是匹配的。in
声明输入顶点属性(Input Vertex Attribute)vec3
向量类型,vec.x、vec.y、vec.z 获取不同分量gl_Position
是 Vertex Shader 的输出。我们必须把位置数据赋值给它。它是一个vec4
类型
编译着色器
const char *vertexShaderSource = R"(#version 330layout (location = 0) in vec3 aPos;layout (location = 1) in vec3 bPos;void main(){gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);}
)";auto vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"<< infoLog << std::endl;return -1;
}
glCreateShader(GL_VERTEX_SHADER)
创建一个顶点着色器,类型是 GL_VERTEX_SHADER 表明其类型。glShaderSource(vertexShader, 1,&vertexShaderSource, NULL);
设置着色器对象的源码。glCompileShader(vertexShader);
编译该着色器。glGetShaderiv
检查是否编译成功
片段着色器(Fragment Shader)
- 片段着色器所做的是计算像素最后的颜色输出
#version 330 core
out vec4 FragColor;void main()
{FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
- 片段着色器只需要输出一个变量。使用
out
关键字定义该变量。 - 编译片段着色器与编译顶点着色器类似
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
着色器程序
- 着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本。
- 我们必须将多个编译好的着色器链接(Link)为一个着色器程序对象,在渲染时激活它。
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog)std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n<< infoLog << std::endl;return -1;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);glUseProgram(shaderProgram);
glCreateProgram
创建着色器程序对象glAttachShader
将顶点着色器和片段着色器附加到程序对象上(程序对象属性的修改)glLinkProgram(shaderProgram);
链接它们。glUseProgram(shaderProgram);
将该程序对象绑定至 OpenGL Context,激活它。glDeleteShader
,Link 以后记得删除着色器对象,我们不再需要它们了。
链接顶点属性
- 之前,我们通过 VBO 将一堆数据送给了顶点着色器。允许我们指定任何以顶点属性为形式的输入。
- 这具有很强的灵活性,还意味着我们必须手动指定输入数据的哪一个部分对应顶点着色器的哪一个顶点属性。
glVertexAttribPointer
告诉 OpenGL 如何解释 VBO 中的数据glEnableVertexAttribArray
启用顶点属性;顶点属性默认是禁用的。
顶点数组对象(VAO)
- OpenGL的核心模式要求我们使用VAO,所以它知道该如何处理我们的顶点输入。如果我们绑定VAO失败,OpenGL会拒绝绘制任何东西。
- 一个顶点数组对象会储存以下这些内容:
- glEnableVertexAttribArray和glDisableVertexAttribArray的调用。
- 通过glVertexAttribPointer设置的顶点属性配置。
- 通过glVertexAttribPointer调用与顶点属性关联的顶点缓冲对象。
glGenVertexArrays
创建 VAO- 使用 VAO 和 VBO 绘制图形流程,如下:
// ..:: 初始化代码(只运行一次 (除非你的物体频繁改变)) :: ..
// 1. 绑定VAO
glBindVertexArray(VAO);
// 2. 把顶点数组复制到缓冲中供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);[...]// ..:: 绘制代码(渲染循环中) :: ..
// 4. 绘制物体
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
someOpenGLFunctionThatDrawsOurTriangle();
三角形!
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
- glDrawArrays函数,它使用当前激活的着色器,之前定义的顶点属性配置,和VBO的顶点数据(通过VAO间接绑定)来绘制图元
元素缓冲对象
- 绘制一个矩形需要 4 个顶点,但 glDrawArrays 只能绘制点、线、和三角形。并不能绘制矩形。幸运的是,你可以绘制两个三角形来达成这个目的。顶点如下:
float vertices[] = {// 第一个三角形0.5f, 0.5f, 0.0f, // 右上角0.5f, -0.5f, 0.0f, // 右下角-0.5f, 0.5f, 0.0f, // 左上角// 第二个三角形0.5f, -0.5f, 0.0f, // 右下角-0.5f, -0.5f, 0.0f, // 左下角-0.5f, 0.5f, 0.0f // 左上角
};
- 指定右下角和左上角两次!一个矩形只有4个而不是6个顶点,这样就产生50%的额外开销。更好的解决方案是只储存不同的顶点,并设定绘制这些顶点的顺序。这样子我们只要储存4个顶点就能绘制矩形了。EBO 就是做这个的。
float vertices[] = {0.5f, 0.5f, 0.0f, // 右上角0.5f, -0.5f, 0.0f, // 右下角-0.5f, -0.5f, 0.0f, // 左下角-0.5f, 0.5f, 0.0f // 左上角
};unsigned int indices[] = {// 注意索引从0开始! // 此例的索引(0,1,2,3)就是顶点数组vertices的下标,// 这样可以由下标代表顶点组合成矩形0, 1, 3, // 第一个三角形1, 2, 3 // 第二个三角形
};
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
- 使用
glDrawElements
使用当前绑定的索引缓冲对象中的索引进行绘制。
相关文章:
LearnOpenGL 笔记 - 入门 04 你好,三角形
系列文章目录 LearnOpenGL 笔记 - 入门 01 OpenGLLearnOpenGL 笔记 - 入门 02 创建窗口LearnOpenGL 笔记 - 入门 03 你好,窗口 文章目录系列文章目录前言你好,三角形顶点输入顶点着色器(Vertex Shader)编译着色器片段着色器&…...
keepalived+mysql高可用
一.设置mysql同步信息两节点安装msyql略#配置节点11.配置权限允许远程访问mysql -u root -p grant all on *.* to root% identified by Root1212# with grant option; flush privileges;2.修改my.cnf#作为主节点配置(节点1)#作为主节点配置 server-id 1 …...
JAVA工具篇--1 Idea中 Gradle的使用
前言: 既然我们已经使用Maven 来完成对项目的构建,为什么还要使用Gradle 进行项目的构建;gradle和maven都可以作为java程序的构建工具,但两者还是有很大的不同之处的:1.可扩展性,gradle比较灵活,…...
弄懂自定义 Hooks 不难,改变开发认知有点不习惯
前言 我之前总结逻辑重用的时候,就一直在思考一个问题。 对于逻辑复用,render props 和 高阶组件都可以实现,同样官方说 Hooks 也可以实现,且还是在不增加额外的组件的情况下。 但是我在项目代码中,没有找到自定义 …...
Java面向对象基础
文章目录面向对象类注意事项内存机制构造器this关键字封装javabean格式成员变量和局部变量区别static静态关键字使用成员方法使用场景内存机制注意事项static应用:工具类static应用:代码块静态代码块实例代码块(用的比较少)static…...
基于python下selenium库实现交互式图片保存操作(批量保存浏览器中的图片)
Selenium是最广泛使用的开源Web UI(用户界面)自动化测试套件之一,可以通过编程与浏览量的交互式操作对网页进行自动化控制。基于这种操作进行数据保存操作,尤其是在图像数据的批量保存上占据优势。本博文基于selenium 与jupyterla…...
一:Datart的下载、本地运行
前言:本文只是个人在使用datart的一个记录,仅供参考。如果有不一样的地方,欢迎评论或私信进行交流。datart 是新一代数据可视化开放平台,支持各类企业数据可视化场景需求,如创建和使用报表、仪表板和大屏,进…...
Docker-compose
一.Docker-compose概述Docker-Compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。Docker-Compose将所管理的容器分为三层,分别是 工程(project),服务(service)以及容器&a…...
经典文献阅读之--PLC-LiSLAM(面,线圆柱SLAM)
0. 简介 对于激光SLAM来说,现在越来越多的算法不仅仅局限于点线等简答特征的场景了,文章《PLC-LiSLAM: LiDAR SLAM With Planes, Lines,and Cylinders》说到,平面、线段与圆柱体广泛存在于人造环境中。为此作者提出了一个使用这些landmark的…...
计算组合数Cnk即从n个不同数中选出k个不同数共有多少种方法math.comb(n,k)
【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 计算组合数Cnk 即从n个不同数中选出k个不同数共有多少种方法 math.comb(n,k) 以下python代码输出结果是? import math print("【执行】print(math.comb(3,1))") print(math.comb(…...
工厂设计模式
基本概念:为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。分为三类:简单工厂模式Simple Factory:不利于产生系列产品;工厂方法模式Factory Method:又称为…...
IO多路转接 —— poll和epoll
文章目录1. poll1.1 poll的函数接口1.2 poll的简单测试程序1.3 poll的优缺点分析2. epoll2.1 epoll的函数接口2.2 epoll的工作原理2.3 epoll的工作模式(LT,ET)2.4 epoll的简易服务器实现(默认是LT工作模式)前言: 接上文讲述的select,它有缺点,…...
计算机网络整理-问答
1. 程序工作的时候网络各层的状态 如下图所示: 1. TCP 在进行三次握手的时候,IP 层和 MAC 层对应都有什么操作呢? TCP 三次握手是通过在传输层建立连接的一个过程,在这个过程中,TCP 和 IP 层、MAC 层都起到了重要的…...
JS 实现抛物线动画案例
相信大家都有浏览过,很多购物网站购物车的添加商品动画,今天,我们就手写一个简单的抛物线动画,先上案例: 一、绘制页面 我们这里简单实现,一个按钮,一个购物车图标,样式这里直接跳过…...
CSGO搬砖项目,23年最适合小白的项目!
大家好,我是阿阳 不懂的小伙伴,咱继续听我娓娓道来 steam搬砖主要涉及的是csgo游戏平台装备的一个搬运,比较很好理解,主要就是道具的搬运工,简单来讲就是,从国外steam游戏平台购买装备,再挂到…...
谈谈会话管理
客户端和服务器之间进行数据传输遵循的是HTTP协议, 此协议属于无状态协议(一次请求对应一次响应, 响应完之后断开连接), 服务器是无法跟踪客户端的请求, 通过cookie技术可以给客户端添加一个标识, 客户端之后发出的每次请求都会带着这个标识从而让服务器识别此客户端, 但由于co…...
Linux查看JVM FULL GC频率
查看系统的full gc频率,可以使用jstack命令一、采用top命令定位进程登录服务器,执行top命令,查看CPU占用情况,找到进程的pid二、使用jstack命令统计垃圾回收jstat -gc pid 5000即会每5秒一次显示进程号为pid的java进程的GC情况以上…...
java世界String的那些事
String的创建机理: 由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。其运行机制是:创建一个字符串时,首先检查池中是否有值相同的字符串对象,如果…...
【图像配准】多图配准/不同特征提取算法/匹配器比较测试
前言 本文首先完成之前专栏前置博文未完成的多图配准拼接任务,其次对不同特征提取器/匹配器效率进行进一步实验探究。 各类算法原理简述 看到有博文[1]指出,在速度方面SIFT<SURF<BRISK<FREAK<ORB,在对有较大模糊的图像配准时&…...
2023金三银四季跳槽季,啃完这软件测试面试题,跳槽不就稳稳的了
前言 2023年也到来了,接近我们所说的“金三银四”也正在执行了,时间晃眼就过去了,有的人为了2023跳槽早早做足了准备,有的人在临阵磨刀,想必屏幕前的你也想在2023年涨薪吧,那么问题来了,怎么才…...
【C++详解】——vector类
📖 前言:本期介绍vector类。 目录🕒 1. vector的介绍🕒 2. vector的使用🕘 2.1 定义🕘 2.2 iterator🕘 2.3 空间增长🕘 2.4 增删查改🕒 2. vector的模拟实现🕘…...
uniapp 离线本地打包
uniapp打包教程地址 https://nativesupport.dcloud.net.cn/AppDocs/usesdk/android.html点击查看 需要的环境: java (1.8)离线SDK(上面的连接下载即可)Android Studio(同上) 配置环境变量 依次点击“计算机”-“属性”&#…...
初识马尔科夫模型(Markov Model)
初识马尔科夫模型(Markov Model)一、概念二、性质三、学习步骤一、概念 马尔科夫模型(Markov Model)是一种概率模型,用于描述随机系统中随时间变化的概率分布。马尔科夫模型基于马尔科夫假设,即当前状态只…...
CentOS7 ifconfig(或 ip addr)命令不显示IP地址
问题(因为当时没有存图 所以这个图上是网上找的 )解决办法第一:可能是本地服务没有开启,检查本地服务。如图所示,检查这两个服务是否开启。注:如何快速找到服务 可以把光标放在其中一个上面 然后按下VM就可…...
2023/2/10总结
拓扑排序 拓扑排序是在一个有向无环图(DAG)所有顶点的线性排序。 拓扑排序核心思想非常简单,就是先找一个入度为0的顶点输出,再从图中删除该顶点和以它为起点的有向边。继续上面的操作知道所有的顶点访问完为止。 入度…...
2023最新版!宝塔面板Docker自建Bitwarden密码管理
Powered by:NEFU AB-IN 请一定要结合B站视频食用!!!!,下面的博客总体来说只是起到提纲作用 B站视频链接!!! 文章目录2023最新版!宝塔面板Docker自建Bitwarden密码管理前…...
【Hello Linux】 Linux基础命令
作者:小萌新 专栏:Linux 作者简介:大二学生 希望能和大家一起进步! 本篇博客简介:介绍Linux的基础命令 Linux基础命令ls指令lsls -als -dls -ils -sls -lls -nls -Fls -rls -tls -Rls -1总结思维导图pwd指令whoami指令…...
151、【动态规划】leetcode ——2. 01背包问题:二维数组+一维数组(C++版本)
题目描述 原题链接:2. 01背包问题 解题思路 (1)二维dp数组 动态规划五步曲: (1)dp[i][j]的含义: 容量为j时,从物品1-物品i中取物品,可达到的最大价值 (2…...
2023-02-09 - 3 Elasticsearch基础操作
本章主要介绍ES的基础操作,具体包括索引、映射和文档的相关操作。其中,在文档操作中将分别介绍单条操作和批量操作。在生产实践中经常会通过程序对文档进行操作,因此在介绍文档操作时会分别介绍DSL请求形式和Java的高级REST编码形式。 1 索引…...
云原生系列之使用 prometheus监控MySQL实战
文章目录前言一. 实验环境二. 安装MySQL5.72.1 配置yum源2.2 安装MySQL之前的环境检查2.3 开始使用yum安装2.4 启动MySQL并测试三. 安装MySQL_exporter3.1 MySQL_exporter的介绍3.2 mysql_exporter的安装3.3 设置MySQL账户,用于数据收集3.4 启动mysql_exporter3.5 配…...
网站群建设座谈会/黑帽seo培训网
docker-slim 的主要作用是对 docker 镜像进行瘦身的工具。经过瘦身处理的镜像会变小一些甚至变小数倍,且瘦身收仍然可以正常运行。 详情请见官方网站:https://dockersl.im/ 安装 在官网获取最新包的下载地址:https://dockersl.im/install.h…...
最专业的网站建设价格/看广告收益最高的软件
矩阵分析之 实矩阵分解(1)特征分解与奇异值分解前言特征分解(又称谱分解、对角化)奇异值分解SVDSVD的进一步理解前言 本篇开始学习记录矩阵分解内容。需要说明的是,目前我所触及的矩阵基本上没有出现复数域࿰…...
关键词是在网站后台做的吗/深圳市住房和建设局
北京各街头的情愿 今天是2016年10月24日,也是1024程序员节,就在前几天,作为IT人群高密度聚集的北京地区,在北京各个街头出现了大量参与“程序员不加班”的请愿行动的人群,受北京“1024程序员节”请愿活动影响ÿ…...
集团做网站方案制作包含哪些方面/搜索引擎营销优缺点
每次遇见你电影《夜玫瑰》插曲 演唱:郭乔伊作词:郭乔伊 作曲:浮克编曲:浮克 那天遇到你也许就是所说 的天意 你那样呆呆望着我好像过了几个世纪你向我走来带起了满天的花雨我呼吸 再次遇到你 还是那样偶然而惊喜 你带有最…...
婚纱摄影的网站怎么做/seo技术培训机构
1.配置Java环境变量 首先进入到终端 输入 vi ~/.bashrc然后输入 i 插入数据 export JAVA_HOME/usr/lib/jvm/jdk15.0.2 export JRE_HOME${JAVA_HOME}/jre export CLASSPATH.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH${JAVA_HOME}/bin:$PATH输入esc 然后 :wq 退出 最后输…...
寻找南京帮助做网站的单位/南昌百度推广公司
在我们为 IDEA 等编辑器配置 svn 时,经常需要配置 svn.exe 文件的目录 C:\Program Files\TortoiseSVN\bin\svn.exe ; 但打开 svn.exe 文件的安装目录 C:\Program Files\TortoiseSVN\bin,却发现没有 svn.exe 文件,其原因是由于安…...