2. Ray Marching

Mandelbulb con RayMarching #

Ray casting #

Es el uso da la intersección de un rayo con una superficie para solucionar diferentes problemas, como determinar el primer elemento interceptado por un rayo, determinar caras ocultas, renderización volumetrica.

Ray tracing #

Técnica de renderizado aplicando una solución analítica al problema de la intersección del rayo con la superficie.

Ray Marching #

Algoritmo iterativo que para encontrar la intersección del rayo con la superficie.

  • Paso fijo: se define un tamaño para cada paso y se va sumando esta distancia al rayo iterativamente hasta encontrar la interseccion de la superficie con el rayo.
  • Sphere tracing: se utiliza SDFs para calcular la distancia recorrida por el rayo en cada iteracion hasta encontrar la interseccion.

rayMarching

Script p5 raymarching.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
let rayMarchingShader;
let easycam;

function preload() {

    // Load shader with position2 varying
    params = {
        precision: Tree.highp, 
        matrices: Tree.NONE, 
        varyings: Tree.position2
    }
    
    rayMarchingShader = readShader('/showcase/sketches/mandelbulb/shader/rayMarching/static/raymarching.frag', params);
}

function setup() {
    let canvas = createCanvas(725, 725, WEBGL);

    parent.disableScroll(canvas.canvas);

    easycam = createEasyCam({ distance: 500 });

    shader(rayMarchingShader);
    textureMode(NORMAL);
    noStroke();
}

function draw() {
    background(0);
    quad(-1, -1, 1, -1, 1, 1, -1, 1);
}
Fragment shader raymarching.frag
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
precision highp float;
varying vec2 position2;

const int MAX_RAY_STEPS = 100;
const float MAX_MARCH_DISTANCE = 100.;
const float MIN_MARCH_DISTANCE = 0.001;

// Get the closest distance from p to world
float world(vec3 p) {
    // SDF sphere
    float sphere = length(p ) - 1.;
    return sphere;
}

// Ray casting with ray march algorithm
float rayMarch(vec3 ro, vec3 rd) {
    // Initialize distance
    float distanceOrigin = 0.;

    for(int i = 0; i < MAX_RAY_STEPS; i++) {

        // Calcualte ray position
        vec3 rp = ro + distanceOrigin * rd;

        // Get the closest distance to ray
        float map = world(rp);

        // Check if ray intersects
        if(map < MIN_MARCH_DISTANCE)
            break;

        // Advance ray to the next closest point
        distanceOrigin += map;

        // Break if ray is too far
        if(distanceOrigin > MAX_MARCH_DISTANCE) {
            distanceOrigin = -1.;
            break;
        }
    }

    // return intersection distance
    return distanceOrigin;
}

void main() {

    // Camera origin, ray origin, ray direction
    vec3 co = vec3(0, 0, -2.);
    vec3 ro = co;
    vec3 rd = normalize(vec3(position2.x, position2.y, 1.));

    // Initial color (background)
    vec3 col = vec3(0.);

    // Spawn ray and get distance
    float t = rayMarch(ro, rd);

    // Temp color based on distance
    col = vec3(clamp(t, 0., 1.));
    
    // Set fragment color
    gl_FragColor = vec4(col, 1.);
}

Traslaciones y rotaciones #

Script p5 raymarching.js
33
34
35
36
37
38
39
40
41
42
43
44
45
function drawShader() {
    push();

    shader(rayMarchingShader);

    rayMarchingShader.setUniform('cameraCenter', easycam.getCenter());
    rayMarchingShader.setUniform('cameraDistance', easycam.getDistance());
    rayMarchingShader.setUniform('dMatrix', dMatrix().mat3);

    quad(-1, -1, 1, -1, 1, 1, -1, 1);

    pop();
}
Fragment shader raymarching.frag
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
uniform float cameraDistance;
uniform vec3 cameraCenter;
uniform mat3 dMatrix;

// Get the closest distance from p to world
float world(vec3 p) {
    // Set point to initial camera distance
    p /= 500.;

    // transform the point to simulate camera zoom
    p /= 1. / cameraDistance;

    // apply rotation and translation transformations from object
    p *= dMatrix;

    // sphere SDF translated on Y axis to see dMatrix effects
    float sphere = length(p - vec3(0., 1., 0.)) - .5;

    return sphere;
}
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
void main() {
    // Camera origin, ray origin, ray direction
    vec3 co = vec3(cameraCenter + vec3(0, 0, -2.));
    vec3 ro = co;
    vec3 rd = normalize(vec3(position2.x, position2.y, 1.));

    // Initial color (background)
    vec3 col = vec3(0.);

    // Spawn ray and get distance
    float t = rayMarch(ro, rd);

    // Temp color based on distance
    col = vec3(clamp(t, 0., 1.));
    
    // Set fragment color
    gl_FragColor = vec4(col, 1.);
}

Referencias #