现代OpenGL+Qt学习笔记之八:GLSL双面渲染

现代OpenGL+Qt学习笔记之八:GLSL双面渲染

主要内容

  本文主要介绍OpenGL中的双面渲染,在一般情况下,如果要绘制的曲面是封闭的,如前面涉及过的圆环体,其内部是隐藏的,但是当一些模型有洞时,其内部可见。但在默认情况下,由于这些面的法向朝向问题,其光照计算通常是不正确的,如何正确计算一个模型的内部面片的光照就是本文的主要内容。如下图是没有使用双面渲染时的模型渲染结果:

现代OpenGL+Qt学习笔记之八:GLSL双面渲染_第1张图片

茶壶内部的光照计算是不正确的。

双面渲染

  要实现正确的双面渲染,就是在计算光照时,将模型的反面光照时,将其法向方向改为相反的方向即可。

#version 430

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;

out vec3 FrontColor;
out vec3 BackColor;

struct LightInfo {
  vec4 Position; // Light position in eye coords.
  vec3 La;       // Ambient light intensity
  vec3 Ld;       // Diffuse light intensity
  vec3 Ls;       // Specular light intensity
};
uniform LightInfo Light;

struct MaterialInfo {
  vec3 Ka;            // Ambient reflectivity
  vec3 Kd;            // Diffuse reflectivity
  vec3 Ks;            // Specular reflectivity
  float Shininess;    // Specular shininess factor
};
uniform MaterialInfo Material;

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;

vec3 phongModel( vec4 position, vec3 normal ) {
    vec3 s = normalize(vec3(Light.Position - position));
    vec3 v = normalize(-position.xyz);
    vec3 r = reflect( -s, normal );
    vec3 ambient = Light.La * Material.Ka;
    float sDotN = max( dot(s,normal), 0.0 );
    vec3 diffuse = Light.Ld * Material.Kd * sDotN;
    vec3 spec = vec3(0.0);
    if( sDotN > 0.0 )
        spec = Light.Ls * Material.Ks *
               pow( max( dot(r,v), 0.0 ), Material.Shininess );

    return ambient + diffuse + spec;
}

void main()
{
    vec3 tnorm = normalize( NormalMatrix * VertexNormal);
    vec4 eyeCoords = ModelViewMatrix * vec4(VertexPosition,1.0);

    FrontColor = phongModel( eyeCoords, tnorm );
    BackColor = phongModel( eyeCoords, -tnorm );

    gl_Position = MVP * vec4(VertexPosition,1.0);
}

  这是一个使用双面渲染技术的顶点着色器的例子,和不考虑反面渲染的着色器的最大不同点是,这里的输出变量有两个,分别是FrontColor和BackColor。因为顶点不包含该顶点是属于正面或者反面的信息,所以将这两种情况下的光照(颜色)都计算出来,并输出到片元着色器中进行处理。代码的第50和51行就是计算FrontColor和BackColor的值,当计算BackColor时,我们要将该点的法向方向倒置。

  在片元着色器中,我们先使用以下代码:

#version 430

in vec3 FrontColor;
in vec3 BackColor;

layout( location = 0 ) out vec4 FragColor;

void main() {

    if( gl_FrontFacing ) {
        FragColor = vec4(FrontColor, 1.0);
    } else {
        FragColor = mix( vec4(BackColor, 1.0), vec4(1.0,0.0,0.0,1.0), 0.7 );
    }
}

  这里用到了一个bool类型的内置变量gl_FrontFacing,当该值是true时,说明该片元是属于模型正面的,当该值为false时就说明该片元属于模型反面的片元。OpenGL中判断一个面片的正面和反面,是其顶点的顺序决定的,默认情况下所在面的顶点为逆时针方向的面为正面,顺时针的即为反面。也可以在OpenGL中调用glFrontFace修改默认设置。

  在上述片元着色器中,当该片元为反面片元时,我们将计算得到的BackColor和纯红色做0.3:0.7的混合,作为最终该片元的颜色。其最终的运行结果如下:

现代OpenGL+Qt学习笔记之八:GLSL双面渲染_第2张图片

  这样做事为了更加清晰地区分模型的正面和反面,该技术还可以用来做模型调试,即观察正反面是否定义出错等。

  将上面片元着色器代码的13行改为:

FragColor = vec4(BackColor, 1.0);

即可得到下面的模型渲染结果:
现代OpenGL+Qt学习笔记之八:GLSL双面渲染_第3张图片

  本文的代码在现代OpenGL+Qt学习笔记之七:Phong光照及在GLSL中使用函数一文的源码基础上,换了新的着色器,且将渲染对象改为了一个茶壶模型,在参数上,初始的观察点,模型的初始旋转量和平移量等都有一定的改动,详情见项目源码。

小结

  双面渲染在某些场合下可能需要用到,但是因为实现起来不是很难,属于GLSL中的基础内容,所以这里做了一个简短的介绍。

源码地址:http://download.csdn.net/download/chaojiwudixiaofeixia/9999542

你可能感兴趣的:(OpenGL)