如何渲染可调节边缘硬度且支持平铺的平滑椭圆?
自定义边缘硬度的平铺椭圆渲染问题
我需要渲染一个支持自定义边缘硬度的椭圆,同时要实现平铺功能(能按指定偏移把渲染结果拆分成多张纹理)。
我编写了如下函数:
float inEllipseSmooth(vec2 pos, float width, float height, float smoothness, float tiling, vec2 offset) { pos = pos / iResolution.xy; float smoothnessSqr = smoothness * tiling * smoothness * tiling; vec2 center = -offset + tiling / 2.0; pos -= center; float x = (pos.x * pos.x + smoothnessSqr) / (width * width); float y = (pos.y * pos.y + smoothnessSqr) / (height * height); float result = (x + y); return (tiling * tiling) - result; }
目前遇到的问题:即使将smoothness设为0,椭圆边缘仍会有平滑效果,无法实现完全锐利的边缘。我考虑过通过计算位置到中心的距离与对应半径比较的方式,但认为可能存在更优解决方案,恳请指点。
问题分析
当前实现的核心问题在于椭圆方程的构造:给x、y分量的平方都加了smoothnessSqr,即使smoothness=0时这个值为0,整个方程的逻辑也不是标准的距离场计算,返回值的方式无法产生阶跃式的锐利边缘。
优化方案:基于距离场的平滑边缘椭圆
采用标准的距离场思路,先将椭圆归一化为单位圆,再通过smoothstep控制边缘平滑度,同时兼容平铺逻辑:
float inEllipseSmooth(vec2 pos, float width, float height, float smoothness, float tiling, vec2 offset) { // 归一化坐标并处理平铺偏移:将坐标映射到[0, tiling]区间,支持重复平铺 vec2 uv = fract(pos / iResolution.xy + offset / tiling) * tiling; vec2 center = vec2(tiling * 0.5); vec2 p = uv - center; // 将椭圆坐标归一化,转为单位圆计算距离 vec2 normalizedP = p / vec2(width, height); float dist = length(normalizedP); const float ellipseEdge = 1.0; // 单位圆的边缘阈值 // 适配椭圆尺寸的平滑范围,保证不同宽高的平滑效果一致 float smoothRange = smoothness * tiling / max(width, height); // smoothstep实现边缘平滑:smoothness=0时退化为阶跃函数,得到锐利边缘 return smoothstep(ellipseEdge + smoothRange, ellipseEdge - smoothRange, dist); }
方案优势
- 原生支持锐利边缘:当
smoothness=0时,smoothRange为0,smoothstep直接输出0或1的阶跃结果,边缘完全锐利。 - 灵活的平滑度控制:
smoothRange根据椭圆最大尺寸做了适配,避免宽高差异导致平滑效果不一致。 - 简洁的平铺逻辑:通过
fract函数自动处理坐标的重复平铺,配合offset轻松实现多纹理块的偏移拆分。
如果需要更灵活的距离值输出(比如用于后续抗锯齿或混合效果),可以修改函数返回距离差,在外部处理平滑:
// 返回椭圆距离场值:>0在椭圆内,<0在椭圆外 float getEllipseDist(vec2 pos, float width, float height, float tiling, vec2 offset) { vec2 uv = fract(pos / iResolution.xy + offset / tiling) * tiling; vec2 center = vec2(tiling * 0.5); vec2 p = uv - center; vec2 normalizedP = p / vec2(width, height); return 1.0 - length(normalizedP); } // 外部控制平滑度 float ellipseDist = getEllipseDist(pos, width, height, tiling, offset); float smoothResult = smoothstep(-smoothness, smoothness, ellipseDist);
内容的提问来源于stack exchange,提问作者Ni2Be
相关产品推荐
相关产品推荐

