现代OpenGL+Qt学习笔记之十:使用逐片元渲染提升真实感

现代OpenGL+Qt学习笔记之十:使用逐片元渲染提升真实感

主要内容

  到目前为止,关于法向、光照(颜色)的计算都是在顶点上进行的,在片元着色器运行之前,OpenGL会对这些顶点的数据(包括法向、颜色、纹理等)进行线性插值,从而得到每个片元的相关数据,我们称这种方法为逐顶点渲染(per-vertex shading),如果能够在顶点的位置和法向数据插值后,再进行光照(颜色)的计算,就能够提高渲染的真实感,这就是本文要介绍的逐片元渲染(per-fragment shading)技术。

逐片元渲染

  在逐顶点渲染中,光照(颜色)的计算是在顶点着色器中进行的,顶点着色器运行结束后,每一个顶点都有一个颜色值,在片元着色器执行前,OpenGL会对这些顶点的颜色数据进行线性插值,从而得到每个片元处的颜色。这就是现代OpenGL+Qt学习笔记之三:显示一个彩色三角形中,为什么只给了3个顶点的颜色值,就能得到一个彩色的三角形的缘故,即三角形中其他点(片元)的颜色值都是通过这给定的3个顶点的颜色值通过线性插值得到的。

  而在逐片元操作中,我们希望OpenGL先对顶点的位置和法向数据进行插值,再在片元着色器中计算每个片元的颜色。

  下面通过程序介绍逐片元渲染的实现过程。如上所述,和逐顶点渲染相比,我们将一部分计算工作转移到了片元着色器中。如下是顶点着色器的内容,主要实现的功能就是法向和顶点位置的变换。

#version 430

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

out vec3 normal;
out vec4 eyeCoords;


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

void main()
{
    normal = normalize( NormalMatrix * VertexNormal);
    eyeCoords = ModelViewMatrix * vec4(VertexPosition,1.0);
    gl_Position = MVP * vec4(VertexPosition,1.0);
}

  如上,顶点着色器中的输出变量是变换后的位置和法向信息,这些数据经过线性插值后会传递给片元着色器,片元着色器会根据插值后的位置和法向数据,计算所有片元的光照(颜色)数据,对应的代码如下:

#version 430

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;

//in vec3 LightIntensity;


in vec3 normal;
in vec4 eyeCoords;

layout( location = 0 ) out vec4 FragColor;

void main() {

    vec3 s = normalize(vec3(Light.Position - eyeCoords));
    vec3 v = normalize(-eyeCoords.xyz);
    vec3 r = reflect( -s, normal );
    float sDotN = max( dot(s,normal), 0.0 );
    vec3 ambient = Light.La * Material.Ka;
    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 );

    vec3 LightIntensity = ambient + diffuse + spec;

    FragColor = vec4(LightIntensity, 1.0);
}

  和逐顶点渲染不同的是,关于光源和材质的信息是输入到片元着色器中,以进行每个片元的光照计算。其计算过程和在逐顶点渲染中的顶点着色器计算原理和过程完全一样。最后输出FragColor,即为该片元的颜色值。注意到这里的颜色值只计算了一次,并不需要经过其他插值操作就会传递到渲染管线的下一过程中。

  程序中其他代码和参数不变,只是使用了不同的着色方法。如下图所示是使用逐顶点渲染得到的结果。



下图所示是使用逐片元渲染得到的结果:

  仔细观察可以发现,使用逐片元操作绘制的物体表面看起来更加光滑,在很多实际的应用中,也能够呈现更加真实的渲染效果。

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

你可能感兴趣的:(OpenGL)