【OpenGL】GLSL-双面渲染技术(Two-sided rendering)

来源:互联网 发布:淘宝最长发货时间 编辑:程序博客网 时间:2024/04/28 19:46

当我们渲染一个完全封闭的物体的时候,多边形的背面都是隐藏的,我们无法看见。但是如果这个物体有一些空洞,那么一些背面就可以被看见了。然而因为这些多边形的法向量的方向在这个时候不是正确的,所以会渲染出错误的结果。为了合理的渲染这些背面,我们必须反转法向量,然后基于这些反转的法向量来计算光照。

下图显示了一个采用Phong光照模型绘制茶壶使用双面渲染与否的结果对比:


图一 使用双面渲染与不使用的效果对比

一、固定管线OpenGL实现双面渲染

在固定管线中,我们需要用一个函数来启用双面光照:

glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);

这样,OpenGL将会反转表面的法线的方向,表示背面多边形的法线。

实例:

[cpp] view plaincopyprint?
  1. void GLWidget::initLight()  
  2. {  
  3.     GLfloat light_ambient[]={0.7,0.8,0.9,1.0};     
  4.     GLfloat light_diffuse[]={1.0,1.0,1.0,1.0};     
  5.     GLfloat light_specular[]={1.0,1.0,1.0,1.0};     
  6.     GLfloat light_position[]={2.0,2.0,2.0,0.0};  
  7.     GLfloat mat_diffuse[]={0.8,0.8,0.8,1.0};   
  8.   
  9.     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);     
  10.     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);     
  11.     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);     
  12.     glLightfv(GL_LIGHT0,GL_POSITION,light_position);   
  13.   
  14.     glEnable(GL_LIGHTING);  
  15.     glEnable(GL_LIGHT0);  
  16.     glEnable(GL_AUTO_NORMAL);  
  17.     glEnable(GL_NORMALIZE);  
  18.     //glEnable(GL_CULL_FACE);  
  19.     //启用双面光照  
  20.     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);  
  21.     glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,mat_diffuse);    
  22. }  
void GLWidget::initLight(){GLfloat light_ambient[]={0.7,0.8,0.9,1.0};   GLfloat light_diffuse[]={1.0,1.0,1.0,1.0};   GLfloat light_specular[]={1.0,1.0,1.0,1.0};   GLfloat light_position[]={2.0,2.0,2.0,0.0};GLfloat mat_diffuse[]={0.8,0.8,0.8,1.0}; glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);   glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);   glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);   glLightfv(GL_LIGHT0,GL_POSITION,light_position); glEnable(GL_LIGHTING);glEnable(GL_LIGHT0);glEnable(GL_AUTO_NORMAL);glEnable(GL_NORMALIZE);//glEnable(GL_CULL_FACE);//启用双面光照glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,mat_diffuse);  }
效果:



二、可编程管线OpenGL实现双面渲染

假设我们的光照(采用Phong光照模型)计算是基于顶点的。在顶点着色器中,我们需要为每个顶点分别计算两次:front face和back face。两次计算采用的法向量不同。

