c++ - 在OpenGL中为地形生成平滑法线

标签 c++ opengl glm-math

我正在实现一个使用Perlin噪声生成地形的系统。这就是我生成顶点的方式:

int arrayIdx = 0;

    for(float x = offset.x - CHUNK_WIDTH / 2.0f; x < float(CHUNK_WIDTH) + offset.x - CHUNK_WIDTH / 2.0f; x += TRIANGLE_WIDTH) {
        for(float y = offset.y - CHUNK_WIDTH / 2.0f; y < float(CHUNK_WIDTH) + offset.y - CHUNK_WIDTH / 2.0f; y += TRIANGLE_WIDTH) {
            float height0 = noise->octaveNoise(x + 0.0f * TRIANGLE_WIDTH, y + 0.0f * TRIANGLE_WIDTH),
            height1 = noise->octaveNoise(x + 1.0f * TRIANGLE_WIDTH, y + 0.0f * TRIANGLE_WIDTH),
            height2 = noise->octaveNoise(x + 0.0f * TRIANGLE_WIDTH, y + 1.0f * TRIANGLE_WIDTH),
            height3 = noise->octaveNoise(x + 1.0f * TRIANGLE_WIDTH, y + 1.0f * TRIANGLE_WIDTH);

            mapVertices[arrayIdx + 0] = glm::vec3(x + 0.0f * TRIANGLE_WIDTH, height0, y + 0.0f * TRIANGLE_WIDTH);
            mapVertices[arrayIdx + 1] = glm::vec3(x + 1.0f * TRIANGLE_WIDTH, height1, y + 0.0f * TRIANGLE_WIDTH);
            mapVertices[arrayIdx + 2] = glm::vec3(x + 0.0f * TRIANGLE_WIDTH, height2, y + 1.0f * TRIANGLE_WIDTH);
            mapVertices[arrayIdx + 3] = glm::vec3(x + 1.0f * TRIANGLE_WIDTH, height3, y + 1.0f * TRIANGLE_WIDTH);
            mapVertices[arrayIdx + 4] = glm::vec3(x + 1.0f * TRIANGLE_WIDTH, height1, y + 0.0f * TRIANGLE_WIDTH);
            mapVertices[arrayIdx + 5] = glm::vec3(x + 0.0f * TRIANGLE_WIDTH, height2, y + 1.0f * TRIANGLE_WIDTH);

            mapUVs[arrayIdx + 0] = glm::vec2(x + 0.0f * TRIANGLE_WIDTH, y + 0.0f * TRIANGLE_WIDTH);
            mapUVs[arrayIdx + 1] = glm::vec2(x + 1.0f * TRIANGLE_WIDTH, y + 0.0f * TRIANGLE_WIDTH);
            mapUVs[arrayIdx + 2] = glm::vec2(x + 0.0f * TRIANGLE_WIDTH, y + 1.0f * TRIANGLE_WIDTH);
            mapUVs[arrayIdx + 3] = glm::vec2(x + 1.0f * TRIANGLE_WIDTH, y + 1.0f * TRIANGLE_WIDTH);
            mapUVs[arrayIdx + 4] = glm::vec2(x + 1.0f * TRIANGLE_WIDTH, y + 0.0f * TRIANGLE_WIDTH);
            mapUVs[arrayIdx + 5] = glm::vec2(x + 0.0f * TRIANGLE_WIDTH, y + 1.0f * TRIANGLE_WIDTH);



            glm::vec3 normal0 = -1.0f * glm::triangleNormal(mapVertices[arrayIdx + 0], mapVertices[arrayIdx + 1], mapVertices[arrayIdx + 2]),
            normal1 = +1.0f * glm::triangleNormal(mapVertices[arrayIdx + 3], mapVertices[arrayIdx + 4], mapVertices[arrayIdx + 5]);

            mapNormals[arrayIdx + 0] = normal0;
            mapNormals[arrayIdx + 1] = (normal0 + normal1) / 2.0f;
            mapNormals[arrayIdx + 2] = (normal0 + normal1) / 2.0f;
            mapNormals[arrayIdx + 3] = normal1;
            mapNormals[arrayIdx + 4] = (normal0 + normal1) / 2.0f;
            mapNormals[arrayIdx + 5] = (normal0 + normal1) / 2.0f;


            arrayIdx += 6;
        }
    }

不使用照明会产生这些非常平滑的结果,

enter image description here

剩下要做的唯一事情就是为三角形生成法线,这将使地形看起来更平滑。
仅使用glm::triangleNormal即可产生此结果,

