Been a while since I haven’t really been doing some stuff in the pixel shader world, when swimming earlier tonight, I was thinking about making a kind of recap that could be used as a way to digest some of the concept. This is not a “how to ray march” but rather a revisiting of the way I write some of my shaders, in a sort of list.

UV stuff

Remap:

I always remap between -1 and 1, instead of 0 to 1.

vec2 uv = (fragCoord -.5 * iResolution.xy) / iResolution.y;

Mirror:

For now it’s only on the x axis, but you can do it on both axis with the same idea.

vec2 uv = (fragCoord -.5 * iResolution.xy) / iResolution.y;
uv.x = abs(uv.x);

Repeat:

Repeat makes our UV repeating, in this case I use a 12 repeat factor.

vec2 uv = (fragCoord - .5 * iResolution.xy) / iResolution.y;
uv *= 12.f;
uv = fract(uv);

vec3 col =vec3(uv.x * uv.y);

Polar Coordinate:

Makes our UV from cartesian to polar coordinate.

vec2 uv = (fragCoord - .5 * iResolution.xy) /iResolution.y;
vec2 st = vec2(atan(uv.x, uv.y), length(uv));

vec3 col =vec3(st.x, st.y, 0.0);

Texture sampling

Cartesian texture sampling:

vec3 texture001 = texture(iChannel0, uv).rgb;

Polarcoordinate texture sampling:

vec2 uv = (fragCoord - .5 * iResolution.xy) /iResolution.y;
vec2 st = vec2(atan(uv.x, uv.y), length(uv));
vec3 texture001 = texture(iChannel0, st).rgb;

Repeat texture:

   vec3 texture001 = texture(iChannel0, uv * 5.).rgb;

3D and SDF

Basic setup:

Simple setup for ray origin and ray direction, pointing in +1 Z.

vec2 uv = (fragCoord - .5 * iResolution.xy) /iResolution.y;
vec3 ro = vec3(0.0, 5.0, -15.0);
vec3 rd = normalize(vec3(uv.x, uv.y, 1)); 

Basic sphere:

In this example we query the render loop from the main function of our pixel shader. We query every pixel to render a hit or not by using SDF formulas. This is the basic ideas of calling the raymarching algorithm.

Note that we won’t deal with object hit and whatever material system. For now: We hit or not, and we place it in white.

float sphere(vec3  pos, float radius)
{	

    return length(pos) - radius;
}



float map(vec3 pos)
{

    float res = sphere(pos, 1.0f);
    
    return res;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{   

    
    vec2 uv = (fragCoord - .5 * iResolution.xy) /iResolution.y;
    vec3 ro = vec3(0.0, 0.0, -5.0);
    vec3 rd = normalize(vec3(uv.x, uv.y, 1)); 
    vec3 col = vec3(0.0);
    // Scene render
    for(int i = 0; i < 1064; i++)
    {
       	float d/*istance*/ = map(ro); 
        if(d < 0.01)
        {	
            col = vec3(1.0f);
        	break;
        }
        ro += d * rd;
        
    }

    // Output to screen
    fragColor = vec4(col,1.0);
}

Basic Normal:

Now that we have a hit point, we need to deal with the normal of that surface. Note that the function takes a position that will query the scene from the position of whatever we try to “get the normal” from. We query the map() function using a slight offset, and compare it to get the normal of the surface.

vec3 getNormalAtPoint(vec3 pos)
{
    vec2 eps = vec2(0.1, 0.0);
    return normalize(vec3(
        map(pos + eps.xyy) - map(pos - eps.xyy),
        map(pos + eps.yxy) - map(pos - eps.yxy),
        map(pos + eps.yyx) - map(pos - eps.yyx)
    ));
}

Let’s remember that for every pixel we compute the hitpoint. So we call the getNormalAtPoint() using where to ro is on a surface if we hit a point, so ro becomes the querying point.

vec2 uv = (fragCoord - .5 * iResolution.xy) /iResolution.y;
vec3 ro = vec3(0.0, 0.0, -5.0);
vec3 rd = normalize(vec3(uv.x, uv.y, 1)); 
vec3 col = vec3(0.0);
// Scene render
for(int i = 0; i < 1064; i++)
{
       float d/*istance*/ = map(ro); 
    if(d < 0.01)
    {    
        col = vec3(1.0f);
        break;
    }
    ro += d * rd;

}

And therefore the ro variable becomes our interest has we hit a point

Leave a Reply

Your email address will not be published. Required fields are marked *