opengl 教程(20) 点光源
原帖地址: http://ogldev.atspace.co.uk/www/tutorial20/tutorial20.html
前面的教程中,我们在方向光的前提下,研究了基本的光照模型(环境光,漫反射光,高光)。方向光没有起点,所有光线都是沿着一个方向,它的强度不会随着距离的增加有任何变化。本篇教程中,我们开始研究点光源,点光源是起始于一个点,向四面照射,会随着传输距离增加而衰减,所以在点光源属性中,我们会增加一个光源位置。
通常点光源衰减程度离物体距离的平方成反,如下面的公式:
但在距离比较小的时候,上面的公式在3D图形学中,效果并不好,所以我们计算点光源衰减系数时候,常使用下面的公式:它包括3个因子,常量因子,线性因子和二次因子。
下面我们总结一下计算点光源时需要的步骤:
- 环境光的计算和方向光一样
- 在世界坐标系中,计算点(像素)到光源的方向,做为光源的方向向量。
- 计算像素到光源的距离,用来计算衰减因子。
- 把环境光、漫反射光和高光加起来,然后乘以衰减因子。
lighting_technique.h
struct BaseLight
{
Vector3f Color;
float AmbientIntensity;
float DiffuseIntensity;
};
.
.
.
struct PointLight : public BaseLight
{
Vector3f Position;
struct
{
float Constant;
float Linear;
float Exp;
} Attenuation;
}
建立一个BaseLight类,抽象出光源的共有属性,然后派生出点光源和方向光源,它们都有自己特有的属性,比如点光源的位置,衰减系数,方向光的方向等等。
lighting_technique.h
void SetPointLights(unsigned int NumLights, const PointLight* pLights);
除了演示点光源,本教程中还要实施多光源,包括一个方向光和多个电光源。
struct {
GLuint Color;
GLuint AmbientIntensity;
GLuint DiffuseIntensity;
GLuint Position;
struct
{
GLuint Constant;
GLuint Linear;
GLuint Exp;
} Atten;
} m_pointLightsLocation[MAX_POINT_LIGHTS];
我们使用结构数组来实现多个点光源,MAX_POINT_LIGHTS是个常量值,指定光源的数目,缺省值是2。
lighting_technique.cpp
vec4 CalcLightInternal(BaseLight Light, vec3 LightDirection, vec3 Normal)
{
vec4 AmbientColor = vec4(Light.Color, 1.0f) * Light.AmbientIntensity;
float DiffuseFactor = dot(Normal, -LightDirection);
vec4 DiffuseColor = vec4(0, 0, 0, 0);
vec4 SpecularColor = vec4(0, 0, 0, 0);
if (DiffuseFactor > 0) {
DiffuseColor = vec4(Light.Color, 1.0f) * Light.DiffuseIntensity * DiffuseFactor;
vec3 VertexToEye = normalize(gEyeWorldPos - WorldPos0);
vec3 LightReflect = normalize(reflect(LightDirection, Normal));
float SpecularFactor = dot(VertexToEye, LightReflect);
SpecularFactor = pow(SpecularFactor, gSpecularPower);
if (SpecularFactor > 0) {
SpecularColor = vec4(Light.Color, 1.0f) *
gMatSpecularIntensity * SpecularFactor;
}
}
return (AmbientColor + DiffuseColor + SpecularColor);
}
上面是计算点光源的通用函数,计算点光源和方向光时候,我们都可以调用该函数来实现。
vec4 CalcDirectionalLight(vec3 Normal)
{
return CalcLightInternal(gDirectionalLight.Base, gDirectionalLight.Direction, Normal);
}
上面是调用该函数实现方向光计算。
vec4 CalcPointLight(int Index, vec3 Normal)
{
vec3 LightDirection = WorldPos0 - gPointLights[Index].Position;
float Distance = length(LightDirection);
LightDirection = normalize(LightDirection);
vec4 Color = CalcLightInternal(gPointLights[Index].Base, LightDirection, Normal);
float Attenuation = gPointLights[Index].Atten.Constant +
gPointLights[Index].Atten.Linear * Distance +
gPointLights[Index].Atten.Exp * Distance * Distance;
return Color / Attenuation;
}
上面是调用该函数实现点光源计算。
void main()
{
vec3 Normal = normalize(Normal0);
vec4 TotalLight = CalcDirectionalLight(Normal);
for (int i = 0 ; i < gNumPointLights ; i++) {
TotalLight += CalcPointLight(i, Normal);
}
FragColor = texture2D(gSampler, TexCoord0.xy) * TotalLight;
}
最终输出最终像素颜色时候,我们把所有光源的效果加起来,并用纹理颜色进行调制。
void LightingTechnique::SetPointLights(unsigned int NumLights, const PointLight* pLights) {
glUniform1i(m_numPointLightsLocation, NumLights);
for (unsigned int i = 0 ; i < NumLights ; i++) {
glUniform3f(m_pointLightsLocation[i].Color, pLights[i].Color.x, pLights[i].Color.y, pLights[i].Color.z);
glUniform1f(m_pointLightsLocation[i].AmbientIntensity, pLights[i].AmbientIntensity);
glUniform1f(m_pointLightsLocation[i].DiffuseIntensity, pLights[i].DiffuseIntensity);
glUniform3f(m_pointLightsLocation[i].Position, pLights[i].Position.x, pLights[i].Position.y, pLights[i].Position.z);
glUniform1f(m_pointLightsLocation[i].Atten.Constant, pLights[i].Attenuation.Constant);
glUniform1f(m_pointLightsLocation[i].Atten.Linear, pLights[i].Attenuation.Linear);
glUniform1f(m_pointLightsLocation[i].Atten.Exp, pLights[i].Attenuation.Exp);
}
}
上面是给各个uniform变量赋值。
程序执行后界面如下,我们使用了2个点光源: