0

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);
}

  • too lazy to analyze your code instead see [GLSL 3D Mesh back raytracer](https://stackoverflow.com/a/45140313/2521214) for comparison ... btw what is the output ? You wrote `does not produce the expected sphere output` so what does it render ? nothing or something distorted (image would be fine in such case) – Spektre Sep 29 '21 at 06:43
  • Hi @Spektre, thanks for the link. I am looking at it now. It currently renders a entirely black image (ill edit the post) – user7428090 Sep 29 '21 at 15:14

1 Answers1

1

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 :)

LJᛃ
  • 7,655
  • 2
  • 24
  • 35