Go to bottom

Raymarching Beginners' Thread

category: code [glöplog]
On 704 i use a simple plane ray intersection to accelerate the raymarching. I think using simple raytracing for around complex raymarching objects is a nice way to accelerate raymarching.
Yes, definitely. Use bounding boxes/spheres wherever possible, trace those, then march. (Of course it's the last bit that's the most expensive to march, but it still gives good speedups!)

Not really 4k friendly though, as it's an extra step and an extra bunch of code. But, if you're going for speed, definitely. I've been tracing the ground plane and doing the same plane intersection for 'flat' objects too. I'd really like to move to pure raytrace though, with complex objects :)
added on the 2011-05-27 10:36:12 by psonice psonice
Let's talk about bad scenarios:
- you start a reflection ray - (+EPSILON) - marching has to accelerate first
- you start a refraction ray - (+EPSILON) - depending on the object this can be even worse than reflection.

How can we speed up/improve this? Can we do something with the gradient (let's pretend computing the gradient is a fast operation)? Something like dot(rayDir, grad) to modifiy the marching speed?

Is there a way to cut out things from the distance function in a way that the cutted out stuff pretends to be maxDist?

Maybe we could also "notice" that we are accelerating - and give it a little "kick".
added on the 2011-05-27 11:08:42 by las las
las: you can cut stuff out by replacing everything with a bounding box and raytracing it as you start the reflection. This way only the closest object will get marched. Problem is, the closest object is the object you just hit, which is what slows it down when it accelerates, but after that it'll help (I think it's best to use bounding box/spheres whenever possible, except when you're size optimising..)

There's a problem with speeding up the ray after a reflection/refraction though - if the surface has small details you can kick the ray past them. For a sphere, you don't need to include the sphere in the distance function at all after a reflection, for a perlin noise cloud you need small steps.. maybe store a 'kick' value per object somewhere?
added on the 2011-05-27 11:31:22 by psonice psonice
las: How about sampling a point some at a certain perpendicular distance to the surface you just hit, i.e.

d = distance_function( point_of_collision + C * normal_at_collision )

Then d/C is a measure of the smoothness of the surface at that point. If d/C is high, then the surface is either locally planar or locally convex, and it's safe to accelerate the reflected ray, at least unless the ray is almost parallel to the surface (but you should have the result of dot(ray, normal) left over from calculating the reflection anyway, so testing for that would be cheap.)

My thinking is that most rays would hit objects on locally smooth regions and the majority would strike at a steep angle.
added on the 2011-05-27 12:39:00 by doomdoom doomdoom
Hi everybody,

I tried playing reflection some days ago (long time before you start to talk it on thread).
It works for me but it seems there is something wrong and i dont know why.

Here is my code (simplified)
Code:ro = (0.0, 0.0, 1.0); rd = (x, y, -1.0); //should we normalize rd ? while(steps < 100) { p = ro + t * rd; d = f(p); if(d < 0.001) { //calculate normal and other stuff n = normalize( ... ); break; } t += d * 0.7; steps++; } //for reflection, shoot a new ray //origin = hit point //direction = direction of first ray but reflected with surface normal... ro = p; rd = normalize(reflect(rd, n)); if(ray_hit) { steps = 0; while(steps < 100) { if { ... raytrace again } t += d * 0.7; steps++; } }

The funny thing is first time i try it, i forget to reset "t" to zero before second raymarching... and it worked. to my point of view, it should be set to zero (we should start new ray at p and not p+d*big_value_of_t), but if i do that, it doesnt works and i dont know why.

also : is it really important to normalize direction vector before raymarching (for reflection or non reflection stuff ?). if we forget to do it , i think it will avance ray faster or slower than expected right ? (so maybe we will more steps than needed to hit surface? or it doest really change anything ?
added on the 2011-05-27 20:45:55 by Tigrou Tigrou
Maybe self intersections? Screenshot please.
And maybe a code one try directly ;)
Before marching a second time move the hitpoint along the normal at that point (just a little bit) to ensure that you are outside the surface.
Btw. if you put two normalized vectors into reflect - you don't have to renormalize again.
added on the 2011-05-27 21:34:50 by las las
added on the 2011-05-27 21:35:07 by las las
Before marching a second time move the hitpoint along the normal at that point (just a little bit) to ensure that you are outside the surface.

Yes, you are totally right. it was so obvious, but never comes to my mind. now it works perfectly and reflection seems to be more realistic. since its an array of cube even if "t" was exagerated it will hit something sooner or later

Here is a screen : (the yellow-blue colors and rounded corners are wanted)
BB Image

Now, time to try refraction...
added on the 2011-05-27 22:09:24 by Tigrou Tigrou
Yeah! nice. Go for it! :)
added on the 2011-05-27 22:13:47 by las las
Shiny! I had that same problem with the shadows in my raytrace code yesterday - if you don't move the ray out from the surface slightly, it intersects immediately and the object shadows itself. Not everywhere though, you get this funky 'noise' effect, with pixels being randomly shadowed.

