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

如何在GLSL中不改变多边形有向距离场(SDF)尺寸的前提下实现边角圆滑处理

解决多边形SDF圆角且保持尺寸不变的问题

我明白你的痛点:直接给SDF减圆角半径确实能让边角变圆滑,但整个图形会向外“膨胀”一圈,完全偏离了原尺寸。其实核心问题在于,这种做法相当于把整个轮廓向外偏移了圆角半径的距离,而我们需要的是只把尖锐边角换成圆弧,同时让整体轮廓和原图形完全对齐。

原理拆解

要实现这个效果,思路其实和处理圆角矩形异曲同工:

  1. 先把原多边形的每条边向内收缩一段距离(这个距离和圆角半径、多边形边数相关),得到一个缩小版的多边形;
  2. 给这个缩小的多边形加上圆角,此时圆角会刚好延伸回原多边形的轮廓位置,最终整体尺寸和原图形完全一致。

具体实现代码(GLSL)

假设你原来的尖锐多边形SDF是这样的:

#define PI 3.14159265359

// 原尖锐多边形SDF:radius是顶点到中心的距离(外接圆半径)
float sdPolygon(vec2 p, int sides, float radius) {
    float a = atan(p.x, p.y) + PI / float(sides);
    float b = 2.0 * PI / float(sides);
    a = mod(a, b) - b * 0.5;
    return cos(a) * length(p) - radius * cos(b * 0.5);
}

修改后的圆角且尺寸不变的SDF如下:

// 保持尺寸不变的圆角多边形SDF
// round:圆角半径
float sdRoundedPolygon(vec2 p, int sides, float radius, float round) {
    float halfAngle = PI / float(sides);
    
    // 计算向内收缩后的边到中心的距离
    // 这个值保证圆角圆弧刚好和原多边形的边相切
    float insetEdgeDist = radius * cos(halfAngle) - round / tan(halfAngle);
    
    // 计算收缩后多边形的SDF
    float a = atan(p.x, p.y) + halfAngle;
    float b = 2.0 * halfAngle;
    a = mod(a, b) - halfAngle;
    float dInset = cos(a) * length(p) - insetEdgeDist;
    
    // 计算到原多边形顶点的距离,用于实现圆角
    vec2 vertexDir = vec2(cos(a + halfAngle), sin(a + halfAngle));
    float dVertex = length(p - radius * vertexDir) - round;
    
    // 组合两个SDF:远离顶点时用收缩后的多边形(保证边的位置不变),顶点附近用圆角
    return max(dInset, dVertex);
}

代码解释

  • insetEdgeDist:原多边形的边到中心的距离是radius * cos(halfAngle),我们需要把边向内收缩round / tan(halfAngle),这样后续添加的圆角会刚好填补收缩的空隙,让边的位置回到原多边形的位置。
  • dInset:收缩后多边形的SDF,确保远离边角的区域和原多边形的边完全对齐。
  • dVertex:以原多边形顶点为圆心的圆形SDF,用来替换尖锐的边角。
  • max(dInset, dVertex):通过取最大值,让两个SDF共同作用——在远离顶点的区域,收缩后的多边形控制轮廓;在顶点附近,圆形SDF控制轮廓,形成完美的圆角,同时整体尺寸和原图形一致。

这个方法适用于所有凸多边形,你可以直接替换原来的SDF函数,调整round参数就能得到不同圆滑程度的边角,而且完全不会改变原图形的尺寸。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 18:42:47