Search code examples
glsl

Super simple ray tracing


I am working on understanding simple ray tracing techniques using GLSL. I was referencing the tutorial from Inigo Quilez, [link] http://www.iquilezles.org/blog/?p=1521

My code compiles correctly without error, but does not produce the expected sphere output. The output image is entirely black. I cannot seem to figure out how to remedy this.

#ifdef GL_ES
precision highp float;
#endif

uniform vec3 unResolution;
uniform float time;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform sampler2D fft;
uniform vec4 unPar;
uniform vec4 unPos;
uniform vec4 unBeatBassFFT;

float iSphere( in vec3 ro, in vec3 rd, in vec4 sph )
{
    // sphere centered at origin
    // |ro|^2 + t^2 + 2<ro,rd>t - r^2 = 0
    vec3 oc = ro - sph.xyz;
    float b = 2.0*dot( oc,rd );
    float c = 2.0*dot( oc,oc );
    float h = b*b - sph.w*sph.w;
    if ( h<0.0 ) return -1.0;
    float t = (-b - sqrt(h))/2.0;
    return t; 
}
vec3 nSphere( in vec3 pos, in vec4 sph )
{
    return (pos-sph.xyz)/sph.w;
}

float iPlane( in vec3 ro, in vec3 rd )
{
    return -ro.y/rd.y;
}

vec3 nPlane( in vec3 pos ) 
{
    return vec3(0.0,1.0,0.0);
}

vec4 sph1 = vec4( 0.0, 1.0, 0.0, 1.0 );
float intersect( in vec3 ro, in vec3 rd, out float resT )
{
    resT = 1000.0;
    float id = -1.0;
    float tsph = iSphere( ro, rd, sph1 );
    float tpla = iPlane( ro, rd );
    if ( tsph>0.0 )
    {
        id = 1.0;
        resT = tsph;
    }
    if (tpla>0.0 && tpla<resT)
    {
        id = 2.0;
        resT = tpla;
    }
    return id;
}

void main(void) 
{
    vec3 light = normalize( vec3( 0.57703 ));
    vec2 uv = (gl_FragCoord.xy/unResolution.xy)*vec2(1.78,1.0);

    //sph1.x = 0.5*cos(time);
    //sph1.z = 0.5*sin(time);

    vec3 ro = vec3( 0.0, 0.5, 3.0 );
    vec3 rd = normalize( vec3( (-1.0+2.0+uv)*vec2(1.78,1.0), 1.0 ) );

    float t;
    float id = intersect( ro, rd, t );

    vec3 col = vec3(0.0);
    if( id>0.5 && id<1.5 )
    {
        vec3 pos = ro + t*rd; 
        vec3 nor = nSphere( pos, sph1 );
        float dif = clamp(dot( nor, light ), 0.0, 1.0);
        float ao = 0.5 + 0.5*nor.y;
    float amb = 0.5 + 0.5*nor.y;
        col = vec3( 0.9, 0.8, 0.6 )*dif+amb*vec3(0.5,0.6,0.7);
    }
    else if( id>1.5 )
    {
        vec3 pos = ro + t*rd;
        vec3 nor = nPlane( pos );
        float dif = clamp( dot(nor, light), 0.0, 1.0 );
        float amb = smoothstep( 0.0, 2.0*sph1.w, length(pos.xz-sph1.xz) );
        col = vec3(amb*0.7);
        //col = vec3( 1.0, 0.0, 0.0 )*dif + amb*vec3(0.5,0.6,0.7);
    }
    col = sqrt(col);

    gl_FragColor = vec4(col,1.0);
}

EDIT: Below is the updated code based on @LJᛃ's answer. Added for users who find this post via search engine.

I still haven't perfected the shading on the sphere but here's a working piece of code for y'all!

#ifdef GL_ES
precision highp float;
#endif

uniform vec2 resolution;
#define unResolution resolution
uniform float time;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform sampler2D fft;
uniform vec4 unPar;
uniform vec4 unPos;
uniform vec4 unBeatBassFFT;

