在ShaderDesigner下编Shader是最为方便的,但这里先用OpenGL下的编程来举例
转载请注明http://blog.csdn.net/boksic 如有疑问欢迎留言
这几个Shader的实际效果:
1.最简单的固定单色Shader
Vertex Shader
坐标经过投影矩阵变换:vTrans = projection * modelview * incomingVertex
void main() { gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; }或者更简单的方式,使用ftransform函数
void main() { gl_Position = ftransform(); }Fragment Shader
赋予像素一个固定值的颜色
void main() { gl_FragColor = vec4(0.4,0.4,0.8,1.0); }
2.颜色Shader
在OpenGL程序当中使用 glColor函数指定颜色时,接收该颜色值的Shader
例如OpenGL程序当中画一个红色茶壶:glColor3f(1, 0, 0); glutSolidTeapot(1);
glColor在Shader当中总共涉及四个值
attribute vec4 gl_Color; varying vec4 gl_FrontColor; // writable on the vertex shader varying vec4 gl_BackColor; // writable on the vertex shader varying vec4 gl_Color; // readable on the fragment shader
流程如下:
OpenGL程序使用glColor函数后,将颜色值以attribute gl_Color的形式传给了Vertex Shader, Vertext Shader接受到后开始计算gl_FontColor和gl_BackColor,而在Fragment Shader则会接受到一个由FontColor和BackColor插值计算出来的varying gl_Color(注意:该gl_Color与Vertex Shader当中的不同),因而可以基于gl_Color开始计算gl_FragColor
Vertex Shader
void main() { gl_FrontColor = gl_Color; gl_Position = ftransform(); }
void main() { gl_FragColor = gl_Color; }
3.动态变形Shader
随着时间变动,改变渲染坐标。关键在于如何把OpenGl的变量传递给Shader
比如在OpenGL中设定一个时间变量time,初始化为0,每次渲染时增加0.1:
float t = 0; void renderScene(void) { ... t += 0.001; }那么将其传递给Shader需要做的是:
1.在初始化阶段使用glGetUniformLocation获取Shader里变量的存取位置
2.在渲染阶段使用glUniform给该存取位置变量赋值
GLint loc; float t = 0; void renderScene(void) { ... glUniform1f(loc, t); t += 0.001; } void setShaders() { ... glUseProgram(p); loc = glGetUniformLocation(p, "time"); }
Vertex Shader
uniform float time; void main() { gl_FrontColor = gl_Color; vec4 v = vec4(gl_Vertex); v.y=v.y*cos(time)+v.y*sin(time); v.z=-v.y*sin(time)+cos(time)*v.z; gl_Position = gl_ModelViewProjectionMatrix * v; }
Fragment Shader
void main() { gl_FragColor = gl_Color; }
4.Lambert Shader
Lambert模型下的Shader,只考虑漫反射,反射强度正比于入射光与法线方向的夹角余弦值:Io= Ld*Md*cosθ
Ld是散射光颜色(gl_LightSource[0].diffuse),Md是材质散射系数(gl_FrontMaterial.diffuse),夹角余弦cosθ可由正规化的法线向量(normal)和入射光向量(lightDir)点乘得到。
OpenGL当中可以对材质和光照的属性进行设置
float lpos[4] = { 1, 0.5, 1, 0 }; float lAmb[4] = { 0.2, 0.5, 1.0, 1 }; float lDif[4] = { 0.2, 1.0, 1.0, 1 }; float lSpe[4] = { 1.0, 1.0, 1.0, 1 }; glLightfv(GL_LIGHT0, GL_POSITION, lpos); glLightfv(GL_LIGHT0, GL_AMBIENT, lAmb); glLightfv(GL_LIGHT0, GL_DIFFUSE, lDif); glLightfv(GL_LIGHT0, GL_SPECULAR, lSpe);
GLfloat ambient [] = { 0.1f, 0.1f, 0.1f, 1.0f}; GLfloat diffuse [] = { 1.0f, 0.0f, 0.0f, 1.0f}; GLfloat specular [] = { 1.0f, 1.0f, 1.0f, 1.0f}; GLfloat shininess[] = { 0.0f}; glMaterialfv(GL_FRONT, GL_AMBIENT, ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, specular); glMaterialfv(GL_FRONT, GL_SHININESS, shininess);
Vertex Shader
void main() { vec3 normal, lightDir; vec4 diffuse; float NdotL; /* 法线向量 */ normal = normalize(gl_NormalMatrix * gl_Normal); /* 入射光向量*/ lightDir = normalize(vec3(gl_LightSource[0].position)); /* cosθ */ NdotL = max(dot(normal, lightDir), 0.0);/* 散射项 */ diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse; gl_FrontColor = NdotL * diffuse;gl_Position = ftransform(); }
Fragment Shader
void main() { gl_FragColor = gl_Color; }
如果再考虑上环境散射项,那么OpenGL中使用glLightfv来设定环境光
float lpos[4] = { 1, 0.5, 1, 0 }; float lAmb[4] = { 0.2, 0.5, 1, 1 }; void renderScene(void) { ... glLightfv(GL_LIGHT0, GL_POSITION, lpos); glLightfv(GL_LIGHT0, GL_AMBIENT, lAmb); ... }
Vertex Shader
void main() { vec3 normal, lightDir; vec4 diffuse, ambient, globalAmbient; float NdotL; normal = normalize(gl_NormalMatrix * gl_Normal); lightDir = normalize(vec3(gl_LightSource[0].position)); NdotL = max(dot(normal, lightDir), 0.0); diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse; /* Compute the ambient and globalAmbient terms */ ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient; globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient; gl_FrontColor = NdotL * diffuse + globalAmbient + ambient; gl_Position = ftransform(); }
5.Blinn-Phong Shader
void main() { vec3 normal, lightDir; vec4 diffuse, ambient, globalAmbient,specular; float NdotL;float NdotHV; normal = normalize(gl_NormalMatrix * gl_Normal); lightDir = normalize(vec3(gl_LightSource[0].position)); NdotL = max(dot(normal, lightDir), 0.0); diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse; /* Compute the ambient and globalAmbient terms */ ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient; globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient; /* compute the specular term if NdotL is larger than zero */ if (NdotL > 0.0) { // normalize the half-vector, and then compute the // cosine (dot product) with the normal NdotHV = max(dot(normal, gl_LightSource[0].halfVector.xyz),0.0); specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess); } gl_FrontColor = NdotL * diffuse + globalAmbient + ambient +specular; gl_Position = ftransform(); }
将法线方向映射到颜色空间中,可用于生成法线贴图
void main() { vec3 normal; normal = normalize(gl_NormalMatrix * gl_Normal); gl_FrontColor = (vec4(normal.x,normal.y,normal.z,1.0)+1)/2; gl_Position = ftransform(); }