如果你还是想使用固定功能管线的glLight*来设置光源的位置的话,需要改一下shader代码。GLSL提供了一个内置的变量gl_LightSource[n].position 其中n为第几个光源。改一下上面的shader.
#define FIX_FUNCTION 1
char vsChar[] = { "#version 120\n"
"uniform vec3 lightPos;\n"
"void main(void)"
"{"
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"
" vec3 N = normalize(gl_NormalMatrix * gl_Normal);"
" vec4 V = gl_ModelViewMatrix * gl_Vertex;"
#if FIX_FUNCTION
" vec3 L = normalize(gl_LightSource[0].position.xyz - V.xyz);"
#else
" vec3 L = normalize(lightPos - V.xyz);"
#endif
" float NdotL = dot(N, L);"
" gl_FrontColor = gl_Color * vec4(max(0.0, NdotL));"
"}"};
void SetupRC()
{
...
#if FIX_FUNCTION
glLightfv(GL_LIGHT0, GL_POSITION, g_lightPos);
#else
lightPosLocation = glGetUniformLocation(program, "lightPos");
if (lightPosLocation != -1)
{
glUniform3fv(lightPosLocation, 1, g_lightPos);
}
#endif
}
这样效果是等价的。你会发现我并没有调用
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
来开启光照。这就是shader的好处,不需要这一堆的开关指令。需要用到的就拿去用吧,没用到就相当于关闭了。shader 可编程管线更加灵活。
镜面光照
镜面光照要考虑光的入射方向,以及眼睛所在的位置。由顶点指向光源的向量,顶点的法线,顶点指向照相机的向量就可以决定镜面光在该顶点的强度。简单起见默认默认照相机在z的正方向上,假设顶点到照相机的单位向量为(0.0, 0.0, 1.0)。根据镜面光的公式:
Cspec = max{N • H, 0}Sexp * Cmat * Cli
H是光线向量与视角向量之间夹角正中方向的单位向量。Sexp代表镜面指数,用于控制镜面光照的紧聚程度。Cmat是材料的颜色,Cli是光的颜色。Cspec 是最终求得的镜面颜色。在下面简单的例子中,假设光是白光(1.0, 1.0, 1.0, 1.0),镜面材料的镜面光属性也为(1.0, 1.0, 1.0, 1.0),所以我们可以忽略掉这一项乘的操作。其中N,L,Cmat 和 Cli 和散射光是一样的。这里镜面指数固定为128.
编写如下的specular.vs:
#version 120
uniform vec3 lightPos;
void main(void)
{
//MVP transform
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
//caculate diffuse
//normal
vec3 N = normalize(gl_NormalMatrix * gl_Normal);
//transform to view coordinate
vec4 V = (gl_ModelViewMatrix * gl_Vertex);
//light vector
vec3 L = normalize(lightPos - V.xyz);
float NdotL = dot(N,L);
vec4 diffuse = vec4(max(NdotL, 0.0)) * gl_Color;
//specular
vec3 H = normalize(vec3(0.0, 0.0, 1.0) + L);
float NdotH = max(0.0, dot(N,H));
const float expose = 128.0;
vec4 specular = vec4(0.0);
if (NdotL > 0.0)
specular = vec4(pow(NdotH, expose));
gl_FrontColor = diffuse + specular;
}
效果图: