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

旋转曲面法线计算异常求助:渲染存在无法消除的线条

旋转曲面法线计算异常导致渲染出现线条问题

渲染旋转曲面时始终存在一条无法消除的异常线条,效果如下:
异常渲染效果

坐标先存入结构体传递给OpenGL,再基于曲面数据计算法线,以下是环面生成代码和法线计算代码,请求排查问题。

环面生成代码

for (int x = 0; x < num_heights; ++x)
{
  for (int y = 0; y < num_revs; ++y)
  {

    mesh[(x * num_revs_) + y].pos[0] = (c + (a * cos(omega * x))) * cos(omega * y) * sphere_size;
    mesh[(x * num_revs_) + y].pos[1] =  a * sin(omega * x) * sphere_size;
    mesh[(x * num_revs_) + y].pos[2] = (c + (a * cos(omega * x))) * sin(omega * y) * sphere_size;

  }
}

法线计算代码(相邻顶点叉积求平均后归一化)

for (int x = 0; x < num_heights; ++x)
{

  for (int y = 0; y < num_revs; ++y)
  {

    int index = x * num_revs + y;

    Vec3 cur = Vec3(mesh[(x * (num_revs)) + y].pos[0], mesh[(x * (num_revs)) + y].pos[1], mesh[(x * (num_revs)) + y].pos[2]);

    int left_ind = index - 1;
    if (left_ind < 0)
    {
      left_ind = num_revs - 1;
    }
    Vec3 left = Vec3(mesh[left_ind].pos[0], mesh[left_ind].pos[1], mesh[left_ind].pos[2]);

    int up_ind = ((x + 1) * (num_revs)) + y;
    Vec3 up = Vec3(mesh[up_ind].pos[0], mesh[up_ind].pos[1], mesh[up_ind].pos[2]);

    int right_ind = index + 1;
    if (right_ind > (num_heights * num_revs))
    {
      right_ind = 0;
    }
    Vec3 right = Vec3(mesh[right_ind].pos[0], mesh[right_ind].pos[1], mesh[right_ind].pos[2]);

    int down_ind = ((x - 1) * (num_revs)) + y;
    if (down_ind < 0)
    {
      down_ind = (num_heights - 1) * (num_revs) + y;
    }
    Vec3 down = Vec3(mesh[down_ind].pos[0], mesh[down_ind].pos[1], mesh[down_ind].pos[2]);

    int new_ind = 0;

    if (x == (num_heights ) - 1)
    {
      left = Vec3(1, 0, 0);
      up = Vec3(1, 0, 0);
      right = Vec3(1, 0, 0);
      down = Vec3(1, 0, 0);
    }

    Vec3 dir_left = left - cur;
    Vec3 dir_up = up - cur;
    Vec3 dir_right = right - cur;
    Vec3 dir_down = down - cur;

    Vec3 cross = Vec3::CrossProduct(dir_left, dir_up);
    cross += Vec3::CrossProduct(dir_up, dir_right);
    cross += Vec3::CrossProduct(dir_right, dir_down);
    cross += Vec3::CrossProduct(dir_down, dir_left);

    cross.Normalize();

    mesh[(x * num_revs_) + y].normal[0] = cross.x;
    mesh[(x * num_revs_) + y].normal[1] = cross.y;
    mesh[(x * num_revs_) + y].normal[2] = cross.z;

  }

}

问题排查方向

  • 数组下标不一致:生成代码用num_revs_作为下标乘数,法线计算代码混用num_revs和num_revs_,若两个变量值不同,会直接导致顶点索引错乱,法线计算完全错误。
  • 边界顶点处理逻辑错误:
    1. 当x = num_heights - 1时,硬编码left/up/right/down为Vec3(1,0,0),导致最后一行顶点法线与相邻顶点完全不连续,渲染时出现断层线条。
    2. right_ind的判断条件错误:right_ind > (num_heights * num_revs)应改为right_ind >= (num_heights * num_revs),更准确的是当y == num_revs - 1时,right_ind应指向当前x行的第一个顶点(x * num_revs),而非直接跳转到0(跨x行取顶点会导致方向向量错误)。
  • 越界访问问题:当x = num_heights - 1时,up_ind = num_heights * num_revs + y会超出数组范围,需根据母线是否闭合处理x方向边界(闭合则循环到x=0,非闭合则取当前x行顶点)。
  • 叉积方向不统一:四个叉积的方向可能存在冲突,导致法线被抵消或方向错误,需确保每个叉积都指向曲面外侧后再累加平均。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 02:02:27