openGL学习
0.本文章主要记录计算机图形学课上学习的重要知识点及openGL的使用要点,文中部分图片和文字来自https://learnopengl-cn.github.io/
1.现在使用的opengl都是新版本的opengl,使用可编程管线的方式。主要分两部分,将3D坐标转化成设备的2D坐标,然后将光栅化后的图像添加颜色
2.vertex shader处理顶点,将顶点的3D坐标(一般是物体坐标系),转换成裁剪坐标系的3D坐标(这是其实已经很接近2D坐标了,是标准坐标系下的3D坐标,在正负1之间)。然后opengl进行处理,光栅化等等,形成2D坐标,然后fragment shader进行颜色,纹理等的主要是颜色的转换和处理
蓝色部分是可以注入自定义着色器的部分
3.VAO、VBO、EBO的关系和代码顺序,移步我的另一篇博客https://www.cnblogs.com/guopinghai/p/9813900.html
4.uniform变量可以用来在应用程序和着色器程序之间进行通讯。哪个着色器用到就在相应的程序内声明他即可。然后在应用程序中定位uniform变量,然后更改它的值
注意,查询uniform地址不要求你之前使用过着色器程序,但是更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的。
5.glUseProgram()并不是开始开始渲染,而是启动、初始化程序。glDrawArray()才是开始渲染。一般渲染的大循环中glClear()几次之后就先glUseProgram(),然后在绑定,设置参数什么的,最终渲染函数执行。https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/
6.设置纹理:需要设置的参数有环绕方式(图片重复,还是边缘拉伸还是。。。。)、纹理过滤(将纹理像素map到物体像素时候用的插值方式)、多级渐远纹理的边界过渡方式https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/
7.opengl中经常需要绑定对象,是因为绑定之后的参数设置才算有效的,设置的参数都是对当前绑定在opengl的对象作用的。此外,绑定的顺序取决于有些对象可能会存储其他对象的一些信息
8.vertex shader处理坐标,fragment shader处理颜色,纹理,光照。。。。 fragment shader知道了纹理坐标了,那么怎么知道纹理对象呢? 对啦,就是使用uniform,他就是用来在application和shader之间来通讯的。不过这次在fragment shader中声名完uniform后,在application中在draw之前再绑定一次就好啦(这是第二次绑定纹理了哦,第一次绑图片,第二次绑片元着色器)
9.若果设置了两个纹理怎么办呢?当然要现在fragment shader中声名两个uniform啦,然后就是application端的操作了
首先绑定多个纹理,记得激活哦
然后在渲染前在绑定这两个纹理(一定要之前glUseProgram()一下,不然如果有多个渲染程序,谁知道你想跟哪个程序的fragment shader通信啊)
10.vertex shader跟fragment shader简单写法详见我的另一篇博客:https://www.cnblogs.com/guopinghai/p/9817847.html
11.处理坐标转换:在顶点着色器上处理坐标的转换,那么如何将转换矩阵传到GPU的vertex shader上呢?对啦,跟传纹理一样的,用uniform!!!
只不过这次不能简单地绑定一下就OK,这次要真的在application端设置好矩阵然后用opengl的api把值传过去
1 #version 330 core 2 layout (location = 0) in vec3 aPos; 3 layout (location = 1) in vec2 aTexCoord; 4 5 out vec2 TexCoord; 6 7 uniform mat4 transform; 8 9 void main() 10 { 11 gl_Position = transform * vec4(aPos, 1.0f); 12 TexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y); 13 } 14 /*图片y轴和opengl的y轴方向不同,所以用1-y,也可以用std_image库处理一下*/
1 unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform"); 2 glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans)); 3 /*我们首先查询uniform变量的地址,然后用有Matrix4fv后缀的glUniform函数把矩阵数据发送给着色器。第一个参数你现在应该很熟悉了,它是uniform的位置值。第二个参数告诉OpenGL我们将要发送多少个矩阵,这里是1。第三个参数询问我们我们是否希望对我们的矩阵进行置换(Transpose),也就是说交换我们矩阵的行和列。OpenGL开发者通常使用一种内部矩阵布局,叫做列主序(Column-major Ordering)布局。GLM的默认布局就是列主序,所以并不需要置换矩阵,我们填GL_FALSE。最后一个参数是真正的矩阵数据,但是GLM并不是把它们的矩阵储存为OpenGL所希望接受的那种,因此我们要先用GLM的自带的函数value_ptr来变换这些数据。*/
12.在应用程序端编写的坐标转换矩阵是没循环一次就要新初始化一下,不能初始化一次,循环里面每次迭代。这样会导致第一次平移1,第二次就平移2,然后3,4,5。。。本质上还是因为顶点的数据每次是不变的(很坑)
13.进入3D!!!!!
13.0坐标转换vs坐标系转换。11仅仅是对顶点坐标稍作变换,以实现动画效果。现在,我们终于进入到3D啦,终于开始了五种坐标系的转换,以及整个的opengl中3D效果是如何形成的!!!!!
13.1五种坐标系及其转换
在顶点着色器中,有模型矩阵、观察矩阵、投影矩阵三个矩阵负责坐标系的转换。其将目标坐标系转换成裁剪坐标系(依旧是三维),然后opengl自动进行投影除法,化成标准设备的裁剪坐标系
13.2代码部分
1 glm::mat4 model; 2 model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f)); 3 //模型矩阵 4 5 6 glm::mat4 view; 7 // 注意,我们将矩阵向我们要进行移动场景的反方向移动。 8 view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); 9 //观察矩阵 10 11 12 glm::mat4 projection; 13 projection = glm::perspective(glm::radians(45.0f), screenWidth / screenHeight, 0.1f, 100.0f); 14 //投影矩阵
既然我们已经创建了变换矩阵,我们应该将它们传入着色器。首先,让我们在顶点着色器中声明一个uniform变换矩阵然后将它乘以顶点坐标:
1 #version 330 core 2 layout (location = 0) in vec3 aPos; 3 ... 4 uniform mat4 model; 5 uniform mat4 view; 6 uniform mat4 projection; 7 8 void main() 9 { 10 // 注意乘法要从右向左读 11 gl_Position = projection * view * model * vec4(aPos, 1.0); 12 ... 13 }
我们还应该将矩阵传入着色器(这通常在每次的渲染迭代中进行,因为变换矩阵会经常变动):
1 int modelLoc = glGetUniformLocation(ourShader.ID, "model")); 2 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); 3 ... // 观察矩阵和投影矩阵与之类似
13.3模型和观察矩阵一般由之前的几种坐标变换形成,投影矩阵则是直接生成,前两者(也就是目标坐标系到世界坐标系再到相机坐标系的转换本质上都是前面讲的坐标的转换)
14.注意坐标(系)转换的时候是从右向左读的,因为坐标在右面,矩阵乘法有结合律没有交换律
15.相机相关:
其实相机就是搞清楚cameraPos,cameraFornt,cameraUp的关系和用法,以及跟三个欧拉角的关系,以及如何跟鼠标、键盘的wasd键联系起来实现跟用户的交互,然后生成LookAt矩阵就OK了,重点是理解跟4中的注意点,详见我的另一篇帖子
https://www.cnblogs.com/guopinghai/p/9821327.html
另外,0.1我们介绍的摄像机系统是一个FPS风格的摄像机,它能够满足大多数情况需要,而且与欧拉角兼容,但是在创建不同的摄像机系统,比如飞行模拟摄像机,时就要当心。每个摄像机系统都有自己的优点和不足,所以确保对它们进行了详细研究。比如,这个FPS摄像机不允许俯仰角大于90度,而且我们使用了一个固定的上向量(0, 1, 0),这在需要考虑滚转角的时候就不能用了。
16.引入光照贴图后,每个纹理单元要绑定两次,设置一次。一次将图片绑定到纹理单元,一次将纹理单元绑定到渲染程序,一次将纹理单元设置到fragment shader的结构体material的sample2D变量中去