CSharpGL(14)用geometry shader渲染模型的法线(normal)
由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了。CSharpGL源码中包含10多个独立的Demo,更适合入门参考。
为了尽可能提升渲染效率,CSharpGL是面向Shader的,因此稍有难度。
问题
在处理光照效果等问题时,模型的顶点的法线是必不可少的数据。但是法线并不直接显示在模型上,也没有别的办法可以直观地看到。如果法线计算错了,那是非常难以排查的。所以我就想用geometry shader来渲染出模型的法线。如下图的白色针状部分,就是这个teapot的法线。为了便于区分,针尖部分是顶点位置,较粗的针头部分则是法线的方向。从这个图中可以看到,我做的这个teapot的法线是很有问题的。怪不得之前拿它试验光照效果时会有一些诡异的现象。
下载
这个示例是CSharpGL的一部分,CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
原理
Geometry shader的执行,在vertex shader之后,在fragment shader之前。Geometry shader的输入数据是一个primitive(points、lines、triangles等),输出可以是0或多个primitive(points、line_strip或triangle_strip)。Geometry shader的作用,就是能增加新的图元。本篇就利用这个功能,将模型顶点的法线作为新增的图元渲染出来。
Geometry shader
Geometry shader代码如下,含义参考注释即可。如果要用此shader,最好删掉中文注释。因为有的显卡可能不支持中文,会造成无法编译通过的情况。
1 #version 410 core 2 3 //输入类型为三角形 4 layout (triangles) in; 5 //输出的是三角形带 6 layout (triangle_strip, max_vertices = 11) out; 7 8 uniform mat4 modelMatrix; 9 uniform mat4 viewMatrix; 10 uniform mat4 projectionMatrix; 11 12 uniform float normalLength = 0.5f; 13 14 in VS_GS_VERTEX 15 { 16 vec3 normal; 17 } vertex_in[]; 18 19 out GS_FS_VERTEX 20 { 21 vec3 color; 22 } vertex_out; 23 24 void main(void) 25 { 26 int i; 27 //先输出模型本身 28 for (i = 0; i < gl_in.length(); i++) { 29 vertex_out.color = vertex_in[i].normal; 30 vec4 position = gl_in[i].gl_Position; 31 gl_Position = projectionMatrix * viewMatrix * (modelMatrix * position); 32 EmitVertex(); 33 } 34 EndPrimitive(); 35 36 //生成顶点的法线(一个法线用一个三棱柱表示) 37 for (i = 0; i < gl_in.length(); i++) {//我的理解:此处gl_in.length()为3 38 //法线颜色为白色 39 vertex_out.color = vec3(1, 1, 1); 40 41 //获取模型的顶点位置(针尖) 42 vec4 position = gl_in[i].gl_Position; 43 //获取模型的法线(针头)位置 44 vec4 target = position + vertex_in[i].normal * normalLength; 45 { 46 vec4 v0 = position; 47 gl_Position = projectionMatrix * viewMatrix * (modelMatrix * v0); 48 EmitVertex();//生成一个三棱柱顶点 49 50 vec4 v1 = target; 51 if (target.x > position.x) { v1.x += normalLength / 30.0f; } 52 else { v1.x -= normalLength / 10.0f; } 53 gl_Position = projectionMatrix * viewMatrix * (modelMatrix * v1); 54 EmitVertex();//生成一个三棱柱顶点 55 56 vec4 v2 = position; 57 gl_Position = projectionMatrix * viewMatrix * (modelMatrix * v2); 58 EmitVertex();//生成一个三棱柱顶点 59 60 vec4 v3 = target; 61 if (target.y > position.y) { v3.y += normalLength / 30.0f; } 62 else { v3.y -= normalLength / 10.0f; } 63 gl_Position = projectionMatrix * viewMatrix * (modelMatrix * v3); 64 EmitVertex();//生成一个三棱柱顶点 65 66 vec4 v4 = position; 67 gl_Position = projectionMatrix * viewMatrix * (modelMatrix * v4); 68 EmitVertex();//生成一个三棱柱顶点 69 70 vec4 v5 = target; 71 if (target.z > position.z) { v5.z += normalLength / 30.0f; } 72 else { v5.z -= normalLength / 10.0f; } 73 gl_Position = projectionMatrix * viewMatrix * (modelMatrix * v5); 74 EmitVertex();//生成一个三棱柱顶点 75 76 vec4 v6 = position; 77 gl_Position = projectionMatrix * viewMatrix * (modelMatrix * v6); 78 EmitVertex();//生成一个三棱柱顶点 79 80 vec4 v7 = target; 81 if (target.x > position.x) { v7.x += normalLength / 30.0f; } 82 else { v7.x -= normalLength / 10.0f; } 83 gl_Position = projectionMatrix * viewMatrix * (modelMatrix * v7); 84 EmitVertex();//生成一个三棱柱顶点 85 86 } 87 88 EndPrimitive();//依据上面的8个顶点,为此顶点的法线生成一个三棱柱 89 } 90 }