OpenGL shader to shade each face similar to MeshLab's visualizer
我具有非常基础的OpenGL知识,但是我正在尝试复制MeshLab可视化器具有的阴影效果。
如果在MeshLab中加载网格,您将意识到,如果一个面向相机的面完全发光,并且在旋转模型时,光照会随着面向相机的面的变化而变化。我在MeshLab中加载了一个带有12个面的简单单位立方体,并捕获了以下屏幕截图,以使我的观点清晰明了:
-
加载了模型(注意面部如何完全变灰):
-
略微旋转模型(注意面部变暗):
-
旋转更多(注意现在所有面孔都变黑了):
在我的头上,我认为它的工作方式是通过某种方式在着色器中为每个面分配颜色。如果脸部法线与摄影机之间的角度为零,则脸部将完全照亮(根据脸部的颜色),否则,照亮与法向矢量和照相机矢量之间的点积成比例。
我已经有了使用着色器/ VBO绘制网格的代码。我什至可以分配每个顶点的颜色。但是,我不知道如何实现类似的效果。据我所知,片段着色器可以在顶点上工作。快速搜索发现了这样的问题。但是,当答案涉及重复顶点时,我感到困惑。
如果有什么不同,在我的应用程序中,我加载
@DietrichEpp回答后的结果
我创建了重复的顶点数组,并使用以下着色器实现了所需的照明效果。从发布的屏幕截图中可以看出,相似之处是不可思议的:)
顶点着色器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #version 330 core uniform mat4 projection_matrix; uniform mat4 model_matrix; uniform mat4 view_matrix; in vec3 in_position; // The vertex position in vec3 in_normal; // The computed vertex normal in vec4 in_color; // The vertex color out vec4 color; // The vertex color (pass-through) void main(void) { gl_Position = projection_matrix * view_matrix * model_matrix * vec4(in_position, 1); // Compute the vertex's normal in camera space vec3 normal_cameraspace = normalize(( view_matrix * model_matrix * vec4(in_normal,0)).xyz); // Vector from the vertex (in camera space) to the camera (which is at the origin) vec3 cameraVector = normalize(vec3(0, 0, 0) - (view_matrix * model_matrix * vec4(in_position, 1)).xyz); // Compute the angle between the two vectors float cosTheta = clamp( dot( normal_cameraspace, cameraVector ), 0,1 ); // The coefficient will create a nice looking shining effect. // Also, we shouldn't modify the alpha channel value. color = vec4(0.3 * in_color.rgb + cosTheta * in_color.rgb, in_color.a); } |
片段着色器:
1 2 3 4 5 6 7 8 9 10 | #version 330 core in vec4 color; out vec4 out_frag_color; void main(void) { out_frag_color = color; } |
不可思议的结果与单位立方体:
看起来该效果是具有按面法线的简单照明效果。有几种不同的方法可以实现按面法线:
-
您可以创建一个具有法线属性的VBO,然后为法线不相同的面复制顶点位置数据。例如,一个立方体将具有24个顶点而不是8个顶点,因为"重复项"将具有不同的法线。
-
您可以使用几何着色器来计算每面法线。
-
您可以在片段着色器中使用
dFdx() 和dFdy() 来近似法线。
我推荐第一种方法,因为它很简单。您可以在程序中提前预先计算法线,然后使用它们来计算顶点着色器中的面部颜色。
这是简单的平面着色,您可以使用此GLSL代码段来评估每个面法线,而不必使用每个顶点法线:
1 2 3 4 | vec3 x = dFdx(FragPos); vec3 y = dFdy(FragPos); vec3 normal = cross(x, y); vec3 norm = normalize(normal); |
然后使用
1 2 3 4 | // diffuse light 1 vec3 lightDir1 = normalize(lightPos1 - FragPos); float diff1 = max(dot(norm, lightDir1), 0.0); vec3 diffuse = diff1 * diffColor1; |