使用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;
}