GLSL光线步进(Ray Marching)渲染SDF重复结构时出现伪影问题咨询
问题原因
你出现伪影的核心原因是SDF计算违反了Lipschitz连续约束,这是光线步进算法能正常工作的核心前提:要求任意采样点返回的SDF值必须小于等于该点到场景中所有表面的最短距离。
你现有代码的逻辑缺陷很明确:先对z坐标取模得到当前单元的局部坐标,再根据当前所在圆盘的序号做x方向位移,最后只返回当前单元内的圆盘SDF值。当交替位移量变大后,相邻z层的圆盘会横向伸入当前采样点所在的z单元,你计算的当前单元SDF完全没有覆盖这些跨单元的表面,返回的距离值比实际最近表面的距离大,导致光线步进的步长过大直接穿透表面,就产生了渲染伪影。
如果所有圆盘统一向同一个方向位移,不会出现相邻圆盘跨单元伸入的情况,自然就不会触发问题。
修复方案
有两种常见的修复思路:
- 调整计算顺序,先给坐标应用位移,再执行模运算,模运算的范围要覆盖位移的最大幅度,确保所有可能的最近表面都被纳入计算
- 直接同时计算当前z单元、前一个z单元、后一个z单元共3层圆盘的SDF值,取最小值作为最终返回结果,避免漏掉相邻单元位移过来的最近表面
第二种方案实现更简单,适配性更高,修改后的代码如下:
vec2 scene(in vec3 p) { float thick = 0.1; float z_step = 6.0 * thick; vec3 cp = p; float min_d = 1e10; float res_idx = 0.0; // 计算当前采样点所属的基准z层序号 float base_idx = floor((cp.z + 0.5 * z_step) / z_step); // 遍历前后共3层z单元,取最小SDF值 for(int i = -1; i <= 1; i++) { float idx = base_idx + float(i); vec3 q = cp; q.z -= idx * z_step; // 应用对应层的x方向位移 if(mod(idx, 2.0) == 0.0) { q.x += sin(idx * t) * t * t; } else { q.x -= sin(idx * t) * t * t; } float d = tunnel_fragment(q, vec3(0.0), vec3(0.0, 0.0, 1.0), 2.0, thick, 0.2); if(d < min_d) { min_d = d; res_idx = idx; } } return vec2(min_d, res_idx); }
内容的提问来源于stack exchange,提问作者buddhabrot
相关产品推荐
相关产品推荐

