You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将带参数法线的2D曲线挤出为3D模型时如何获得平滑法线?

问题描述

将正弦波曲线挤出为3D模型时,渲染后发现法线没有平滑效果。
该正弦波是通过参数法线生成的,代码如下:

vector<CurvePoint> sineWave(int n, float x0, float y0, float step, float period)
{
    vector<CurvePoint> curve;

    for (int i = 0; i < n; i++) {
        float a = TWO_PI / period;
        float x = x0 + i * step;
        float y = y0 - sinf(x * a);
        float c = cosf(x * a);
        auto normal = glm::vec2(a * c, 1) / sqrtf(a * a * c * c + 1);

        curve.emplace_back(glm::vec2(x, y), normal);
    }

    return curve;
}

使用的挤出方法代码如下:

void extrude(IndexedVertexBatch<XYZ.N> &batch, const Matrix &matrix, const vector<CurvePoint> &curve, GLenum frontFace, float distance)
{
    auto size = curve.size();
    if (size > 1 && distance != 0) {
        bool cw = ((frontFace == CW) && (distance > 0)) || ((frontFace == CCW) && (distance < 0));

        for (auto i = 0; i < size - 1; i++) {
            auto &p0 = curve[i].position;
            auto &p1 = curve[i + 1].position;
            auto normal = matrix.transformNormal(glm::vec3(curve[i].normal, 0));

            batch
                .addVertex(matrix.transformPoint(p0), normal)
                .addVertex(matrix.transformPoint(p1), normal)
                .addVertex(matrix.transformPoint(glm::vec3(p1, distance)), normal)
                .addVertex(matrix.transformPoint(glm::vec3(p0, distance)), normal);

            if (cw) {
                batch.addIndices(0, 3, 2, 2, 1, 0);
            } else {
                batch.addIndices(0, 1, 2, 2, 3, 0);
            }

            batch.incrementIndices(4);
        }
    }
}

渲染采用类Phong(phong-like)着色,渲染效果如下:
渲染效果图

解决方法

当前问题的核心是挤出逻辑中顶点法线赋值错误,导致相邻面的共享顶点法向量不连续,出现硬边效果,可按以下步骤修改:

  • 修正法线赋值逻辑:你当前给同一段挤出面的4个顶点统一使用了当前段起点curve[i]的法线,导致相邻段的共享顶点(前一段终点和后一段起点)法向量不一致。需要给每个顶点匹配其所属曲线上点的对应法线,p1对应的法线为curve[i+1].normal
  • 调整挤出代码的顶点法线绑定:
auto normal0 = matrix.transformNormal(glm::vec3(curve[i].normal, 0));
auto normal1 = matrix.transformNormal(glm::vec3(curve[i+1].normal, 0));

batch
    .addVertex(matrix.transformPoint(p0), normal0)
    .addVertex(matrix.transformPoint(p1), normal1)
    .addVertex(matrix.transformPoint(glm::vec3(p1, distance)), normal1)
    .addVertex(matrix.transformPoint(glm::vec3(p0, distance)), normal0);
  • 确保顶点复用:如果你的顶点批处理默认不共享顶点,需要调整逻辑让相邻段的共享顶点(比如前一段的p1和后一段的p0)复用同一份顶点数据,避免相同位置的顶点被重复创建、携带不同法线
  • 检查渲染管线配置:确认渲染时没有开启面法线覆盖顶点法线的选项,保证光照计算使用你传入的顶点法线

修改完成后,顶点法向量会沿正弦曲线平滑过渡,Phong着色的插值计算就会生成连续的平滑光照效果。

内容的提问来源于stack exchange,提问作者Ariel Malka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 00:27:04