enter image description here

如您所见,照明确实破坏了光滑表面的幻觉。

我尝试在三角形的碰撞顶点上使用法线的平均值,如下所示:
arrayIdx = 0;

    for(float x = offset.x - CHUNK_WIDTH / 2.0f; x < float(CHUNK_WIDTH) + offset.x - CHUNK_WIDTH / 2.0f; x += TRIANGLE_WIDTH) {
        for(float y = offset.y - CHUNK_WIDTH / 2.0f; y < float(CHUNK_WIDTH) + offset.y - CHUNK_WIDTH / 2.0f; y += TRIANGLE_WIDTH) {

            if((x == offset.x - CHUNK_WIDTH / 2.0f && y == offset.y - CHUNK_WIDTH / 2.0f) ||
               (x == float(CHUNK_WIDTH) + offset.x - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH && y == offset.y - CHUNK_WIDTH / 2.0f) ||
               (x == offset.x - CHUNK_WIDTH / 2.0f && y ==  float(CHUNK_WIDTH) + offset.y - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH) ||
               (x == float(CHUNK_WIDTH) + offset.x - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH && y ==  float(CHUNK_WIDTH) + offset.y - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH)) {
                //Special case
            }
            else if(x ==  float(CHUNK_WIDTH) + offset.x - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH ||
                    y ==  float(CHUNK_WIDTH) + offset.y - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH) {
                 //Special case
            }
            else {
                glm::vec3 averageNormals = (mapNormals[arrayIdx + 3 + 0] + //This triangle
                                            mapNormals[arrayIdx + 0 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6 + 6] + //Triangle after and this one
                                            mapNormals[arrayIdx + 2 + 6] + //Triangle in the right
                                            mapNormals[arrayIdx + 5 + 6] + //Triangle in the right
                                            mapNormals[arrayIdx + 1 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6] + //Triangle after this one
                                            mapNormals[arrayIdx + 4 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6])  //Triangle after this one
                                            / 6.0f;

                mapNormals[arrayIdx + 3 + 0] = averageNormals;
                mapNormals[arrayIdx + 2 + 6] = mapNormals[arrayIdx + 3 + 0];
                mapNormals[arrayIdx + 5 + 6] = mapNormals[arrayIdx + 3 + 0];
                mapNormals[arrayIdx + 1 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6] = mapNormals[arrayIdx + 3 + 0];
                mapNormals[arrayIdx + 4 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6] = mapNormals[arrayIdx + 3 + 0];
                mapNormals[arrayIdx + 0 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6 + 6] = mapNormals[arrayIdx + 3 + 0];
            }

            arrayIdx += 6;
        }
    }

产生了这个结果,

enter image description here

但这看起来并不好。

使用法线作为片段颜色可得到以下结果:

enter image description here

将法线渲染为线会产生这种情况,这是在优化之前并使用较大的三角形以减少线数的:

enter image description here

这是我的优化:

enter image description here

不知何故,两个法线没有设置。

这里的蓝线是平均法线,绿线是优化前的单个法线,它们看起来不错:

enter image description here

这是线框:

enter image description here

也许某些法线未设置为平均值?

如何生成平滑的法线?

最佳答案

我可以看到我要求的图像...

现在,如果没有一些法线指向向下的字眼,请查看地形下方。如果是,则表示在地形几何中交叉的乘积顺序错误或绕组不同。

然后检查法线是否已归一化(相同大小),但快速浏览对我来说还可以。

如果渲染平均法线(使用不同的颜色),则该法线应该被其他法线(在它们中间)包围

无论如何,这对我来说并不正确:

看起来法线之一正在朝反射的方向前进。您确定要从正确的顶点计算法线吗?

您的几何图形看起来像是三角QUAD网格,因此您可能使用了错误的对角线...

关于c++ - 在OpenGL中为地形生成平滑法线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60241266/

相关文章:

C++:类成员引用有效性?

c++ - 有没有办法在不破坏窗口/上下文的情况下切换全屏?

c++ - glm::rotate() 改变旋转轴,但为什么呢?

c++ - 在 Opengl 中使用 glm 旋转

c++ - 翻译过程中 MVP 行为异常

c++ - 用未知长度的列填充 vector

c++ - 刷新用作 QTableView 编辑器的 QComboBox 中的选项

c++ - linux 中 poll 系统调用的不正确行为

c++ - OpenGL GL_UNPACK_ALIGNMENT

java - GLSL 结构统一位置返回 -1