为啥我的法线不会在顶点之间插值,从而产生平坦的阴影效果?

Posted

技术标签:

【中文标题】为啥我的法线不会在顶点之间插值,从而产生平坦的阴影效果?【英文标题】:Why won't my normals interpolate between the vertices, thus creating a flat shading effect?为什么我的法线不会在顶点之间插值,从而产生平坦的阴影效果? 【发布时间】:2019-06-01 01:03:00 【问题描述】:

我的 Phong 着色算法遇到了一些问题。似乎法线关闭了,我总是得到一个平坦的阴影表面。

我正在实现延迟渲染,所以在我的几何传递中,我将顶点的位置和法线存储在纹理中。在光照过程中,我将法线作为颜色输出以检查它们的“值”。编辑 1 我的 Gbuffer 使用两个纹理来存储位置和法线。两种纹理都使用 GL_RGB16F 格式。

EDIT 5 正如你在这张图片中看到的 我没有得到优雅的颜色过渡。左边的头像是我的引擎的结果,右边你可以在 Blender 中看到相同的模型。在 Blender 中,法线(蓝色)看起来是正确的(没有重复等),但在我的引擎中,它们的行为就像一个面法线(每 4 个显示在同一方向上)。 我正在使用 Assimp 加载模型,并在导入它们时包含 aiProcess_GenSmoothNormals 标志。

几何通道顶点着色器:

#version 440 core

layout (location = 0) in vec4 positionOS;
layout (location = 1) in vec3 normalOS;
layout (location = 2) in vec2 uv;

out VertexData

    vec3 PositionVS;
    vec3 NormalVS;
    vec2 UV;
 vs_out;

uniform mat3 normalViewMatrix;
uniform mat4 modelViewMatrix;

struct Camera

    mat4 viewMatrix;
    mat4 projMatrix;
;

layout(std140, binding = 0) uniform CameraBlock

    Camera cam;
;

void main()

    vec4 positionVS = modelViewMatrix * positionOS;
    vs_out.PositionVS = positionVS.xyz;
    vs_out.NormalVS = normalViewMatrix * normalOS;
    vs_out.UV = uv;

    gl_Position = cam.projMatrix * positionVS;

几何通道片段着色器:

#version 440 core

layout (location = 0) out vec3 PositionVS;
layout (location = 1) out vec3 NormalVS;

in VertexData

    vec3 PositionVS;
    vec3 NormalVS;
    vec2 UV;
 vs_in;

void main()

    PositionVS = vs_in.PositionVS;
    NormalVS = normalize(vs_in.NormalVS);

EDIT 4 从 Assimp (aiMesh) 读取顶点值

void MeshLoader::prepareMesh(const aiMesh & mesh)

     // vertices

        for (int i = 0; i < mesh.mNumVertices; i++) 
            if (mesh.HasPositions()) 
                glm::vec3 position = glm::vec3(mesh.mVertices[i].x, mesh.mVertices[i].y, mesh.mVertices[i].z);

                positions.emplace_back(position.x, position.y, position.z, 1);
            

            if (mesh.HasNormals()) 
                normals.emplace_back(mesh.mNormals[i].x, mesh.mNormals[i].y, mesh.mNormals[i].z);
            

            if (mesh.HasTangentsAndBitangents()) 
                tangents.emplace_back(mesh.mTangents[i].x, mesh.mTangents[i].y, mesh.mTangents[i].z);
                bitangents.emplace_back(mesh.mBitangents[i].x, mesh.mBitangents[i].y, mesh.mBitangents[i].z);
            

            if (mesh.HasTextureCoords(0)) 
                uvs.emplace_back(mesh.mTextureCoords[0][i].x, mesh.mTextureCoords[0][i].y);
            

        
    

     // polygons
        numFaces = mesh.mNumFaces;

        for (int i = 0; i < numFaces; i++) 
            indices.push_back(mesh.mFaces[i].mIndices[0]);
            indices.push_back(mesh.mFaces[i].mIndices[1]);
            indices.push_back(mesh.mFaces[i].mIndices[2]);
        
    

我想找到解决这个问题的方法。我知道法线是问题,但我不知道为什么。EDIT 2 从球体的外观来看,这似乎是一个插值问题,因此上面的着色器代码。如果着色器是正确的,那么可能是导入器类导致了这个问题?编辑 3 我还实现了前向渲染版本并将法线显示为颜色,但我得到的结果与在延迟版本中。

【问题讨论】:

看起来像导出或加载模型的问题。根据提供的代码,我们都无法提供帮助。 Why does my OpenGL Phong shader behave like a flat shader?的可能重复 @PeterWishart 我已经阅读了该帖子并尝试使用搅拌机解决问题并分析法线,但没有任何帮助:/ @ybungalobill 这是否意味着我的着色器是正确的?如果是,我可以提供模型导入器类的代码。 您如何检查您正在加载的实际模型是否已经具有平滑法线? 【参考方案1】:

我正在使用 Assimp 加载模型,并在导入它们时包含 aiProcess_GenSmoothNormals 标志。

来自documentation:

如果在评估此标志时已经存在法线,则忽略此设置。模型导入器尝试从源文件加载它们,因此它们通常已经存在。

因此,如果您使用平面法线导出模型,此标志将不起作用。所以你有两个选择:

    导出具有正确平滑法线的模型。 (更好的选择,因为这可以让您完全控制法线,保留想要的硬边等等。还可以在需要时减少计算。) 强制 assimp 重新计算法线。看看aiProcess_RemoveComponent 标志:

    如果您想强制 Assimp 重新计算法线或切线,此步骤也很有用。如果它们已经存在(从源资产加载),则相应的步骤不会重新计算它们。通过使用此步骤,您可以确保它们不存在。

【讨论】:

【参考方案2】:

由于您正在处理延迟渲染,我的建议是确保屏幕空间法线纹理实际上正确保存法线值。首先,你为法线纹理分配了多少空间? GL_RGBA16 纹理应该可以完成这项工作。

您必须注意的第二件事是,在 glsl 中,负颜色值在写入纹理缓冲区时会被剪切,因为纹理缓冲区是无符号的。尝试使用某种转换器方法将颜色数据转换为矢量数据,反之亦然。方法

vec4 toCoord(vec4 color) 
    return vec4(2.0 * color.xyz - vec3(1, 1, 1), color.w);


vec4 toColor(vec4 coord) 
    return vec4(0.5 * coord.xyz + vec3(0.5, 0.5, 0.5), coord.w);

应该可以正常工作。 如果您还没有做这两件事中的任何一件,请尝试一下:)

如果它不起作用,那么你在片段着色器中写入和读取纹理缓冲区中的普通数据的方式中的一些内部将有所帮助:)

【讨论】:

感谢您的建议!我会尝试它,但我认为它不会有太大变化,因为我在使用前向渲染方法时遇到了同样的问题(参见 EDITs 1 和 3):(

以上是关于为啥我的法线不会在顶点之间插值,从而产生平坦的阴影效果?的主要内容,如果未能解决你的问题,请参考以下文章

如何在OpenGl中计算三角形网格的顶点法线?

[Unity Shader] 逐顶点光照和逐片元光照

为啥maya绑定的模型导出fbx会丢失顶点法线信息?

为什么基本的镜面遮光液,而不是锯齿状?

Unity Shader 获取深度纹理和法线纹理

Maya2018多变形建模过程中顶点法线方向不一致问题的解决方法