使用GLSL实现对光照的模拟(二)
上一篇文章讲到了我对于光照这一块的实践,这回折腾了一阵子。写了一个小小的应用程序。測试我写的光照是否还有问题。
原创文章,反对未声明的引用。原博客地址:http://blog.csdn.net/gamesdev/article/details/24199913
OpenGL固定渲染管线主要实现的是高洛德着色模型,这是一种简单的光照效果,主要应用在实时渲染领域。
我这次实践主要将全局光照、漫反射效果以及镜面反射效果实现了,漫反射使用了兰伯特(Lambert)公式、镜面反射使用可比林 – 冯(Blinn - Phong)公式。
以下是程序的截图:
这里使用了三盏灯光,分别在角色左上角、右下角和正后方。使用的是同一颜色:白色。
同一时候设置全局的光颜色为黑色。这样能够凸显出每一个光源的着色效果。
为了实现这种效果。我在Host端定义了光源基类,而且有一些派生类来实现。以下是相关类的代码:
class BaseLight { bool m_Enabled; Vector3F m_Ambient; Vector3F m_Diffuse; Vector3F m_Specular; }; /*---------------------------------------------------------------------------*/ class DirectionalLight: public BaseLight { Vector3F m_Direction; Vector3F m_HalfPlane; }; /*---------------------------------------------------------------------------*/ class PointLight: public BaseLight { Vector3F m_Position; Vector3F m_SpotDirection; Vector3F m_Attenuation;// K0, K1 and K2 float m_Exponent; }; /*---------------------------------------------------------------------------*/ class SpotLight: public PointLight { float m_CutOffAngle; };
而在着色器端,我相同定义了类似的数据结构。因为GLSL不支持class以及继承,有些成员仅仅得反复定义了。我将方向光源、点光源以及聚光灯这三类光源分开,存入了vary中,若有须要,可在片断着色器中处理颜色的混合。
以下是GLSL代码:
/*---------------------------------------------------------------------------*/ attribute vec3 position; attribute vec3 normal; attribute vec3 texCoord; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; varying vec2 v_TexCoord; /*---------------------------------------------------------------------------*/ struct DirectionalLight// 方向光源 { int enabled; vec3 ambient; vec3 diffuse; vec3 specular; vec3 direction; vec3 halfPlane; }; /*---------------------------------------------------------------------------*/ struct PointLight// 点光源 { int enabled; vec3 ambient; vec3 diffuse; vec3 specular; vec3 position; vec3 spotDirection; vec3 attenuation; float exponent; }; /*---------------------------------------------------------------------------*/ struct SpotLight// 聚光灯 { int enabled; vec3 ambient; vec3 diffuse; vec3 specular; vec3 position; vec3 spotDirection; vec3 attenuation; float exponent; float cutOffAngle; }; /*---------------------------------------------------------------------------*/ const int MAX_LIGHT = 8;// 通过改动此參数以改动最多多少个光源 uniform int directionalLightCount; uniform int pointLightCount; uniform int spotLightCount; uniform DirectionalLight directionalLight[MAX_LIGHT]; uniform PointLight pointLight[MAX_LIGHT]; uniform SpotLight spotLight[MAX_LIGHT]; uniform int lightingIsEnabled; // 这些是由模型传来的材质參数 uniform float shininess; varying vec3 directionalAmbient; varying vec3 directionalDiffuse; varying vec3 directionalSpecular; varying vec3 pointAmbient; varying vec3 pointDiffuse; varying vec3 pointSpecular; varying vec3 spotAmbient; varying vec3 spotDiffuse; varying vec3 spotSpecular; /*---------------------------------------------------------------------------*/ void InitLightOutput( void ) { directionalAmbient = vec3( 0.0 ); directionalDiffuse = vec3( 0.0 ); directionalSpecular = vec3( 0.0 ); pointAmbient = vec3( 0.0 ); pointDiffuse = vec3( 0.0 ); pointSpecular = vec3( 0.0 ); spotAmbient = vec3( 0.0 ); spotDiffuse = vec3( 0.0 ); spotSpecular = vec3( 0.0 ); } /*---------------------------------------------------------------------------*/ void DirectionalLighting( vec3 position, vec3 normal ) { for ( int i = 0; i < directionalLightCount; ++i ) { if ( directionalLight[i].enabled == 0 ) continue; vec3 N = normalize( normal ); vec3 V = normalize( position ); vec3 L = normalize( directionalLight[i].direction ); vec3 H = normalize( directionalLight[i].halfPlane ); // 计算全局光 directionalAmbient += directionalLight[i].ambient; // 使用兰伯特余弦定律(Lambert' cosine law)计算漫反射 float NdotL = max( 0.0, dot( N, L ) ); directionalDiffuse += directionalLight[i].diffuse * NdotL; // 使用比林 - 冯着色模型(Blinn - Phong shading model)来计算镜面反射 float NdotH = max( 0.0, pow( dot( N, H ), shininess ) ); directionalSpecular += directionalLight[i].specular * NdotH; } } /*---------------------------------------------------------------------------*/ void PointLighting( vec3 position, vec3 normal ) { for ( int i = 0; i < pointLightCount; ++i ) { if ( pointLight[i].enabled == 0 ) continue; vec3 N = normalize( normal ); vec3 V = normalize( position ); vec3 L = normalize( pointLight[i].position - position ); vec3 H = normalize( V + L ); // 计算衰减 float d = length( pointLight[i].position - position ); float attenuation = 1.0 / ( pointLight[i].attenuation[0] + pointLight[i].attenuation[1] * d + pointLight[i].attenuation[2] * d * d ); // 计算全局光 pointAmbient += pointLight[i].ambient * attenuation; // 使用兰伯特余弦定律(Lambert' cosine law)计算漫反射 float NdotL = max( 0.0, dot( N, L ) ); pointDiffuse += pointLight[i].diffuse * NdotL * attenuation; // 使用比林 - 冯着色模型(Blinn - Phong shading model)来计算镜面反射 float NdotH = max( 0.0, pow( dot( N, H ), shininess ) ); pointSpecular += pointLight[i].specular * NdotH * attenuation; } } /*---------------------------------------------------------------------------*/ void SpotLighting( vec3 position, vec3 normal ) { for ( int i = 0; i < spotLightCount; ++i ) { if ( spotLight[i].enabled == 0 ) continue; vec3 N = normalize( normal ); vec3 V = normalize( position ); vec3 L = normalize( spotLight[i].position - position ); vec3 H = normalize( V + L ); // 计算衰减 float d = length( spotLight[i].position - position ); float attenuation = 1.0 / ( spotLight[i].attenuation[0] + spotLight[i].attenuation[1] * d + spotLight[i].attenuation[2] * d * d ); // 计算顶点是否所在半切角内,来决定是否接受聚光灯光照 float dotSpot = dot( -L, normalize( spotLight[i].spotDirection ) ); float cosCutOff = cos( spotLight[i].cutOffAngle ); float spotAttenuation = 0.0; if ( dotSpot > cosCutOff )// 顶点所在聚光灯光照范围内 { spotAttenuation = pow( dotSpot, spotLight[i].exponent ); } attenuation *= spotAttenuation; // 计算全局光 spotAmbient += spotLight[i].ambient * attenuation; // 使用兰伯特余弦定律(Lambert' cosine law)计算漫反射 float NdotL = max( 0.0, dot( N, L ) ); spotDiffuse += spotLight[i].diffuse * NdotL * attenuation; // 使用比林 - 冯着色模型(Blinn - Phong shading model)来计算镜面反射 float NdotH = max( 0.0, pow( dot( N, H ), shininess ) ); spotSpecular += spotLight[i].specular * NdotH * attenuation; } } /*---------------------------------------------------------------------------*/ void main( void ) { if ( lightingIsEnabled == 1 ) { InitLightOutput( ); DirectionalLighting( position, normal ); PointLighting( position, normal ); SpotLighting( position, normal ); } gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ); v_TexCoord = texCoord.xy; }