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

如何在OpenGL中将顶点法向量替换为邻点法向量均值以实现平滑卡通着色

OpenGL平滑Toon Shading的平均法向量实现方案

核心结论

OpenGL本身没有内置的自动计算顶点平均法向量的API,它仅负责渲染你提交的顶点数据,不感知模型的拓扑相邻关系。你需要在CPU端加载模型的阶段预处理计算平滑法向量,方案非常成熟,不需要依赖额外工具。

具体实现步骤

  • 第一步:顶点去重,建立唯一顶点映射
    从OBJ文件加载的原始顶点数据中,同一个空间位置的顶点可能会因为纹理坐标、面归属不同被重复存储。你首先需要通过坐标比对(可以设置1e-6左右的浮点容差避免精度问题),把所有空间位置一致的顶点归为同一个唯一ID,用哈希表存储原始顶点索引到唯一ID的映射。
  • 第二步:遍历三角面累加面法向量
    对每个三角面,先计算该面的面法向量:取三个顶点的坐标做叉乘cross(v1-v0, v2-v0),叉乘结果的模长等于三角面面积的2倍,天然带面积权重(面积更大的面对法向量贡献更高,边缘过渡会更自然),不需要提前归一化。之后把这个面法向量分别累加到三个顶点对应的累计法向量变量中。
  • 第三步:归一化得到最终平滑法向量
    所有三角面遍历完成后,对每个唯一顶点的累计法向量做归一化处理,得到的就是你需要的相邻顶点平均后的平滑法向量,直接替换从OBJ中加载的原始法向量即可。

Toon Shading专属优化提示

如果需要比默认平滑着色更柔和的卡通边缘效果,可以做两个额外调整:

  • 法向量计算时适当放宽顶点匹配的容差,把位置接近的顶点也纳入平均范围,消除细小硬边的毛刺
  • 片段着色器中做toon阶跃处理前,先对法向量和光照方向的点积做smoothstep平滑处理,避免阶跃边缘出现锯齿

核心逻辑伪代码示例

// 假设已经定义顶点结构:struct Vertex { glm::vec3 pos; glm::vec3 normal; };
std::vector<Vertex> unique_vertices;
std::unordered_map<uint64_t, int> pos_to_idx;
std::vector<glm::ivec3> triangle_indices;

// 步骤1:顶点去重逻辑省略,已填充unique_vertices和triangle_indices

// 步骤2:遍历所有三角面累加面法向量
for (const auto& tri : triangle_indices) {
    glm::vec3 v0 = unique_vertices[tri.x].pos;
    glm::vec3 v1 = unique_vertices[tri.y].pos;
    glm::vec3 v2 = unique_vertices[tri.z].pos;
    glm::vec3 face_normal = glm::cross(v1 - v0, v2 - v0);
    unique_vertices[tri.x].normal += face_normal;
    unique_vertices[tri.y].normal += face_normal;
    unique_vertices[tri.z].normal += face_normal;
}

// 步骤3:归一化所有顶点法向量
for (auto& vert : unique_vertices) {
    vert.normal = glm::normalize(vert.normal);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 22:24:06