1.shader 编程基础
1.1 Vertex shader与Fragment shader
Vertex shader即顶点着色器,用来改变顶点的属性。Fragment shader即片元着色器,用来改变片元的颜色,在Direct3D中称为Pixel shader,像素着色器。
1.2 编程语言
面向OpenGL的GLSL语言,Cg语言,以及面向Direct3D的HLSL语言。Cg语言与HLSL语言语法基本上相同。
1.3 Vertex shader 功能
vertex shader 可以控制如下方框中的通道对应的属性信息,包括几何变换、投影变换、光照计算等。
1.4 Fragment shader功能
Fragment shader的功能如下。
主颜色与辅助颜色(Primary Color、Secondary Color):设置辅助颜色的目的是将Specular颜色与Ambient、Diffuse颜色分开;
- 在典型的光照计算中,分别计算ambient、diffuse、specular和自发光的值,让后进行叠加;而在这之后进行纹理映射的话,specular可能会被覆盖(弱化)。
- 为了解决这个问题,OpenGL中提供了glLightModelfv(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR)函数,调用函数可以将每个顶点的光照计算分为主颜色和辅助颜色,主颜色包含所有非镜面反射光照的总贡献,而辅助颜色为镜面反射光照的贡献。
- 纹理映射的时候只将主颜色和辅助颜色混合起来,执行完纹理映射后,再将主颜色和纹理颜色混合的结果与辅助颜色混合起来。
Fragment shader可以做片元测试,但实际中可以交给固定管线来做。在新版本中alpha测试需要在Frgment shader来做,固定管线已经废弃。可以使用discard来做alpha测试。
//alpha 值小于0.1f。该片元将被丢弃
if(vColorValue.a < 0.1f)
{
discard;
}
1.5 vertex shader 与Fragment shader案例
- vertex shader
void main(void)
{
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_vertex;
}
uniform float time;
void main(void)
{
vec4 v = gl_Vertex;
v.z = sin(5.0 * v.x * time ) * 0.25;
gl_Position = gl_ModelViewMatrix * v;
}
- fragment shader
//改变颜色
void main(void)
{
gl_FragColor = vec4(0.0,1.0,0.0,1.0);
}
2.OpenGL中的shader编程
2.1 OpenGL通过引入扩展库 glew来更方便的进行shader编程。
glew下载地址:http://glew.sourceforge.net/,对下载的源码进行编译生成lib和dll即可引用。
2.2 检测显卡是否支持shader编程
if(GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader)
{
printf("Ready for glsl\n");
{
2.3 将shader代码可以在C++源代码文件中存储在字符串变量中,也可将shader代码编写到独立的文件中,然后将文本文件读取到字符串中即可。
2.4 两个概念
- 着色器对象(shader):一个着色器对象对应一个shader程序;
- 着色器程序(program):可以装载多个着色器对象;
shader编程主要过程:
- 创建着色器对象;
- 将着色器源代码装载入着色器对象;
- 将源代码编译为目标代码;
- 创建着色器程序;
- 将着色器对象附加到着色器程序中;
- 链接着色器程序;
http://www.lighthouse3d.com.tutorials/glsl-tutorial/
2.5 shader编程
glUseProgram(GL_shaderProgram);
//释放
glUseProgram(0);
- 检测编译是否正确
//获取编译错误,存放到param中
glGetShaderiv(GLuint objec,GLenum type ,int* param);
type : GL_COMPILE_STATUS
- 检查链接是否正确
glGetProgramiv(GLuint objec,GLenum type ,int* param)
type : GL_LINK_STATUS
- 获取日志Infolog
glGetSHaderInfoLog(GLuint objec,int maxLen ,int* len,char* log);
glGetProgramInfoLog(GLuint objec,int maxLen ,int* len,char* log);
- 清除
glDetachShader(GLuint program,GLuint shader);
glDeleteShader(GLuint id);
glDeleteProgram(GLuint id);
3.VS与FS之间的数据流
vertex shader代码只需要编写对单个顶点的操作即可,GPU会自动将其作用到其他顶点上;fragment shader只需要编写对单个片元的操作即可。
- VS的输入:针对顶点的属性、一致型变量(uniform)(对所有顶点都一样);
- VS的输出:针对顶点的数据;
- FS的输入:针对片元的属性、一致型变量(uniform)(对所有片元都一样);
- FS的输出:gl_FragColor、gl_FragDepth 、…;
编写shader时,内建的变量以gl开头,可以直接使用。而自定义的变量,从程序中传入。
- 代码程序,如下进行time以及vertCol变量的传递,与VS中的同名变量绑定。
glUseProgram(GL_shaderProgram);
//取得shader代码中的time变量的location
time_location = glGetUniformLocation(G_shaderProgram,"time");
//将time_location与程序中定义的变量shaderTime进行关联,则shaderTime的值就可以传递到time,即可对shaderTime进行操作即可;
glUniform1f(time_location,shaderTime);
//取得shader中vertCol变量的localtion
vertCol_location = glGetAttributeLocation(G_shaderProgram,"vertCol");
//通过函数将值进行传递
glVertexAttrib4f(vertCol_location ,1.0f,0.0f,0.0f,0.0f);
- vertex shader程序
uniform float time;
in vec4 vertCol; // 表述输入数据
out vec4 col; //输出数据;
void main(void)
{
vec4 v = gl_Vertex;
v.y += time * 0.1;
col = vertCol;
gl_Position = gl_ModelViewProjectionMatrix * v;
}
- fragment shader程序
in vec4 col; //与vertxe中col对应
void main(void)
{
gl_FragColor = col;
}
4.VS/FS的输入与输出变量
4.1 可以传输给VS的数据
-
内建的uniform变量,如gl_ModelViewMatrix;只读类型;
-
内建的顶点属性变量(in 类型),如gl_Vertex;只读。
-
自己定义的uniform 类型变量,需要人为赋值,如下进行赋值操作。
-
自己定义的in类型变量,并进行赋值操作;也可以利用内建的变量来传递,如rgba中的a分量;
in vec4 vertMass;
- 纹理数据;
shader编程还是内嵌到固定流水线当中,内建变量的值还是来自固定流水线。
4.2 VS的输出
-
特定变量(Special Output Variables);可读可写;
-
内建顶点属性变量(加out限定符);可读可写;
-
自定义的顶点属性变量(加out限定符);
4.3 FS输入
- 内建的uniform变量;
- 内建的in类型变量;
- 其他
4.4 FS的输出
可以将缓冲数据绘制到gl_FragData,后面通过gl_FragData获取绘制到缓冲区的数据;
5.VS的out变量与FS的in变量的对应
6.总结
附录:图形编程技术,北京林业大学,杨刚