Skip to content

Commit

Permalink
Better image; update GLSL code.
Browse files Browse the repository at this point in the history
  • Loading branch information
terrynsun committed Oct 26, 2015
1 parent 7f4042d commit b915627
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 65 deletions.
34 changes: 27 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ The Shadertoy demo includes a large set of debug flags, where you can turn on:
* `OVERRELAX/SPEHRETRACE/NAIVE`: for different raymarching types
* `DISTANCE/NORMAL/ITERS`: for different debug displays

Check out the Shadertoy demo: there's animation!

[![](img/thumb.png)][shadertoy]

[shadertoy]: https://www.shadertoy.com/view/XISSRc
Expand Down Expand Up @@ -118,7 +120,9 @@ that location.

For this raymarch we must use a naive method (with fixed step size), since the
function does not compute a distance from a point on the ray to the terrain (it
only defines the terrain height at a given point).
only defines the terrain height at a given point). That makes this technique
very, very slow -- especially when the other two raymarches (initial object and
soft shadow) can be optimized with sphere tracing.

#### Sphere Overrelaxation

Expand All @@ -127,13 +131,19 @@ Reference: McQuire ["Implicitly Defined Surfaces"][mcguire], Keinert ["Enhanced
The number of ray march iterations displayed as grayscale, with darker areas
indicating fewer iterations before the surface is considered intersected.

![](img/iters_spheretracing.png)
(This method is not currently working...)

![](img/iters_overrelax.png)
### Performance

(This method is not currently working.)
Performance data was taken from the following four scenes:

### Performance
![](img/scenes-all4.png)

Data was taken with [stats.js][statsjs]. Unfortunately it was hard to get a good
reading by using the ms timer in statsjs, so I mostly relied on taking the FPS
counter and calculating millisecond timing by 1000/FPS.

[statsjs]: https://github.com/mrdoob/stats.js/

Render times for different scenes grouped by raymarch type:

Expand All @@ -159,16 +169,21 @@ Main takeaways:
light.

* stats.js wasn't great for finding timing data: all of the shadow-only timings
are actually bound by the 60fps cap.
are actually bound by the 60fps cap. I intended to also take
no-shadow/no-terrain data, but given this finding it would not have been very
useful.

* There sphere scene was similar to the close scene in composition, but had only
one object rather than many. The difference in time between those two are
likely due ot repeated distance function computations.

Another comparison I should have done would be a box and a menger sponge of the
same size.

Since there's no use in directly comparing sphere-traced and naive times, this
is a graph that scales render times by the full version of that raymarch type:

![](img/perf_scaled.png)
![](img/perf_scaled_times.png)

### Bonus images

Expand All @@ -180,6 +195,11 @@ Normals for each geometry, computed with the equation given in McGuire (8).

![](img/debug_normals.png)

Some geometry with iteration displayed as color (white indicates more raymarches
before intersection).

![](img/iters_spheretracing.png)

Naive ray marching with a step size of 0.1 (too large), got wireframes instead
of solid shapes:

Expand Down
Binary file added img/scenes-all4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified img/thumb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
155 changes: 97 additions & 58 deletions raymarch.glsl
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
/********************************** Constants *********************************/
/********************************** Constants *********************************/
const vec3 light = vec3(2.0, 5.0, -1.0);
const vec3 color = vec3(0.85, 0.85, 0.85);
const vec3 ambient = vec3(0.05, 0.05, 0.05);
const vec3 background = vec3(0.2, 0.5, 0.5);
const vec3 ground = vec3(0.2, 0.8, 0.8);
const vec3 background = vec3(0.3, 0.5, 0.5);
const vec3 ground = vec3(0.8, 0.8, 0.2);

const vec3 scene2cam = vec3(.35, 2.5, -2);
//const vec3 scene2cam = vec3(2.5, 2.2, -.8);
const float EPSILON = 0.01;
const float TMIN = 0.01;
const float TMAX = 20.0;

// Debug flags
#define OVERRELAX 0
Expand All @@ -19,13 +20,47 @@ const float TMIN = 0.01;
#define NORMAL 0
#define ITERS 0

#define SHADOW 0
#define SHADOW 1

#define PLANE 0
#define HEIGHTMAP 1

#define FIXEDCAM 1

#define SCENE1 0
#define SCENE2 1

/***************************** Geometry Combinators ***************************
* McGuire 11: http://graphics.cs.williams.edu/courses/cs371/f14/reading/implicit.pdf
*/

// set union of two geometry
float setunion(float d1, float d2) {
return min(d1, d2);
}

// intersection of two geometry
float intersect(float d1, float d2) {
return max(d1, d2);
}

// set subtraction of d2 from d1
float subtract(float d1, float d2) {
return max(d1, -d2);
}

// transformation OF P by applying the inverse transform
vec3 tr(vec3 p, vec3 translate) {
return p - translate;
}

// transformation OF P by magic rotatiion tricks
vec3 rot(vec3 p, float x) {
float t = iGlobalTime/3.;
vec3 offset = x * vec3(cos(t), 0, sin(t)) + vec3(0,1,0);
return tr(p, offset);
}

/*************************** Signed distance functions ***********************
* McGuire: http://graphics.cs.williams.edu/courses/cs371/f14/reading/implicit.pdf
* iq: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
Expand All @@ -49,6 +84,28 @@ float sdBox(vec3 p, vec3 b) {
return min(dmax, 0.0) + length(max(d, vec3(0, 0, 0)));
}

// http://www.iquilezles.org/www/articles/menger/menger.htm
float sdSponge(in vec3 p, float scale) {
p = p * scale;
float t = sdBox(p, vec3(1.));

float s = 1.0;
for( int m=0; m<3; m++ )
{
vec3 a = mod( p*s, 2.0 )-1.0;
s *= 3.0;
vec3 r = abs(1.0 - 3.0*abs(a));

float da = max(r.x,r.y);
float db = max(r.y,r.z);
float dc = max(r.z,r.x);
float c = (min(da,min(db,dc))-1.0)/s;

t = max(t,c);
}
return t/scale;
}

float sdRoundedBox(vec3 p, vec3 b, float r) {
return length(max(abs(p) - b, vec3(0, 0, 0))) - r;
}
Expand Down Expand Up @@ -81,65 +138,48 @@ float sdTorus88(vec3 p, vec2 t) {
return length8(q)-t.y;
}

/***************************** Geometry Combinators ***************************
* McGuire 11: http://graphics.cs.williams.edu/courses/cs371/f14/reading/implicit.pdf
*/

// set union of two geometry
float setunion(float d1, float d2) {
return min(d1, d2);
}

// intersection of two geometry
float intersect(float d1, float d2) {
return max(d1, d2);
}

// set subtraction of d2 from d1
float subtract(float d1, float d2) {
return max(d1, -d2);
}

// transformation OF P by applying the inverse transform
vec3 tr(vec3 p, vec3 translate) {
return p - translate;
}

/********************************** Raymarch **********************************
* McGuire: http://graphics.cs.williams.edu/courses/cs371/f14/reading/implicit.pdf
*/

float nearestIntersection(in vec3 p) {
#if SCENE1
float t = sdSphere(p, 0.5);
//t = setunion(t, sdPlane (tr(p, vec3(-2.0)), vec3(.0, 1.0, .0)) );
#if PLANE
t = setunion(t, sdPlane (tr(p, vec3(-2.0)), vec3(.0, 1.0, .0)) );
#endif
t = setunion(t, sdTorus (tr(p, vec3(-1.3,.8,-1.3)), .4, .15) );
t = setunion(t, sdTorus88 (tr(p, vec3(.0,.0,-1.5)), vec2(.35,.2)) );
t = setunion(t, sdCapsule (tr(p, vec3(.6, .0, .6)), vec3(1.0), vec3(.5), .2) );
t = setunion(t, sdRoundedBox(tr(p, vec3(-1.0, 1.0, .0)), vec3(.2), .1) );
t = setunion(t, sdCylinder (tr(p, vec3(1.2,.0,.0)), .5, .2) );
t = setunion(t, sdTriPrism (tr(p, vec3(.0,.0,1.5)), vec2(.4, .4)) );
t = setunion(t, sdSponge (tr(p, vec3(-.45,1, .85)), 4.0));
#endif

#if SCENE2
float t = sdCylinder(tr(p, vec3(-.1, .25, 0)), 1.5, .25);
//t = setunion(t, sdPlane (tr(p, vec3(-2.0)), vec3(.0, 1.0, .0)) );
t = subtract(t, sdTriPrism (tr(p, vec3(-.1,1,0)), vec2(1., 1.)) );

t = setunion(t, sdSphere (tr(p, vec3(1,1,0)), .2) );
t = setunion(t, sdTorus (tr(p, vec3(.4, 1,0)), .15, .1) );
t = setunion(t, sdTorus88 (tr(p, vec3(-.2, 1,0)), vec2(.16, .06)) );
t = setunion(t, sdRoundedBox(tr(p, vec3(-.75,1,0)), vec3(.08), .06) );
t = setunion(t, sdBox (tr(p, vec3(-1.15, 1,0)), vec3(.12)) );
t = subtract(t, sdTriPrism (tr(p, vec3(-.1,1,0)), vec2(1., 1.)) );

#if PLANE
t = setunion(t, sdPlane (tr(p, vec3(-2.0)), vec3(.0, 1.0, .0)) );
#endif

t = setunion(t, sdSphere (rot(p, 1.4 ), .2) );
t = setunion(t, sdTorus (rot(p, .7 ), .15, .1) );
t = setunion(t, sdTorus88 (rot(p, .1 ), vec2(.16, .06)) );
t = setunion(t, sdRoundedBox(rot(p, -.4 ), vec3(.08), .06) );
t = setunion(t, sdBox (rot(p, -.85 ), vec3(.12)) );
t = setunion(t, sdSponge (rot(p, -1.35), 8.0) );
#endif
return t;
}

// Naive iteration
vec2 raymarch_naive(in vec3 ro, in vec3 rd) {
const float tmax = 30.0;
const float tstep = 0.01;
int iters = 0;
for (float t = 0.1; t < tmax; t += tstep) {
for (float t = 0.1; t < TMAX; t += tstep) {
iters++;
vec3 p = ro + rd * t;
float intersection = nearestIntersection(p);
Expand All @@ -152,7 +192,6 @@ vec2 raymarch_naive(in vec3 ro, in vec3 rd) {

// Sphere tracing
vec2 raymarch_sphere(in vec3 ro, in vec3 rd) {
const float tmax = 30.0;
float t = 0.0;
for (int i = 0; i < 100; i++) {
vec3 p = ro + rd * t;
Expand All @@ -162,7 +201,7 @@ vec2 raymarch_sphere(in vec3 ro, in vec3 rd) {
} else if (intersection > 0.0) {
t += intersection;
}
if (t > tmax) {
if (t > TMAX) {
break;
}
}
Expand All @@ -173,7 +212,6 @@ vec2 raymarch_sphere(in vec3 ro, in vec3 rd) {
// http://erleuchtet.org/~cupe/permanent/enhanced_sphere_tracing.pdf
vec2 raymarch_overrelax(in vec3 ro, in vec3 rd) {
int iters = 0;
const float tmax = 30.0;
float t = 0.1;
float dt = 0.0;
float extrastep = 0.0;
Expand Down Expand Up @@ -215,7 +253,7 @@ vec2 raymarch_overrelax(in vec3 ro, in vec3 rd) {
} else if (intersection > 0.0) {
t += intersection;
}
if (t > tmax) {
if (t > TMAX) {
return vec2(-1,-1);
}
}
Expand Down Expand Up @@ -251,7 +289,6 @@ vec3 lambert(in vec3 p, in vec3 n, in vec3 c) {

// http://www.iquilezles.org/www/articles/rmshadows/rmshadows.htm
float shadowMarch(in vec3 ro, in vec3 rd) {
const float tmax = 10.0;
const float k = 8.0;

float shadow = 1.0;
Expand All @@ -261,12 +298,12 @@ float shadowMarch(in vec3 ro, in vec3 rd) {
vec3 p = ro + rd * t;
float intersection = nearestIntersection(p);
if (intersection > 0.0 && intersection < TMIN) {
return 0.0;
return .1;
} else if (intersection > 0.0) {
t += intersection;
shadow = min(shadow, k*intersection/t);
}
if (t > tmax) {
if (t > TMAX) {
break;
}
}
Expand All @@ -278,7 +315,7 @@ vec3 shadow(in vec3 p, in vec3 color) {
float lightdist = distance(p, light);

float shadow = shadowMarch(p, lightdir);
return clamp(shadow * color, 0.0, 1.0) + ambient;
return shadow * color + ambient;
}

// http://www.iquilezles.org/www/articles/terrainmarching/terrainmarching.htm
Expand All @@ -299,14 +336,13 @@ vec3 heightn(vec3 p) {
}

vec2 heightmarch(in vec3 ro, in vec3 rd) {
const float tmax = 30.0;
const float tstep = 0.01;
int iters = 0;
for (float t = 0.1; t < tmax; t += tstep) {
for (float t = 0.1; t < TMAX; t += tstep) {
iters++;
vec3 rayp = ro + rd * t;
float height = heightf(rayp.xz);

if (rayp.y < height) {
return vec2(t, iters);
}
Expand All @@ -318,31 +354,35 @@ vec3 render(in vec3 ro, in vec3 rd) {
vec2 ray = raymarch(ro, rd);
float d = ray.x;
vec3 p = ro + rd*d;
vec3 basecolor = color;

vec3 basecolor = color;

if (d < 0.0) {
#if HEIGHTMAP
ray = heightmarch(ro, rd);
d = ray.x;
if (ray.y < 0.0) {
return background;
}

vec3 rayp = ro + rd * d;
float y = heightf(p.xz);
p = vec3(rayp.x, y, rayp.z);
basecolor = background;
vec3 n = heightn(p);
vec3 c = lambert(p, n, ground);
vec3 orig = c;
#if SHADOW
c = shadow(p, c);
c = clamp(c, 0.0, 1.0);
#endif
return c;
#else
return background;
#endif
}

float iters = ray.y;

#if ITERS
return vec3(iters/20.);
#endif
Expand Down Expand Up @@ -370,7 +410,6 @@ vec3 render(in vec3 ro, in vec3 rd) {

#if SHADOW
c = shadow(p - 3.0*rd*EPSILON, c);
c = clamp(c, 0.0, 1.0);
#endif

return c;
Expand Down

0 comments on commit b915627

Please sign in to comment.