本文根据教程:ogldev进行扩充学习,一步步从零开始,记录学习历程
一、OpenGL 渲染管线
这节相比上一节有了本质上的区别,OpenGL实际上是通过渲染管线(rendering pipeline),经过一系列的数据处理,将应用程序的数据转换到最终渲染的图像。
在《OpenGL Programming Guide 9th》讲解了渲染管线,下图即是OpenGL 4.5版本的管线:
- Vertex Data(顶点数据):OpenGL将所有数据保存到缓存对象当中,正如上节当中的glVertexAttribPointer()函数所做的工作,并调用glDrawArrays()函数请求渲染几何图元
- Vertex Shader(顶点着色器):接受在顶点缓存对象中给出的顶点数据,独立处理每个顶点(对于绘制命令传输的每个顶点,OpenGL都会调用一个顶点着色器来处理顶点的相关数据)。这个阶段是必须的
- Tessellationj shading stage(细分着色阶段):这个阶段是由Tessellation Control Shader(细分控制着色器)和Tessellation Evaluation Shader(细分赋值着色器)完成的。 这个阶段启用之后,会收到来自顶点着色阶段的输出数据,并对收到的顶点进行进一步的处理,它会在OpenGL管线内部生成新的几何体。这是一个可选阶段
- Geometry Shader(几何着色器):它会在OpenGL管线内部对所有几何图元进行修改,可以选择输入图元生成更多的几何体,改变几何图元的类型(将三角形转化乘线段之类),或者放弃所有的几何体。这是一个可选阶段
- Primitive Setup(图元装配):之前着色阶段处理的都是顶点数据,此外,这些顶点构成几何图元的所有信息也会被传递到OpenGL当中。图元装配阶段将这些顶点与相关的几何图元之间组织起来,准备下一步的剪切和光栅化工作
- Culling and Clipping(裁剪和剪切):顶点可能落在视口之外(即我们能够绘制的窗口区域),此时顶点相关的图元会做出改动,保证相关像素不会绘制在视口以外。由OpenGL自动完成
- Rasterization(光栅化):光栅化是判断某一部分几何体(点、线或者三角形)所覆盖的屏幕空间。因为屏幕是由一个个的像素点构成的,如果要画一条线,就要判断这条线在哪几个像素点表示,配合下图理解:
- Fragment Shader(片元着色器):最后一个可以通过编程控制编程控制屏幕上显示颜色的阶段叫做片元着色阶段。这个阶段处理OpenGL光栅化之后生成的独立片元,使用着色器计算片元的最终颜色和它的的深度值。这个阶段是必须的
二、GLSL构建顶点着色器和片元着色器
GLSL是OpenGL的着色语言,跟使用C语言作为基础高阶着色语言,通过了解渲染管线我们知道了顶点着色器和片元着色器必不可少的,所以我们用GLSL语言构建顶点着色器和片元着色器。
shader.vs(顶点着色器):
#version 330
layout (location = 0) in vec3 Position;
void main()
{
gl_Position = vec4(0.5 * Position.x, 0.5 * Position.y, Position.z, 1.0);
}
shader.fs(片元着色器):
#version 330
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
#version 330
void main()
{
// code
}
- 每一个着色器程序与C程序类似,都是从main()函数开始
- #version 这个预处理命令设置当前使用GLSL版本名称,这里#version 330 代表使用3.3版本
layout (location = 0) in vec3 Position;
- layout(location=0) 是一个布局限定符,作用是把缓冲区里索引的数据绑定到我们输入和输出变量上,即glVertexAttributePointer()函数第一个参数所代表的索引
- in vec3 Position:意思是声明一个三个浮点数的输入变量为Position
gl_Position = vec4(0.5 * Position.x, 0.5 * Position.y, Position.z, 1.0);
- gl_Position是一个内置变量,作为输出变量,用来保存顶点位置的齐次坐标,即就是顶点着色器输出的顶点位置信息存储的地方
- gl_Position是一个四维向量,需要4个分量,所以用vec4()构造,而参数是刚才从顶点缓冲区传来的Position的xyz三个分量的一半,和1.0一起构成,第四个分量W其实代表透明度,设为1.0即不透明。
out vec4 FragColor;
FragColor = vec4(1.0, 0.0,