如何在GLSL中不改变多边形有向距离场(SDF)尺寸的前提下实现边角圆滑处理
解决多边形SDF圆角且保持尺寸不变的问题
我明白你的痛点:直接给SDF减圆角半径确实能让边角变圆滑,但整个图形会向外“膨胀”一圈,完全偏离了原尺寸。其实核心问题在于,这种做法相当于把整个轮廓向外偏移了圆角半径的距离,而我们需要的是只把尖锐边角换成圆弧,同时让整体轮廓和原图形完全对齐。
原理拆解
要实现这个效果,思路其实和处理圆角矩形异曲同工:
- 先把原多边形的每条边向内收缩一段距离(这个距离和圆角半径、多边形边数相关),得到一个缩小版的多边形;
- 给这个缩小的多边形加上圆角,此时圆角会刚好延伸回原多边形的轮廓位置,最终整体尺寸和原图形完全一致。
具体实现代码(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
相关产品推荐
相关产品推荐

