CG‎ > ‎

    Distance Fields

    September 2011

    Signed distance functions have become quite popular in recent years for easily and elegantly creating geometry which requires very little data but can produce visually interesting shapes. Especially in the demoscene, where there has been an explosion of mathematical tricks and tools for producing the most impressive shapes and shadings with a minimum of code. This method also goes by the name 'functional representation' or F-rep, in comparison to B-rep which stands for 'boundary representation' and usually refers to vertex-edge-polygon topologies which theoretically (in the strictest sense) possess area but no volume. F-rep for videogames has been explored in the book GPU Pro (first edition), in which some interesting realtime animations were produced by combining B-rep models with F-rep augmentations composed of metacylinder skeletons. One advantage of distance field rendering is that a wide class of shapes can be manipulated. Most of the time the rendering process is accelerated by using the returned distance value to offset the sampling point (please see below for a link to Inigo Quilez's paper on 'two-triangle worlds', an excellent exploration of fragment-shader-only visuals). However, I have encountered difficulties when attempting to render complex nonlinear functions with this method, as the assumption that the distance value is actual euclidean distance breaks down very soon when you start adding twists, expansions, and compactions to your space.


    Metacubes

    The image immediately above, and the next four images are from my most recent work in raymarching on GPU to create interesting images. I set up a window and OpenGL 3 context with freeglut and gl3w so I could have only what was required to run a fragment shader for each pixel in the image. Once this is set up, anything can be achieved using the power of GLSL and raymarching!


    Using mod() to replicate a cylinder, and a procedural
    darkening of the image's periphery


    mod() repetition again, but with rounded cubes and
    subtle fresnel highlighting

    Here is the GLSL code I used to create the repeated, bevelled cubes. The 'tile' is one rotated cube, and each tile instance has specific parameters.

    vec3 rotateY(vec3 p, float ang)
    {
        float c = cos(ang), s = sin(ang);
        return vec3(p.z * c - p.x * s, p.y, p.z * s + p.x * c);
    }

    vec3 rotateZ(vec3 p, float ang)
    {
        float c = cos(ang), s = sin(ang);
        return vec3(p.x * c - p.y * s, p.x * s + p.y * c, p.z);
    }

    float square(float x) { return x * x; }

    float tile(vec3 p, vec3 pi)
    {
        p = rotateZ(rotateY(p, pi.x), pi.z + sin(p.z * 0.2 + p.x * 0.2 + time));
        p.y += cos(pi.x) * sin(pi.z);

        // compute distance to cube. if closest point on cube is a vertex, then
        // this will be the distance to that vertex.
       
        float a = sqrt(square(max(0, +p.x - 1.0)) +
                       square(max(0, +p.y - 1.0)) +
                       square(max(0, +p.z - 1.0)) +
                       square(max(0, -p.x - 1.0)) +
                       square(max(0, -p.y - 1.0)) +
                       square(max(0, -p.z - 1.0)));
                      
        // offseting this returned value makes the cube rounder
        return a - 0.2;

    }

    // this function returns the distance to the closest point on
    // the scene's geometry.
    float scene(vec3 p)
    {
        // pi = the snapped position of p, or the origin of the tile that p lies in
        vec3 pi = p / TILE_SIZE - fract(p / TILE_SIZE);
       
        // p = the sub-tile position, with {0, y, 0} at the centre of the tile
        p = vec3(mod(p.x, TILE_SIZE) - TILE_SIZE * 0.5, p.y + 2, mod(p.z, TILE_SIZE) - TILE_SIZE * 0.5);

        // evaluate the tile twice, to avoid skipping too far
        return min(tile(p, pi), tile(p - vec3(0, 0, TILE_SIZE), pi + vec3(0, 0, 1)));
    }


    Undulating torus shapes

    Here is the distance function for the torus shape. While it is simple to calculate the intersection of a ray with a torus, using minimum distance evaluations allows easy modification of the torus shape.

    float donut(vec3 p, float r)
    {
        return distance(p, vec3(normalize(p.xy), 0)) - r;
    }


    Torus shapes with varying inner radii. They are also
    shaded with a fresnel effect, and a cycling hue.


    Distance-Field Texture Mask


    Uses technique published by Valve. Highly-detailed texture mask without a high-resolution. No additional shaders required. Avoids jagged or blobby appearance of lines and curves.
    Now that NV_path_rendering is being developed, this technique may not longer be needed to display crisp edges for path-generated images.

    http://www.valvesoftware.com/.../SIGGRAPH2007_AlphaTestedMagnification.pdf
    http://developer.download.nvidia.com/assets/gamedev/files/GL_NV_path_rendering.txt
    http://developer.nvidia.com/nv-path-rendering-videos/ - NV_path_rendering demonstration videos by Mark J. Kilgard


    Distance Field Raycasting on GPU (CUDA)


    A simple distance-field raycaster. Implicit shapes with simple transformations and animation. Multiple point lightsources. This was my first piece of work on distance-field rendering, so it only has simple features.

    Almost demoscene-ish!

    Download

    http://www.iquilezles.org/www/material/nvscene2008/nvscene2008.htm

    http://gpupro.blogspot.com/ - See "Polygonal-Functional Hybrids for Computer Animation and Games". I would also recommend this book to anyone interested in modern CG, as I enjoyed reading it myself. There is now a second edition of this book which I have not yet read.
    Comments