- 什么是 shader ?
- 给顶点添加颜色(attribute, varying)
- 添加顶点数据
- 着色器也添加对应的顶点属性(添加 VS:attribute,varying,FS:varying)
- 在应用程序设置顶点颜色的属性(设置:VS:attribute)
- 绘制效果
- 让三角型旋转起来(使用:uniform)
- 在顶点着色器添加 uniform 的矩阵
- 先获取 uniform 变量的 location
- 再根据 location 设置 uniform 变量
- 运行效果
- 换y轴来旋转
- 查看面向剔除
- 设置面向剔除
- 注意
- 完整源码
- References
本人才疏学浅,如有什么错误,望不吝指出。
上一篇:LearnGL - 02 - DrawTriangle,学会了如何画一个最最基本的三角形。
这一篇:我将在上一篇简单的例子的基本上添加一些功能,便于学习 shader。
什么是 shader ?shader 直译是:着色器。
shader 是用 GLSL 编写的(OpenGL Shading Language,的简写, 意思是 OpenGL 着色器语言 )
GLSL 是一种 CLike 语言,所以与 C语言非常类似。
在 OpenGL 中,我们用 GLSL 编写的 shader 编译好后,都会链接到一个 Program(程序)对象中,OpenGL 调用该程序对象中链接到的 shader 来处理对应阶段的逻辑。
Program(程序)对象的 Shader 对应有几个阶段:
- Vertex Shader(顶点着色器)
- Tessellation Control Shader(细分控制着色器)
- Tessellation Evaluation Shader(细分计算着色器)
- Geometry Shader(几何着色器)
- Fragment Shader(片段/片元着色器)
其中顶点、片元着色器是必须的,其他都是可选的。
之前的文章 LearnGL - 01.1 - OpenGL 概述 & 管线概述 中我也有列出对应 OpenGL 的简述渲染管线图解,这里再引用一下: 由上面图解所示,箭头流向也就是阶段执行的顺序。
每个阶段的输入,就是上个阶段的输出。
这也是目前着色器中唯一能在着色器之间单向通讯的规则。
另外,着色器的 编译、链接 在之前的文章也有简述 LearnGL - 02 - DrawTriangle - VBO,这里不在说明。
下面使用 shader 来给之前的三角形的每个顶点添加一些不同的颜色,首先是在应用程序层添加顶点的颜色数据。
给顶点添加颜色(attribute, varying) 添加顶点数据我们原来的顶点数据是:
float vertices[] = {
// x, y, z
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
};
现在需要添加颜色,而在GLSL中颜色值最终的输出都是0.0~1.0范围的,都是float浮点型,所以我们在此基础上有添加了3个颜色分量,RGB
float vertices[] = {
// x, y, z r, g, b
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
};
着色器也添加对应的顶点属性(添加 VS:attribute,varying,FS:varying)
下面是原本的两着色器:顶点、片元着色器
前面有说着色器的输入,就是上一个阶段的输出。反过来讲:着色器的输出,就是下个阶段的输入。
但有一些另外,就是着色器阶段中的 头部 着色器,与 尾部 着色器。
- 头部 着色器就是我们的:顶点着色器 vertex shader,它的上个着色器阶段是不存在的,对它输入数据的就是应用程序层,在这里就是我们在 C++ 写的代码来设置 vertex shader 的 vertex attribute(顶点属性)的部分,就是应用层对顶点着色器的数据输入。
- 尾部 着色器就是我们的:片元/片段着色器 fragment shader,它的下个着色器阶段是不存在的,它的输出数据是可以指向 帧缓存目标写入数据的(输出数据)。
在这里我们使用的是 attribute
、varying
关键字。也可以使用 in
、out
关键字。
- 在 顶点着色器中
attribute
是专门给顶点着色器 定义 输入数据的变量用的。varying
是顶点着色器输出给片元着色器用的(或是存在下一个可选着色器阶段的话,会先传给下个阶段)。而 在顶点着色器的varying
变量有个特定,它在会在光栅化阶段对其他生成的片元中,对该varying
变量进行顶点之间的插值得来的。
- 在 片元着色器中
- 不使用
attribute
关键字定义变量,否则会报错。因为attribute
只能在顶点着色器中使用。 varying
是上个着色器阶段传入的片元。一般都会经过顶点之间的插值得到的。
- 不使用
在下面我们对原来的顶点、片元着色器添加了对应的输入、输出变量。
static const char* vertex_shader_text =
"#version 450 compatibility\n"
"attribute vec3 vPos;\n"
"void main() {\n"
" gl_Position = vec4(vPos, 1.0);\n"
"}\n";
static const char* fragment_shader_text =
"#version 450 compatibility\n"
"void main() {\n"
" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n";
下面因为需要填加颜色的顶点的颜色输出,需要增加3个float分量的attribute、还有varying属性:
static const char* vertex_shader_text =
"#version 450 compatibility\n"
"attribute vec3 vPos;\n"
"attribute vec3 vCol;\n"
"varying vec3 fCol;\n"
"void main() {\n"
" gl_Position = vec4(vPos, 1.0);\n"
" fCol = vCol;\n"
"}\n";
static const char* fragment_shader_text =
"#version 450 compatibility\n"
"varying vec3 fCol;\n"
"void main() {\n"
" gl_FragColor = vec4(fCol, 1.0);\n"
"}\n";
这里再次强调说明一下,顶点着色器添加了:attribute vec3 vPos;
varying vec3 fCol;
两个属性,前者是attribute的,用于顶点着色器输入的,后者varying用于顶点传递到片元着色器用的属性,外部不需要设置,我们只要在顶点的main函数里对varying赋值就好,这样片元着色器中的 varying 变量会得到顶点之间的插值数据,只要顶点着色器的varying的变量名称 与 片元着色器中的varying的变量名称对应上就可以传递了,否则会有着色器程序的链接错误。
如果我给片元着色器中的 varying vec3 fCol;
改为 varying vec3 fColAAA;
,后者多加了 AAA 三个字符的变量名。 运行的话就会得到着色器程序链接的错误提示(还记得我们上一篇有讲得如何输出错误把?)
修改了变量名后的片元着色器
static const char* fragment_shader_text =
"#version 450 compatibility\n"
"varying vec3 fColAAA;\n"
"void main() {\n"
" gl_FragColor = vec4(fColAAA, 1.0);\n"
"}\n";
运行后的报错:
- Program Linking 的错误
- fColAAA 有错误相关的变量名
- previous 前一个shader,即fColAAA的前一个shader,在这里就是vertex shader顶点着色器
- not write 就是说前面的vertex shader没有对fColAAA写入数据
GLint vcol_location;
...
vpos_location = glGetAttribLocation(program, "vPos"); // 获取 顶点着色器中的顶点 attribute 属性的 location
vcol_location = glGetAttribLocation(program, "vCol"); // 获取 顶点着色器中的顶点 attribute 属性的 location
glVertexAttribPointer(vpos_location, 3, GL_FLOAT, GL_FALSE, // 设置 顶点属性 vPos 格式
sizeof(float) * 6, (void*)0);
glEnableVertexAttribArray(vpos_location); // 启用 顶点缓存 location 位置的属性
glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE, // 设置 顶点属性 vCol 格式
sizeof(float) * 6, (void*)(sizeof(float) * 3));
glEnableVertexAttribArray(vcol_location); // 启用 顶点缓存 location 位置的属性
注意 glVertexAttribPointer 函数调用时:
- 第一个参数 设置对应的 location 索引值
vpos_locaiton
的不变,vcol_location
的也是通过glGetAttribLocation
来获取即可。 - 第二个参数 还是填入
3
即可,因为颜色也是RGB三个分量。 - 第三个参数 和 第四个参数 都不变
- 第五个参数 原始是
sizeof(float) * 3
的,现在因为每个顶点属性都添加了一个三个分量的RGB,所以6 个 float 的字节大小。改为:sizeof(float) * 6
就可以了。 - 第六个参数 主要是设置
vcol_location
的需要该为(void*)(sizeof(float)*3)
,因为从偏移3个float的字节数开始,因为前面3个float的字节数据都是 vPos 用的。
这时 顶点属性(Vertex Attribute) 有两个,对应到着色器中就是:一个是:vPos
,另一个是 vCol
。
另外 顶点属性的数量也是有限的,可以通过以下 API 查看你的 OpenGL 环境支持多少个:
int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?