聚光灯

今天根据第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);

}