Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
/*
* Reviewing ray-tracing basics in glsl. Loosely based on Inigo Quilez's articles.
* Éric Renaud-Houde - num3ric.com
* December 2012
*/
#ifdef GL_ES
precision highp float;
#endif
#define EPS 0.0001
#define PI 3.14159265
#define TWO_PI 6.28318530
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
struct Ray {
vec3 o; //origin
vec3 d; //direction (should always be normalized)
};
struct Sphere {
vec3 pos; //center of sphere position
float rad; //radius
vec4 col; //surface color
};
struct Camera {
vec3 pos; //camera position
vec3 aim; //view target
float fov; //field of view
};
float cosr = cos(TWO_PI*mouse.x-PI);
float sinr = sin(TWO_PI*mouse.x-PI);
mat3 rotationMatrix = mat3(cosr, 0.0, sinr, 0.0, 1.0, 0.0, -sinr, 0.0, cosr);
float focusDistance = 5.0;
Camera cam = Camera(rotationMatrix*vec3(0.0, 1.5, focusDistance),
vec3(0.0, 0.5, 0.0), 10.0);
float rrad = 1.35;
float rspeed = 0.01*sin(0.001*time)+1.0;
Sphere sphere1 = Sphere(vec3(rrad*cos(rspeed*time), 1.0, rrad*sin(rspeed*time)),
1.0, vec4(0.7, 0.9, 0.0, 1.0));
Sphere sphere2 = Sphere(vec3(-rrad*cos(rspeed*time), 1.0, -rrad*sin(rspeed*time)),
1.0, vec4(1.0, 0.2, 0.0, 1.0));
float reflection_factor = 0.25;
vec4 specularColor = vec4(1.0);
vec3 lightPos = vec3(10.0, 10.0, 10.0);
vec4 amb = vec4(0.1, 0.2, 0.4, 1.0);
/* ---------- Object intersection functions ---------- */
float intersectSphere(in Ray ray, in Sphere sphere)
{
vec3 oc = ray.o - sphere.pos;
float b = 2.0 * dot(ray.d, oc);
float c = dot(oc, oc) - sphere.rad*sphere.rad;
float disc = b * b - 4.0 * c;
if (disc < 0.0)
return -1.0;
// compute q as described above
float q;
if (b < 0.0)
q = (-b - sqrt(disc))/2.0;
else
q = (-b + sqrt(disc))/2.0;
float t0 = q;
float t1 = c / q;
// make sure t0 is smaller than t1
if (t0 > t1) {
// if t0 is bigger than t1 swap them around
float temp = t0;
t0 = t1;
t1 = temp;
}
// if t1 is less than zero, the object is in the ray's negative direction
// and consequently the ray misses the sphere
if (t1 < 0.0)
return -1.0;
// if t0 is less than zero, the intersection point is at t1
if (t0 < 0.0) {
return t1;
} else {
return t0;
}
}
float intersectPlane(in Ray ray)
{
return -ray.o.y/ray.d.y;
}
int worldIntersect(in Ray ray, in float maxlen, in int id, inout float t)
{
t = maxlen;
float ts1 = intersectSphere(ray, sphere1);
float ts2 = intersectSphere(ray, sphere2);
float tp = intersectPlane(ray);
//FIXME: why is id needed to prevent surface acne (idem in worldShadow)?
if (ts1 > EPS && id !=1) {
t = ts1;
id = 1;
}
if (ts2 > EPS && ts2 < t && id !=2 ) {
t = ts2;
id = 2;
}
if ( tp > EPS && tp < t && id !=3 ) {
t = tp;
id = 3;
}
return id;
}
/* ---------- Object normals functions ---------- */
vec3 sphereNormal(in vec3 pos, in Sphere sphere)
{
return normalize((pos - sphere.pos)/sphere.rad);
}
vec3 worldNormal(in vec3 pos, in int id)
{
if (id == 1) {
return sphereNormal(pos, sphere1);
} else if (id == 2) {
return sphereNormal(pos, sphere2);
} else if (id == 3) {
return vec3(0.0, 1.0, 0.0);
}
}
/* ----------------------------------------------- */
float worldShadow(in Ray ray, in float maxlen, in int id)
{
float ts1 = intersectSphere(ray, sphere1);
float ts2 = intersectSphere(ray, sphere2);
if(ts1 > EPS && id !=1 )
return 0.0;
if(ts2 > EPS && id !=2 )
return 0.0;
return 1.0;
}
float diffuseFactor(in vec3 surfaceNormal, in vec3 lightDir) {
return clamp(dot(surfaceNormal, lightDir), 0.0, 1.0);
}
float specularFactor(in vec3 surfaceNormal, in vec3 lightDir)
{
vec3 viewDirection = normalize(cam.pos);
vec3 halfAngle = normalize(lightDir + viewDirection);
float ks = dot(surfaceNormal, halfAngle);
ks = clamp(ks, 0.0, 1.0);
ks = pow(ks, 50.0);
return ks;
}
void applyFog(in float t, inout vec4 col)
{
col = mix(col, amb, clamp(sqrt(t*t)/10.0, 0.0, 1.0));
}
void reflect(inout Ray ray, in vec3 surfaceNormal)
{
float cosI = -dot(surfaceNormal, ray.d);
ray.d = ray.d + 2.0*cosI*surfaceNormal;
}
vec4 rendererCalculateColor(inout Ray ray, inout int id)
{
vec4 col = vec4(0.0);
float t = 0.0;
float maxlen = 1000.0;
bool hit = false;
// Find the ray's closest intersection in the world scene
id = worldIntersect(ray, maxlen, id, t);
if (t<0.0 || t+EPS>maxlen) {
applyFog(maxlen, col);
return col;
}
// Compute the color (diffuse & specular reflection)
vec3 pos = ray.o + t*ray.d;
vec3 lightDir = normalize(lightPos-pos);
vec3 surfaceNormal = worldNormal(pos, id);
if (id == 1) {
float dif = diffuseFactor(surfaceNormal, lightDir);
float spec = specularFactor(surfaceNormal, lightDir);
col = dif*sphere1.col+spec*specularColor;
} else if (id == 2) {
float dif = diffuseFactor(surfaceNormal, lightDir);
float spec = specularFactor(surfaceNormal, lightDir);
col = dif*sphere2.col+spec*specularColor;
} else if (id == 3) {
col = vec4(0.7, 0.7, 0.7, 1.0);
}
// Darken the color if it's in the shadow
col *= worldShadow(Ray(pos, lightDir), 100.0, id);
col = col + 0.2*amb;
// Apply fog to the color using the distance to the intersection
applyFog(t, col);
// Update the ray for the next reflection iteration
reflect(ray, surfaceNormal);
ray.o = pos;
return col;
}
void main( void ) {
vec2 uv = gl_FragCoord.xy/resolution.xy - 0.5;
vec3 d = (cam.aim - cam.pos) + rotationMatrix*vec3(cam.fov*uv, 0.0);
d.y *= resolution.y/resolution.x;
Ray ray = Ray(cam.pos, normalize(d));
vec3 surfaceNormal;
int id = 0;
vec4 col = rendererCalculateColor(ray, id);
//We compute reflected rays iteratively (no recursion in this version of glsl)
//Both 'ray' and intersection 'id' are updated by renderedCalculateColor.
for(int i=1; i<=4; ++i) {
vec4 new_col = rendererCalculateColor(ray, id);
col = (1.0-reflection_factor)*col + reflection_factor*mix(new_col, col, 1.0-reflection_factor);
}
gl_FragColor = col;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.