float iSphere( in vec3 ro, in vec3 rd, in vec4 sph )
{
    // sphere centered at origin
    // |ro|^2 + t^2 + 2<ro,rd>t - r^2 = 0
    vec3 oc = ro - sph.xyz;
    float b = 2.0*dot( oc,rd );
    float c = 2.0*dot( oc,oc );
    float h = b*b - sph.w*sph.w;
    if ( h<0.0 ) return -1.0;
    float t = (-b - sqrt(h))/2.0;
    return t; 
}
vec3 nSphere( in vec3 pos, in vec4 sph )
{
    return (pos-sph.xyz)/sph.w;
}

float iPlane( in vec3 ro, in vec3 rd )
{
    return -ro.y/rd.y;
}

vec3 nPlane( in vec3 pos ) 
{
    return vec3(0.0,1.0,0.0);
}

vec4 sph1 = vec4( 0.0, 1.0, 0.0, 1.0 );
float intersect( in vec3 ro, in vec3 rd, out float resT )
{
    resT = 1000.0;
    float id = -1.0;
    float tsph = iSphere( ro, rd, sph1 );
    float tpla = iPlane( ro, rd );
    if ( tsph>0.0 )
    {
        id = 1.0;
        resT = tsph;
    }
    if (tpla>0.0 && tpla<resT)
    {
        id = 2.0;
        resT = tpla;
    }
    return id;
}

void main(void) 
{
    vec3 light = normalize( vec3( 0.57703 ));
    vec2 uv = (gl_FragCoord.xy/unResolution.xy);
    vec2 p = uv * 2.0 - 1.0;
    p.x*=unResolution.x/unResolution.y;

    //sph1.x = 0.5*cos(time);
    //sph1.z = 0.5*sin(time);

    vec3 ro = vec3( 0.0, 1.0, -1.0 );
    vec3 rd = normalize( vec3(p,0.2) );

    float t;
    float id = intersect( ro, rd, t );

    vec3 col = vec3(0.0);
    if( id>0.5 && id<1.5 )
    {
        vec3 pos = ro + t*rd; 
        vec3 nor = nSphere( pos, sph1 );
        float dif = clamp(dot( nor, light ), 0.0, 1.0);
        float ao = 0.1 + 0.5*nor.y;
        float amb = 0.5 + 0.5*nor.y;
        col = vec3( 1.0, 0.0, 0.0 )*dif + amb*vec3(0.5,0.6,0.7);
    }
    else if( id>1.5 )
    {
        vec3 pos = ro + t*rd;
        vec3 nor = nPlane( pos );
        float dif = clamp( dot(nor, light), 0.5, 1.0 );
        float amb = smoothstep( 0.5, 2.0*sph1.w, length(pos.xz-sph1.xz) );
        col = vec3(amb*0.9);
        //col = vec3( 1.0, 0.0, 0.0 )*dif + amb*vec3(0.5,0.6,0.7);
    }
    col = sqrt(col);

    gl_FragColor = vec4(col,1.0);
}


Solution

  • So I ported this to GLSLSandbox.com, I substituted the unResolution uniform with the one glsl sandbox provides:

    uniform vec2 resolution;
    #define unResolution resolution
    

    Then I looked at your basic setup for ray origin and direction. The way you're trying to account for the aspect ratio in the uv is wrong, you want calculate the centered coordinate first, then apply the aspect ratio transform:

    vec2 uv = gl_FragCoord.xy/unResolution.xy;
    // p is 0 at the center of the screen, x=-1 on the left, +1 on the right etc.
    vec2 p = uv * 2.0 - 1.0;
    // account for aspect ratio
    p.x*=unResolution.x/unResolution.y;
    
    // substitute uv with p in the following code
    

    At this point I saw what I assumed to be your ground plane, the screen being horizontally split between offwhite and black, so I played with your ray origin and direction a bit:

    vec3 ro = vec3( 0.0, 1., -1. );
    vec3 rd = normalize( vec3( p, .25 ) )
    

    And voila we have a sphere: rendered image

    The projection is still kinda weird, moving the ray origin doesn't behave the way I'd expect it to, but I'll leave that to you :)