今天根据第6章的介绍,实现了聚光灯的着色器。教材上有很多写错的地方,我在实现这个着色器时遇到了不少坑,好在是搞定了。
上图中,变量 uAngle
表示聚光灯的角度;uLeftRight
变量用于调整聚光灯的投射焦点,进一步改变光照的方向;uWidth
用于调整聚光灯边缘的模糊程度。当 uWidth
= 0.0 时,聚光灯的边缘会显得非常锐利。
具体实现时,主要遇到的坑是光源的位置。教材上使用的坐标为 vec4(0., 0., 40., 1.)
,不得不说这个光源离茶壶模型实在是太远了,以至于uAngle
的值稍微大一点,就完全看不到聚光灯的边缘了。我把这个值改成了 vec3(0.0, 0.0, 4.0)
,终于才好受一点。
另外,之前我写代码的机器的GLSL版本是 #version 140
,现在是 #version 430
,为了兼容,我在glib文件中加入了 Gstap
指令,用于导入教材作者提供的 Gstap.h
头文件。
下面是源代码:
spotlight.glib
# 聚光灯
LookAt 0 0 4 0 0 0 0 1 0
Gstap
Vertex spot.vert
Fragment spot.frag
Program SpotLights \
uAngle <0, 2, 15> \
uLeftRight <-1, 0, 1> \
uWidth <0.0, 0.0005, 0.001>
Color 1 0 0
Teapot
spot.vert
#version 430 compatibility
uniform mat4 uModelViewMatrix;
uniform mat4 uModelViewProjectionMatrix;
uniform mat3 uNormalMatrix;
in vec4 aVertex;
in vec3 aNormal;
in vec4 aColor;
out vec4 vColor;
out float vLightIntensity;
out vec3 vECposition;
out vec3 vNormal;
const vec3 LIGHTPOS = vec3(0.0, 0.0, 4.0);
void main()
{
vColor = aColor;
vECposition = vec3(uModelViewMatrix * aVertex);
vNormal = normalize( uNormalMatrix * aNormal );
vLightIntensity = dot(vNormal, normalize(LIGHTPOS - vECposition));
vLightIntensity = max(0.0, vLightIntensity);
gl_Position = uModelViewProjectionMatrix * aVertex;
}
spot.frag
#version 430 compatibility
uniform float uAngle;
uniform float uLeftRight;
uniform float uWidth;
in vec4 vColor;
in float vLightIntensity;
in vec3 vECposition;
const vec3 LIGHTPOS = vec3(0.0, 0.0, 4.0);
const float AMBCOEFF = 0.3;
const float DIFFCOEFF = 0.7;
void main()
{
vec3 ECLightTarget = vec3( gl_ModelViewMatrix * vec4(uLeftRight, 0.0, 1.5, 1.0) );
vec3 LightDirection = normalize( ECLightTarget - LIGHTPOS );
vec3 EyeDirection = normalize( vECposition - LIGHTPOS);
// ambient only
gl_FragColor = vColor * AMBCOEFF * vLightIntensity;
// add diffuse light based on spotlight
float myAngleCosine = dot( LightDirection, EyeDirection );
float CutoffCosine = cos( radians(uAngle) );
float BlendFactor = smoothstep( CutoffCosine - uWidth, CutoffCosine + uWidth, myAngleCosine );
gl_FragColor += DIFFCOEFF * BlendFactor * vColor * vLightIntensity;
gl_FragColor = vec4(gl_FragColor.rgb, 1.0);
}