OpenGL 4.0 用GLSL实现双面渲染

来源:互联网 发布:c语言指针3三个数交换 编辑:程序博客网 时间:2024/04/30 23:13

当渲染一个完全封闭的  mesh ,如果里面是隐藏的,则不会被渲染。然而 ,如果这个封闭的mesh有个洞,那么里面就可以有一部分是可见的,则需要被渲染出来。在这中情况下,如果不做处理,可能用shader渲染出的光照效果是不正确的(由于mesh表面法线的方向问题)。为了克服这个问题  需要把mesh的表面法线向量反过来,即-normal,然后计算光照模型。


如图



左边的光照模型采用ADS模式(不正确的光照模型),右边的用ADS光照模型和双边光照模型(Two-sided  rendering  technique)


顶点shader

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #version 430  
  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; // Light position in eye coords.  
  11.   vec3 La;       // Ambient light intensity  
  12.   vec3 Ld;       // Diffuse light intensity  
  13.   vec3 Ls;       // Specular light intensity  
  14. };  
  15. uniform LightInfo Light;  
  16.   
  17. struct MaterialInfo {  
  18.   vec3 Ka;            // Ambient reflectivity  
  19.   vec3 Kd;            // Diffuse reflectivity  
  20.   vec3 Ks;            // Specular reflectivity  
  21.   float Shininess;    // Specular shininess factor  
  22. };  
  23. uniform MaterialInfo Material;  
  24.   
  25. uniform mat4 ModelViewMatrix;  
  26. uniform mat3 NormalMatrix;  
  27. uniform mat4 ProjectionMatrix;  
  28. uniform mat4 MVP;  
  29.   
  30. vec3 phongModel( vec4 position, vec3 normal ) {  
  31.     vec3 s = normalize(vec3(Light.Position - position));  
  32.     vec3 v = normalize(-position.xyz);  
  33.     vec3 r = reflect( -s, normal );  
  34.     vec3 ambient = Light.La * Material.Ka;  
  35.     float sDotN = max( dot(s,normal), 0.0 );  
  36.     vec3 diffuse = Light.Ld * Material.Kd * sDotN;  
  37.     vec3 spec = vec3(0.0);  
  38.     if( sDotN > 0.0 )  
  39.         spec = Light.Ls * Material.Ks *  
  40.                pow( max( dot(r,v), 0.0 ), Material.Shininess );  
  41.   
  42.     return ambient + diffuse + spec;  
  43. }  
  44.   
  45. void main()  
  46. {  
  47.     vec3 tnorm = normalize( NormalMatrix * VertexNormal);  
  48.     vec4 eyeCoords = ModelViewMatrix * vec4(VertexPosition,1.0);  
  49.   
  50.     FrontColor = phongModel( eyeCoords, tnorm );  
  51.     BackColor = phongModel( eyeCoords, -tnorm );  
  52.   
  53.     gl_Position = MVP * vec4(VertexPosition,1.0);  
  54. }  

片元shader
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #version 430  
  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.         FragColor = vec4(FrontColor, 1.0);  
  12.     } else {  
  13.        // FragColor = mix( vec4(BackColor, 1.0), vec4(1.0,0.0,0.0,1.0), 0.7 );  
  14.         FragColor = vec4(BackColor, 1.0);  
  15.     }  
  16. }  

在vertex shader中,ADS光照模型  计算方法放在了phongModel中,这个函数调用了两次,第一次用想模型本身的normal  vertor(transformed  into eye  coordinates ) ,第二次用负normal vector,对应的结果存在Frontcolor 和  Backcolor 中,然后传到片元shader中。


能够优化的地方:

Note that there are a few aspects of the shading model that are independent of the orientation of the normal vector (such as the ambient component). One could optimize this code by rewriting it so that the redundant calculations are only done once. However, in this recipe we compute the entire shading model twice in the interest of making things clear and readable.


注意:在片元shader中,判断是mesh模型的 front 还是back 是通过内置变量gl_FrontFacing判断的,这个判断是基于多边形的环绕方向,而不是法线方向!

A polygon is said to have counter-clockwise winding(逆时针环绕) if the vertices are specified in counter-clockwise order as viewed from the front side of the polygon

默认情况下,如果屏幕上的顶点顺序是逆时针顺序(counter-clockwise order) ,则说明这是一个front  facing  polygon,然而我们可以通过glFrontFace 来改变。



需要注意的地方

In the vertex shader we determine the front side of the polygon by the direction of the normal vector, and in the fragment shader, the determination is based on the polygon's winding. For this to work properly, the normal vector must be defined appropriately for the face determined by the setting of glFrontFace.


用two-sided rendering 来调试信息

有时候我们想知道哪些是一个mesh的front facing ,哪个是back facing。我们可以用上面的方法,通过在片元shader中中把mesh的back faces  或者 front faces 设置成一个单一颜色来辨认,例如,对于上面的Frag  shader ,我们可以在else 中  改为

FragColor = mix( vec4(BackColor,1.0),vec4(1.0,0.0,0.0,1.0), 0.7 );

如下图:

0 0
原创粉丝点击