Btw, your cubes are a bit weird. They're shaded + reflecting like rounded cubes, but have sharp corners! Is it a rounded cube function or just straight cube? If it's straight cube maybe the epsilon is too big somewhere. Although I think it looks quite cool :)
added on the 2011-05-27 23:33:05 by psonice psonice
psonice: maybe the normals for a rounded cube are faster to calculate than the rounded cube
added on the 2011-05-27 23:49:56 by xernobyl xernobyl
nice pic. the "reflections" look cool - really spectacular, even tho they not correct. faked well - I'd say. :)
added on the 2011-05-28 00:08:03 by yumeji yumeji
xernobyl: unless he's using some other technique, to get the normals we normally use do the exact same distance function we get to find the surface to find the normal. A high epsilon value when calculating the normal might have the effect of 'blurring' the normals though, like this - which is actually a very useful thing to know :)
added on the 2011-05-28 00:13:16 by psonice psonice
these are normal cubes, but like psonice said, the epsilon value for calculating normals is quite high. as a consequence the normals around the edges of the object are not really precise, which give this "rounded cube with sharp corners" effect. this can also give nice result when applying lighting (color = dot(n, vec3(...)).
added on the 2011-05-28 20:41:57 by Tigrou Tigrou
if you don't move the ray out from the surface slightly, it intersects immediately and the object shadows itself.

another way to cast shadows is to cast shadow rays not from the object hit point towards the light, but from the light towards the hitpoint. you also need the epsilon trick, but at least the shadow computations are faster (usually) cause you don't suffer the slow escape problem that you have when you march away from an object, where the distance field is small. you cannot apply the same trick for reflections, thought :(
added on the 2011-05-28 21:26:34 by iq iq
purely theoretical idea to speed up raymarching in scenes with many objects.
split raymarching into three parts:
1. raymarch other objects
2. raymarch myself
3. Compare t values

1. naturally allows larger delta steps, 2. can be terminated early in bounded cases as per psonice comments, and even if not, its much faster (as small steps exist but test only one object). Should work for refracted, reflected and shadow rays. Eye rays simply have a null step 2.

There are ways to do ONLY step 2 for refracted rays if your objects do not intersect geometrically.

But as I say theoretical because, well I never tried it, and likely step 1 has an if in the inner loop...ugh
added on the 2011-05-30 07:26:20 by auld auld
For convex objects the zeno paper states some nice properties!
added on the 2011-05-30 12:13:03 by las las
auld: what i do is a simple raytrace step for simple objects (planes, spheres etc) or bounding boxes, then take the minimum value of that. Then I march the other objects, and just include the value from the raytrace step in the distance function.

Problem is like you say though, how to switch from bounding box to full march without an if in the loop and such. One way is to bail out of the loop when you hit the edge of a bounding box, then start a new loop with a full march. Maybe there's a better way?
added on the 2011-05-30 12:40:57 by psonice psonice
sorry yeah, I should have said, for speeding up reflections and refractions as per las question. The idea is to accelerate away from surfaces the ray is originating on faster. In this case psonice, if you have a bounding sphere or box, for the object you have just intersected, then step 2 can use early termination on iteration once the ray distance to the bounding object is about 0.

added on the 2011-05-30 15:56:48 by auld auld
psonice: render the bounding box as geometry and raymarch with a shader just that object; use z write from pixelshader to handle intersections and clip (discard) to take care of the pixels that dont really hit the object.
added on the 2011-05-30 16:30:17 by smash smash
smash : is it fast, when I tried it (ok it was x1950 days) the z write made the thing run like a dog - is that fixed now?
added on the 2011-05-30 16:38:36 by auld auld
auld: the reason is i think - the older gpus relied on low granularity hierarchical z / tile-based zcull and only had per-pixel ztest after the pixelshader executed; writing z from pixelshader broke early out from z cull tests because it didnt work with zcull properly - and it killed the performance.
the newer gpus (nv 8xxx and later) added a 2nd z unit before the pixelshader - so per-pixel z is tested before pixelshader executes. i.e. a failed test prevents the pixelshader from executing. (same reason stencil optimisations work better now, too.)

i.e. it's still not as fast as z testing geometry because it probably doesnt work properly with zcull, but its still faster than a load of branching in long pixelshaders.
added on the 2011-05-30 17:01:06 by smash smash
smash: what's early z gotta do with bounding box rendering. the box gets rendered all the time and the zvalue is tested after pixelshader. per pixel or fragment doesn't even matter. cause it gets written always. afaik.
added on the 2011-05-30 17:13:31 by yumeji yumeji
yumeji: except for fully opaque fragments tested. ofc. forgot that. -_- that's just like regular rendering.
added on the 2011-05-30 17:24:25 by yumeji yumeji


Go to top