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

如何在C++、OpenGL及GLM环境下沿物体边界向内挤出2D顶点/向量以赋予物体厚度?

如何用C++、OpenGL和GLM给顶点沿物体边界向内挤出赋予厚度?

我拥有一组顶点/向量,需要将它们沿物体边界向内挤出,以此为物体赋予厚度。举例来说:我需要将类似这样的图形转换为类似这样的图形,请问该如何实现?(我正在使用C++、OpenGL及GLM)——更新——感谢@Futurologist的解答,我已成功解决该问题,效果非常好!

刚好之前做过类似的需求!用C++、GLM和OpenGL实现顶点向内挤出赋予厚度,核心是计算每个顶点的内法线方向,然后沿这个方向偏移顶点,同时处理相邻顶点的平滑衔接,下面是具体的实现思路和代码示例:

核心思路

对于简单多边形(无自相交)的顶点挤出,我们需要为每个顶点找到指向物体内部的偏移方向,这个方向是相邻两条边的内法线的归一化平均值,这样能保证挤出后的顶点不会出现错位或缺口。

具体步骤

1. 判断多边形顶点顺序(确定内法线方向)

首先计算多边形的有向面积:如果面积为正,说明顶点是逆时针排列;如果为负则是顺时针排列。这个结果会决定我们如何计算每条边的内法线。

2. 计算每条边的内法线

对于每条边的向量(比如从顶点v_prev到v_curr的向量edge),我们需要生成指向多边形内部的法线:

  • 若顶点是逆时针排列,将边向量顺时针旋转90度得到内法线(向量(edge.y, -edge.x))
  • 若顶点是顺时针排列,将边向量逆时针旋转90度得到内法线(向量(-edge.y, edge.x))

3. 计算顶点的挤出方向

每个顶点的挤出方向是相邻两条边的内法线的归一化平均值,这样能让挤出后的顶点平滑衔接,避免尖角处出现断裂。

4. 沿挤出方向偏移顶点

用原始顶点位置加上「挤出方向 × 厚度值」,得到挤出后的新顶点。

C++ + GLM 代码实现

#include <glm/glm.hpp>
#include <vector>
#include <cmath>

// 计算多边形的有向面积,用于判断顶点排列顺序
float calculatePolygonArea(const std::vector<glm::vec2>& vertices) {
    float area = 0.0f;
    const int vertexCount = vertices.size();
    for (int i = 0; i < vertexCount; ++i) {
        const int nextIdx = (i + 1) % vertexCount;
        area += vertices[i].x * vertices[nextIdx].y - vertices[nextIdx].x * vertices[i].y;
    }
    return area * 0.5f;
}

// 将2D多边形顶点向内挤出指定厚度
std::vector<glm::vec2> extrudeVerticesInward(const std::vector<glm::vec2>& originalVertices, float thickness) {
    std::vector<glm::vec2> extrudedVertices;
    const int vertexCount = originalVertices.size();
    if (vertexCount < 3) return extrudedVertices; // 至少需要三角形才能挤出厚度

    const float polygonArea = calculatePolygonArea(originalVertices);
    const bool isCounterClockwise = polygonArea > 0.0f;

    for (int i = 0; i < vertexCount; ++i) {
        const int prevIdx = (i - 1 + vertexCount) % vertexCount;
        const int currIdx = i;
        const int nextIdx = (i + 1) % vertexCount;

        const glm::vec2& prevVertex = originalVertices[prevIdx];
        const glm::vec2& currVertex = originalVertices[currIdx];
        const glm::vec2& nextVertex = originalVertices[nextIdx];

        // 计算前后两条边的向量
        const glm::vec2 edgePrev = currVertex - prevVertex;
        const glm::vec2 edgeNext = nextVertex - currVertex;

        // 根据顶点排列顺序计算内法线
        glm::vec2 normalPrev, normalNext;
        if (isCounterClockwise) {
            // 逆时针排列,内法线为边向量顺时针旋转90度
            normalPrev = glm::vec2(edgePrev.y, -edgePrev.x);
            normalNext = glm::vec2(edgeNext.y, -edgeNext.x);
        } else {
            // 顺时针排列,内法线为边向量逆时针旋转90度
            normalPrev = glm::vec2(-edgePrev.y, edgePrev.x);
            normalNext = glm::vec2(-edgeNext.y, edgeNext.x);
        }

        // 归一化法线,确保方向一致
        normalPrev = glm::normalize(normalPrev);
        normalNext = glm::normalize(normalNext);

        // 计算当前顶点的挤出方向:两个法线的平均后归一化
        const glm::vec2 extrusionDir = glm::normalize(normalPrev + normalNext);

        // 生成挤出后的顶点
        const glm::vec2 extrudedVertex = currVertex + extrusionDir * thickness;
        extrudedVertices.push_back(extrudedVertex);
    }

    return extrudedVertices;
}

额外注意事项

  • 上述代码针对2D多边形,如果是3D物体的表面挤出,需要先确定每个面的法线,然后针对每个顶点计算相邻面法线的平均方向,再沿指向物体内部的方向偏移。
  • 只适用于简单多边形(无自相交),复杂多边形需要额外的拓扑处理。
  • 挤出厚度thickness需要根据你的坐标尺度调整,避免挤出后顶点重叠。
  • 如果要生成带厚度的3D网格,还需要将原始顶点和挤出后的顶点连接成面(比如每个原始边对应一个矩形面),再加上原始面和挤出后的面,形成闭合的3D模型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 19:44:09