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

如何渲染可调节边缘硬度且支持平铺的平滑椭圆?

自定义边缘硬度的平铺椭圆渲染问题

我需要渲染一个支持自定义边缘硬度的椭圆,同时要实现平铺功能(能按指定偏移把渲染结果拆分成多张纹理)。

我编写了如下函数:

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 02:54:25