0

我的 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我还实现了前向渲染版本并将法线显示为颜色,但我得到与延迟版本相同的结果。

4

2 回答 2

1

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

文档中:

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

因此,如果您使用平面法线导出模型,则此标志将无济于事。所以你有两个选择:

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

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

于 2019-06-03T18:03:14.020 回答
0

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

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

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

于 2019-06-01T01:42:13.773 回答