[cpp] view plaincopyprint?
  1. #version 400  
  2.   
  3. layout (location = 0) in vec3 VertexPosition;  
  4. layout (location = 1) in vec3 VertexNormal;  
  5.   
  6. out vec3 FrontColor;  
  7. out vec3 BackColor;  
  8.   
  9. struct LightInfo{  
  10.     vec4 Position;  
  11.     vec3 La;  
  12.     vec3 Ld;  
  13.     vec3 Ls;  
  14. };  
  15. uniform LightInfo Light;  
  16.   
  17. struct MaterialInfo{  
  18.     vec3 Ka;  
  19.     vec3 Kd;  
  20.     vec3 Ks;  
  21.     float Shininess;  
  22. };  
  23. uniform MaterialInfo Material;  
  24.   
  25. uniform mat4 ModelViewMatrix;  
  26. uniform mat3 NormalMatrix;  
  27. uniform mat4 ProjectionMatrix;  
  28. uniform mat4 MVP;  
  29.   
  30. //转化到视空间   
  31. void getEyeSpace(out vec3 tnorm,out vec4 eyeCoords)  
  32. {  
  33.     //convert normal and position to eye coords  
  34.     tnorm = normalize(NormalMatrix * VertexNormal);  
  35.     eyeCoords = ModelViewMatrix * vec4(VertexPosition,1.0);  
  36. }  
  37.   
  38. //计算Phong光照模型   
  39. vec3 phongModel(vec4 eyeCoords,vec3 tnorm)  
  40. {  
  41.     vec3 s = normalize(vec3(Light.Position - eyeCoords));  
  42.     vec3 v = normalize(-eyeCoords.xyz);  
  43.     vec3 r = reflect(-s,tnorm);  
  44.   
  45.     vec3 ambient = Light.La * Material.Ka;  
  46.   
  47.     float sDotN = max(dot(s,tnorm),0.0);  
  48.     vec3 diffuse = Light.Ld * Material.Kd * sDotN;  
  49.   
  50.     vec3 specular = vec3(0.0);  
  51.     if(sDotN > 0)  
  52.         specular = Light.Ls * Material.Ks *   
  53.                     pow(max(dot(r,v),0.0),Material.Shininess);  
  54.   
  55.     return (ambient + diffuse + specular);  
  56. }  
  57.   
  58. void main()  
  59. {  
  60.     vec3 tnorm;  
  61.     vec4 eyeCoords;  
  62.     getEyeSpace(tnorm,eyeCoords);  
  63.     FrontColor = phongModel(eyeCoords,tnorm);  
  64.     BackColor = phongModel(eyeCoords,-tnorm);  
  65.   
  66.     gl_Position = MVP * vec4(VertexPosition,1.0);  
  67. }  
#version 400layout (location = 0) in vec3 VertexPosition;layout (location = 1) in vec3 VertexNormal;out vec3 FrontColor;out vec3 BackColor;struct LightInfo{vec4 Position;vec3 La;vec3 Ld;vec3 Ls;};uniform LightInfo Light;struct MaterialInfo{vec3 Ka;vec3 Kd;vec3 Ks;float Shininess;};uniform MaterialInfo Material;uniform mat4 ModelViewMatrix;uniform mat3 NormalMatrix;uniform mat4 ProjectionMatrix;uniform mat4 MVP;//转化到视空间void getEyeSpace(out vec3 tnorm,out vec4 eyeCoords){//convert normal and position to eye coordstnorm = normalize(NormalMatrix * VertexNormal);eyeCoords = ModelViewMatrix * vec4(VertexPosition,1.0);}//计算Phong光照模型vec3 phongModel(vec4 eyeCoords,vec3 tnorm){vec3 s = normalize(vec3(Light.Position - eyeCoords));vec3 v = normalize(-eyeCoords.xyz);vec3 r = reflect(-s,tnorm);vec3 ambient = Light.La * Material.Ka;float sDotN = max(dot(s,tnorm),0.0);vec3 diffuse = Light.Ld * Material.Kd * sDotN;vec3 specular = vec3(0.0);if(sDotN > 0)specular = Light.Ls * Material.Ks * pow(max(dot(r,v),0.0),Material.Shininess);return (ambient + diffuse + specular);}void main(){vec3 tnorm;vec4 eyeCoords;getEyeSpace(tnorm,eyeCoords);FrontColor = phongModel(eyeCoords,tnorm);BackColor = phongModel(eyeCoords,-tnorm);gl_Position = MVP * vec4(VertexPosition,1.0);}
在片断着色器中,我们根据关键字gl_FrontFacing的值来为片断赋予颜色值。

gl_FrontFacing是GLSL内置的关键字,它代表图元是面向光源还是背向光源。

片断着色器 twosided.frag:

[cpp] view plaincopyprint?
  1. #version 400  
  2.   
  3. in vec3 FrontColor;  
  4. in vec3 BackColor;  
  5.   
  6. layout (location = 0) out vec4 FragColor;  
  7.   
  8. void main()  
  9. {  
  10.     if(gl_FrontFacing)  
  11.     {  
  12.         FragColor = vec4(FrontColor,1.0);  
  13.     }  
  14.     else  
  15.     {  
  16.         FragColor = vec4(BackColor,1.0);  
  17.     }  
  18. }  
#version 400in vec3 FrontColor;in vec3 BackColor;layout (location = 0) out vec4 FragColor;void main(){if(gl_FrontFacing){FragColor = vec4(FrontColor,1.0);}else{FragColor = vec4(BackColor,1.0);}}


原创粉丝点击