原文
众所周知,OpenGL固定管线只提供了最多8盏灯光。如何使得自己的场景之中拥有更多的灯光效果呢?
这里提供一种使用GLSL shader实现更多数量的局部光照。在GLSL里,首先建立光照参数数据结构: struct myLightParams{ bool enabled; vec4 position; vec4 ambient; vec4 diffuse; vec4 specular; vec3 spotDirection; float spotCutoff; float spotExponent; float constantAttenuation; float linearAttenuation; float quadraticAttenuation;};
然后,需要app传入的参数: const int maxLightCount = 32;uniform myLightParams light[maxLightCount];uniform bool bLocalViewer;uniform bool bSeperateSpecualr;
主函数: void main(){ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; vec4 pos = gl_ModelViewMatrix * gl_Vertex; vec3 epos = vec3(pos)/pos.w; vec3 normal = normalize(gl_NormalMatrix * gl_Normal); vec3 eye; if (bLocalViewer) eye = -normalize(epos); else eye = vec3(0, 0, 1.0); vec4 amb = vec4(0); vec4 diff = vec4(0); vec4 spec = vec4(0); for (int i=0; i<maxLightCount; i++) { if (light[i].enabled == false) continue; if (light[i].position.w == 0) { DirectionalLight(i, eye, epos, normal, amb, diff, spec); } else if (light[i].spotCutoff == 180.0) { PointLight(i, eye, epos, normal, amb, diff, spec); } else { SpotLight(i, eye, epos, normal, amb, diff, spec); } } vec4 color = gl_FrontLightModelProduct.sceneColor + amb * gl_FrontMaterial.ambient + diff * gl_FrontMaterial.diffuse; if (bSeperateSpecualr) { gl_FrontSecondaryColor = spec * gl_FrontMaterial.specular; } else { gl_FrontSecondaryColor = vec4(0, 0, 0, 1.0); color += spec * gl_FrontMaterial.specular; } gl_FrontColor = color;}
对于方向光源的计算: void DirectionalLight(int i, vec3 eye, vec3 epos, vec3 normal, inout vec4 amb, inout vec4 diff, inout vec4 spec){ float dotVP = max(0, dot(normal, normalize(vec3(light[i].position)))); float dotHV = max(0, dot(normal, normalize(eye+normalize(vec3(light[i].position))))); amb += light[i].ambient; diff += light[i].diffuse * dotVP; spec += light[i].specular * pow(dotHV, gl_FrontMaterial.shininess);}
对于点光源: void PointLight(int i, vec3 eye, vec3 epos, vec3 normal, inout vec4 amb, inout vec4 diff, inout vec4 spec){ vec3 VP = vec3(light[i].position) - epos; float d = length(VP); VP = normalize(VP); float att = 1.0/(light[i].constantAttenuation + light[i].linearAttenuation*d + light[i].quadraticAttenuation*d*d); vec3 h = normalize(VP+eye); float dotVP = max(0, dot(normal, VP)); float dotHV = max(0, dot(normal, h)); amb += light[i].ambient * att; diff += light[i].diffuse * dotVP * att; spec += light[i].specular * pow(dotHV, gl_FrontMaterial.shininess) * att;}
对于聚光灯: void SpotLight(int i, vec3 eye, vec3 epos, vec3 normal, inout vec4 amb, inout vec4 diff, inout vec4 spec){ vec3 VP = vec3(light[i].position) - epos; float d = length(VP); VP = normalize(VP); float att = 1.0/(light[i].constantAttenuation + light[i].linearAttenuation*d + light[i].quadraticAttenuation*d*d); float dotSpot = dot(-VP, normalize(light[i].spotDirection)); float cosCutoff = cos(light[i].spotCutoff*3.1415926/180.0); float spotAtt = 0; if (dotSpot < cosCutoff) spotAtt = 0; else spotAtt = pow(dotSpot, light[i].spotExponent); att *= spotAtt; vec3 h = normalize(VP+eye); float dotVP = max(0, dot(normal, VP)); float dotHV = max(0, dot(normal, h)); amb += light[i].ambient * att; diff += light[i].diffuse * dotVP * att; spec += light[i].specular * pow(dotHV, gl_FrontMaterial.shininess) * att;}
这样,对于场景之中的任意对象,它所能够接受计算的光源就可以突破8个的限制了。上述光照计算是遵循OpenGL spec的,因此与固定管线的效果是一致的。