Search code examples
glslraymarching

GLSL artefacts when ray marching


In the following shadertoy I illustrate an artefact that occurs when raymarching

https://www.shadertoy.com/view/stdGDl

This is my "scene" (see code fragment below). It renders a primitive "tunnel_fragment" which is an SDF (Signed Distance Function), and uses modulo on the coordinates to calculate "infinite" repetitions of these fragments. It then also calculates which disk we are in (odd/even) to displace them.

I really don't understand why these artefacts occur when the disks (or rings -> see tunnel_fragment, if you remove a comment they become rings instead of disks) present these artefacts when the alternate movement in x direction becomes large.

These artefacts don't appear when the disk structure moves to the right on its whole, it only appears when the disks alternate and the entire structure becomes more complex.

What am I doing wrong? It's really boggling me.

vec2 scene(in vec3 p)
{
    float thick = 0.1;
    vec3 cp = p;

    // Use modulo to simulate inf disks
    vec3 c = vec3(0,0,6.0*thick);
    vec3 q = mod(cp+0.5*c,c)-0.5*c;
    
    // Find index of the disk
    vec3 disk = (cp+0.5*c) / (c);
    float idx = floor(disk.z);
    
    // Do something simple with odd/even disks
    // Note: changing this shows the artefacts are always there
    if(mod(idx,2.0) == 0.0) {
        q.x += sin(disk.z*t)*t*t;
    } else {
        q.x -= sin(disk.z*t)*t*t;
    }

    float d = tunnel_fragment(q, vec3(0.0), vec3(0.0, 0.0, 1.0), 2.0, thick, 0.2);
    return vec2(d, idx);
}

Solution

  • The problem is illustrated with this diagram:

    enter image description here

    When the current disk (based on modulo) is offset by more than the spacing between the disks, then the distance that you calculate is larger than the distance to the next disk. Consequently you risk in over-stepping the next disk.

    To solve this you need to either limit the offset (as said -- no more than the spacing between the disks), or sample odd/even disks separately and min() between them.