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

基于嵌套循环生成径向/椭圆/跑道形渐变蒙版:自定义宽高与距离归一化

嵌套循环实现椭圆类渐变蒙版的问题排查与修正

核心问题拆解

问题1:自定义椭圆尺寸的Padding匹配异常

原有判断逻辑的失真原因:

  • 强转int的截断操作:计算偏移量平方除以半径平方的结果时,直接强转为int会截断所有小数部分。比如x偏移为3、x半轴为15时,计算结果是9/225=0.04,强转int后直接变为0,y方向的计算同理也变为0,最终x+y=0远小于1的判断阈值,大量本该在椭圆外的点会被误判为内部,最后生成的椭圆会收缩在中心区域,四周只剩1单位左右的冗余,完全达不到自定义Padding的预期效果
  • 参数定义混淆:椭圆标准方程中的半径参数是半轴长(即从椭圆中心到对应方向边缘的直线距离),不是椭圆的总宽、总高。如果直接把椭圆总宽当半径传入,生成的椭圆尺寸会是预期的两倍,自然会超出遍历范围
  • 对称Padding的自动实现逻辑:只要先定好固定遍历的总尺寸,再定义需要的椭圆总宽、总高,取遍历区域的正中心为椭圆中心,Padding会自动对称分布:比如遍历区域宽45、高100,指定椭圆总宽39、总高90时,左右Padding自动为(45-39)/2=3,上下Padding自动为(100-90)/2=5,不需要手动计算预留值

修正后的边界判断函数:

bool EdgeOrInBounds(Vector2 position) {
    // 全程用浮点数计算,禁止强转int截断小数
    float offsetX = position.x - center.x;
    float offsetY = position.y - center.y;
    // 这里的radius.x、radius.y直接存储椭圆的半轴长
    float ellipseEquationResult = (offsetX * offsetX) / (radius.x * radius.x) 
                                + (offsetY * offsetY) / (radius.y * radius.y);
    return ellipseEquationResult <= 1.0f;
}

问题2:椭圆内部距离的0-1归一化失效

原有距离计算的逻辑问题:

  • 欧氏直线距离的适配性差:原有的距离函数本身的数学计算逻辑没有错误,但欧氏直线距离只适合正圆的归一化。椭圆是拉伸的形状,不同方向的边缘到中心的距离不一样,同样的直线距离,在短轴方向可能已经碰到椭圆边缘了,在长轴方向可能还在很靠近中心的位置,直接拿这个直线距离除以半径做缩放,会出现渐变畸变,生成的渐变是正圆形的,和拉伸的椭圆形状不匹配
  • 无意义的重复计算:要生成顺着椭圆形状的渐变蒙版,根本不需要计算欧氏直线距离,直接用椭圆标准方程的计算结果做归一化即可

正确的归一化逻辑:
椭圆标准方程的计算结果t有固定规律:

  • 位置在椭圆中心时,offsetX、offsetY都为0,t=0
  • 位置在椭圆边缘时,椭圆方程刚好满足等于1的条件,t=1
  • 位置在椭圆外部时,t>1
    直接用t值就能实现符合要求的0-1归一化:
  • 椭圆外的位置返回0
  • 椭圆内的位置返回1 - t,数值会从中心的1平滑线性降到椭圆边缘的0,完全顺着椭圆形状分布,没有畸变

修正后的归一化计算代码:

float GetNormalizedMaskValue(Vector2 position) {
    float offsetX = position.x - center.x;
    float offsetY = position.y - center.y;
    float radiusX = radius.x;
    float radiusY = radiusY;
    float t = (offsetX * offsetX) / (radiusX * radiusX) 
            + (offsetY * offsetY) / (radiusY * radiusY);
    
    // 椭圆外直接返回0
    if (t >= 1.0f) return 0.0f;
    // 椭圆内返回1-t,实现中心亮、边缘暗的线性渐变
    return 1.0f - t;
}

// 原平方根函数的数学逻辑正确,可正常用于其他直线距离计算场景
float sqrt(float a) {
    return 1.0 / pow(a, -0.5f);
}

修正后的完整遍历逻辑

// 固定遍历的总尺寸
const int mapWidth = 45;
const int mapHeight = 100;
// 自定义需要的椭圆总宽、总高,调整这两个值即可自动匹配对称Padding
const float ellipseTotalWidth = 30;
const float ellipseTotalHeight = 70;

// 椭圆中心取遍历区域正中心,自动实现四周对称Padding
Vector2 center = new Vector2(mapWidth / 2.0f, mapHeight / 2.0f);
// 计算椭圆半轴长
Vector2 radius = new Vector2(ellipseTotalWidth / 2.0f, ellipseTotalHeight / 2.0f);

// 双循环遍历所有坐标点
for (int x = 0; x < mapWidth; x++) {
    for (int y = 0; y < mapHeight; y++) {
        Vector2 currentPos = new Vector2(x, y);
        float maskValue = GetNormalizedMaskValue(currentPos);
        // 将maskValue存入蒙版数组即可,数值范围为0~1
    }
}

通用扩展说明

如果后续需要做卵形、跑道形蒙版,只需要替换GetNormalizedMaskValue函数中的形状判断逻辑,归一化的思路完全通用,不需要重新调整循环结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 18:45:03