繁体   English   中英

为什么我的法线不插入顶点之间,从而产生平坦的阴影效果?

[英]Why won't my normals interpolate between the vertices, thus creating a flat shading effect?

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

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

编辑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);
}

编辑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]);
        }
    }
}

我想找到解决这个问题的方法。 我知道常态是问题,但我不知道为什么。
编辑2通过球体的外观,似乎是一个插值问题,因此上面的着色器代码。 如果着色器正确,那么可能是进口商类引起了此问题?
编辑3我还实现了正向渲染版本并将法线显示为颜色,但是得到的结果与延迟版本相同。

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

文档中

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

因此,如果使用平坦的法线导出模型,则此标志将不起作用。 因此,您有两种选择:

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

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

由于您正在处理延迟渲染,因此我的建议是确保屏幕空间法线纹理实际上正确地保存了法线值。 首先,您为正常纹理分配了多少空间? 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);
}

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

如果它不起作用,那么在片段着色器中与纹理缓冲区之间来回读写普通数据的方式中的一些内容将很有帮助:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM