着色器(Shader)
- 在之前的随笔中说过,着色器(Shader)是运行在GPU上的小程序。从基本意义上来说,着色器只是一种把输入转化为输出的程序。
- 着色器之间很独立,唯一的共同方式就是通过输入和输出
GLSL
- 一个典型的着色器有以下的结构
#version version_number
in type in_variable_name;
in type in_variable_name;
out type out_variable_name;
uniform type uniform_name;
int main()
{
// 处理输入并进行一些图形操作
...
// 输出处理过的结果到输出变量
out_variable_name = weird_stuff_we_processed;
}
VertexShader:输入变量也叫顶点属性( Vertex Attribute )。要特别注意以下,我们能声明的顶点属性是有上限的。OpenGL确保至少有16个包含4个分量的顶点属性可用。
( 可以通过GL_MAX_VERTEX_ATTRIBS获取 )。
int nrAttributes;
/* 大部分情况下16个够用了,要啥自行车。 /抠鼻 */
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
cout << "Maximum nr of vertex attributes supported: " << nrAttributes << sendl;
数据类型
- GLSL的数据类型有三大类
- 基本的数据类型:int float double uint bool
- 向量(Vector)
- 矩阵(Maxtrix)
- 向量
- 一般来说vecn就够了( float 足够满足大部分需求 )
- 一个向量的分量可以通过 vec.x 获取( .x .y .z .w获取第1 2 3 4个分量 )
- GLSL允许对颜色使用rbga,对纹理坐标使用stpq访问相同的分量
- 还有一种骚操作叫重组。
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);
/* 语法不难理解,但确实骚 */
vecn | 包含n个float分量的默认向量 |
bvecn | 包含n个bool分量的向量 |
ivecn | 包含n个int分量的向量 |
uvecn | 包含n个unsigne intd分量的向量 |
dvecn | 包含n个double分量的向量 |
输入与输出
- 之前说过各个着色器之间相互独立,所以要通过输入与输出来实现Shader之间的数据交流与传递。
- GLSL定义了in和out这两个关键字来实现输入和输出。只要上一个阶段的输出变量与下一个阶段的输出变量匹配,它就会传递下去。( 但是在顶点着色器和片段着色器之间会有一些不同 )
下面着重说一下顶点着色器和片段着色器哪里不同
- 顶点着色器:
- 他的输入是从顶点数据中直接接收输入。所以使用location来指定输入变量去定义顶点数据如何管理,这样我们才可以在CPU上配置顶点属性。
- 顶点着色器需要为它的输入提供一个额外的
layout
标识,这样我们才能把它链接到顶点数据。
你也可以忽略layout(location = 0)标识符,通过在OpenGL代码中使用glGetAttribLocation查询属性位置值(Location),但是我更喜欢在着色器中设置它们,这样会更容易理解而且节省你(和OpenGL)的工作量。
- 片段着色器:他的输入是一个ve4颜色的输出变量,如果你的片段着色器没有定义输出颜色,默认为黑色或白色。
- 顶点着色器:
all in all: 如果我们打算从一个着色器向另一个着色器发送数据,必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了(这是在链接程序对象时完成的)。
举个栗子 (