You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何计算有符号距离函数(SDF)交集(max操作)的梯度?

如何计算有符号距离函数(SDF)交集(max操作)的梯度?

嘿,这个问题问到点子上了!我来给你拆解清楚~

首先先回顾下你的设定:SDF在物体内部为负、表面为0、外部为正,两个物体的交集用max(sdf1, sdf2)来表示——这个前提没问题,咱们顺着这个逻辑来推导梯度。

梯度的计算要分三种场景来看,核心是看当前点到底由哪个SDF主导:

1. 当其中一个SDF的值严格大于另一个时

比如max(sdf1, sdf2) = sdf1(也就是sdf1 > sdf2),这说明当前点更靠近第二个物体的外部,或者说完全处于第一个物体的“影响范围”内。这时候max操作的结果完全由sdf1决定,梯度自然就等于sdf1的梯度

反过来,如果max(sdf1, sdf2) = sdf2,那梯度就直接取sdf2的梯度就行。

拿你举的两个球体例子来说:如果当前点的sdfSphere1sdfSphere2大,那这个点的交集SDF梯度就是球体1的梯度(p1 - x)/||p1 - x||;反之则用球体2的梯度。

2. 当两个SDF的值相等时(sdf1 = sdf2

这时候当前点刚好落在两个物体的交线/交面上,理论上是max函数的“不可导点”?别慌,在CSG的实际工程应用中,我们有很实用的处理方式:
先分别计算两个SDF的梯度,把它们各自归一化,然后取平均,最后再归一化一次,得到的就是交界面上的有效梯度。

这么做的原因是:交界面上的点同时属于两个物体的表面,我们需要的梯度方向是“离开两个物体交集”的方向,两个表面梯度的归一化平均刚好能满足这个需求,用它来做梯度下降的话,收敛会更稳定。

3. 处理数值精度问题

实际计算中,浮点误差会导致两个SDF的值很难严格相等,所以建议你设置一个很小的阈值(比如1e-6),当两个SDF的差值小于这个阈值时,就按上面“相等”的情况处理,避免梯度突然跳变影响结果。

给你个伪代码参考

def get_intersection_sdf_and_grad(p, sdf_func1, grad_func1, sdf_func2, grad_func2):
    d1 = sdf_func1(p)
    d2 = sdf_func2(p)
    eps = 1e-6
    
    if d1 > d2 + eps:
        # 由sdf1主导
        return (d1, grad_func1(p))
    elif d2 > d1 + eps:
        # 由sdf2主导
        return (d2, grad_func2(p))
    else:
        # 处理近似相等的情况,归一化后平均再归一化
        g1 = grad_func1(p)
        g2 = grad_func2(p)
        # 归一化单个梯度
        g1_norm = g1 / max(norm(g1), eps)
        g2_norm = g2 / max(norm(g2), eps)
        # 组合后再次归一化
        combined_grad = (g1_norm + g2_norm) / max(norm(g1_norm + g2_norm), eps)
        return ((d1 + d2)/2, combined_grad)

这样处理下来,不管是在交集的内部还是交界面上,梯度都能正确指导你的梯度下降找到物体表面啦~

备注:内容来源于stack exchange,提问作者LucioleMaléfique

火山引擎 最新活动