GLSL光照运用

1、在GLSL中运用光照,首先需考虑的是光照强度,有了光强,有就明暗和层次感,那么光照强度如何判断?我们可以通过计算顶点的法线向量与光照向量的夹角余弦值(cosine值)来得到。所以,当法线离光照方向很接近的情况下,将会出现亮的色调;当光照方向与法线方向的夹角很大时,渲染的地方就会很暗,换句话而言就是,夹角的cosine值影响色调的强度,Cosine值的计算可以利用下面这个公式,也即是点积公式:

 

     cos(lightDir,normal) = lightDir . normal / ( |lightDir| * |normal| )

     当法线向量和光照都被归一化,这个公式将被简化成:

     cos(lightDir,normal) = lightDir . normal


2、所以在GLSL编程时经常会看到如下代码:

Vec3 normal = gl_NormalMatrix * gl_Normal;

Vec3 tnorm = normalize( normal  );

float intensity = dot( lightdir, n );

这里如果没有进行 normalize( normal  );那显然dot是不成行的,即得不到光的强度。

 

3、光线向量的计算

光线向量即从光源点发出,到顶点结束的向量,所以豪无疑问的是我们首先必须得到光源位置以及顶点位置(以观察坐标为基):

(1)光源位置

我们自己设置一个光源位置,比如:uniform vec3 LightPostion;在应用程序中对其赋值。也可以通过GLSL内置的光照属性结构体来获取,结构命名为gl_LightSourceParameters,大概如下:

struct gl_LightSourceParameters {

        vec4 ambient;

        vec4 diffuse;

        vec4 specular;

        vec4 position;

        ...

};

uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];

这样光源的位置为:gl_LightSource[0].postion

 

(2)顶点位置

经过模型视图变换的顶点位置可以这样计算:

vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Vertex);

注意这里是模型视图变换矩阵,不是模型视图投影变换矩阵。

 

接下来求光线方向就非常容易了:vec3 lightVec   = normalize(LightPosition - ecPosition);

 

4、例子1:

        varying vec3 normal;

              

               void main()

               {

                               float intensity;

                               vec4 color;

                               vec3 n = normalize(normal);

                              

                               intensity = dot(vec3(gl_LightSource[0].position),n);

                              

                               if (intensity > 0.95)

                                              color = vec4(1.0,0.5,0.5,1.0);

                               else if (intensity > 0.5)

                                              color = vec4(0.6,0.3,0.3,1.0);

                               else if (intensity > 0.25)

                                              color = vec4(0.4,0.2,0.2,1.0);

                               else

                                              color = vec4(0.2,0.1,0.1,1.0);

                              

                               gl_FragColor = color;

               }

此处通过光强来设置不同的颜色。


例子2:

/// CH06-brick.vert(取自GLSL橙皮书例子)

uniform vec3 LightPosition;

const float SpecularContribution = 0.3;
const float DiffuseContribution  = 1.0 - SpecularContribution;

varying float LightIntensity;
varying vec2  MCposition;

void main()
{
    vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Vertex);
    vec3 tnorm      = normalize(gl_NormalMatrix * gl_Normal);
    vec3 lightVec   = normalize(LightPosition - ecPosition);
    vec3 reflectVec = reflect(-lightVec, tnorm);
    vec3 viewVec    = normalize(-ecPosition);
    float diffuse   = max(dot(lightVec, tnorm), 0.0);
    float spec      = 0.0;

    if (diffuse > 0.0)
    {
        spec = max(dot(reflectVec, viewVec), 0.0);
        spec = pow(spec, 16.0);
    }

    LightIntensity  = DiffuseContribution * diffuse +
                      SpecularContribution * spec;

    MCposition      = gl_Vertex.xy;
    gl_Position     = ftransform();
}

 在该顶点着色器代码中,需注意几点:

(1)通过顶点的坐标位置(ecPosition ),及顶点的单位法线向量(tnorm),并由此计算出光线在顶点平面上的反射向量(reflectVec )。注意该函数TYPE reflect( TYPE I, TYPE N ) 它根据规范化的表面方向向量N,返回入射向量I的反射方向:result = I - 2*dot(N, I) * N

(2)float diffuse = max(dot(lightVec, tnorm), 0.0);

这里dot(lightVec, tnorm)实质是计算光的强度,由于dot实质是计算两向量的夹角余弦,所以当角度>90度时为负值,当处于0~90度时为正值。

 max(dot(lightVec, tnorm), 0.0);的意思也即当光线方向的逆方向(注意lightVec的定义)与法向量的夹角大于90度时,取0值(即就是说完全没有漫射光了,比如想像一下点法向量朝Z轴正上,而光线方向是(1, 1, 1) )。

(3)if (diffuse > 0.0) 即如果存在漫射光,那必然是有反射光的。

        spec = max(dot(reflectVec, viewVec), 0.0);
        spec = pow(spec, 16.0);
这里dot(reflectVec, viewVec)表示反射光强,前面讲过很多了。

pow也是一个GLSL的内置函数,TYPE pow( TYPE x, TYPE y )

表x的y次方。注意spec是个float型数,所以必须为16.0

此处用16次方是随意的,你可以用任意一个数,它只是为了使反射光更加明显,当光线向量与点法微量非常接近的时候,不至于看不见。

 

/// CH06-brick.frag(取自GLSL橙皮书例子)

uniform vec3  BrickColor, MortarColor;
uniform vec2  BrickSize;
uniform vec2  BrickPct;

varying vec2  MCposition;
varying float LightIntensity;

void main()
{
    vec3  color;
    vec2  position, useBrick;
    
    position = MCposition / BrickSize;

    if (fract(position.y * 0.5) > 0.5)
        position.x += 0.5;

    position = fract(position);

    useBrick = step(position, BrickPct);

    color  = mix(MortarColor, BrickColor, useBrick.x * useBrick.y);
    color *= LightIntensity;
    gl_FragColor = vec4(color, 1.0);
}

你可能感兴趣的:(GLSL光照运用)