Raymarching Beginners' Thread
category: code [glöplog]
Quick video: http://www.youtube.com/watch?v=vCJHZzUpGIU. I'm getting my head round creating complex geometry. And I improved my light + shadow code a bit more.
Some questions:
- I'm repeating geometry with mod(). But say I have a cube at (0, 0, 0, 0.5), and use cube(cubePos, mod(rayPos, 1.0)). The repeat happens in the middle of the cube, which totally breaks it and results in horrible noise instead of cubes. Is there a way round that, or is it a bug at my end? (It works fine if the cube is totally within the repeating area).
- Is it better to calculate the ray intersection for some objects and store them? Then during the marching, you can just compare this stored result with the distance function of other objects.. could be a good optimisation?
Some questions:
- I'm repeating geometry with mod(). But say I have a cube at (0, 0, 0, 0.5), and use cube(cubePos, mod(rayPos, 1.0)). The repeat happens in the middle of the cube, which totally breaks it and results in horrible noise instead of cubes. Is there a way round that, or is it a bug at my end? (It works fine if the cube is totally within the repeating area).
- Is it better to calculate the ray intersection for some objects and store them? Then during the marching, you can just compare this stored result with the distance function of other objects.. could be a good optimisation?
Quote:
- I'm repeating geometry with mod(). But say I have a cube at (0, 0, 0, 0.5), and use cube(cubePos, mod(rayPos, 1.0)). The repeat happens in the middle of the cube, which totally breaks it and results in horrible noise instead of cubes. Is there a way round that, or is it a bug at my end? (It works fine if the cube is totally within the repeating area).
It's just normal :) - objects to be repeated have to be totally inside the repeating area - everything outside that area is just "cut off" - resulting in ugly open geometry.
Quote:
Is it better to calculate the ray intersection for some objects and store them? Then during the marching, you can just compare this stored result with the distance function of other objects.. could be a good optimisation?
--verbose
What I mean is, for some objects calculating the intersection of the ray and surface might be easy. Why not separate out these objects, and find the minimum distance to them in one step right at the start? Then in the main raymarch loop, you can remove all these objects and replace them with the pre-calculated distance, making the loop faster.
minimum distance to what? ;)
You are walking along a ray, so your position changes - also the distance to the closest object changes... and so on. What you can do is texel's optimisation technique.
You are walking along a ray, so your position changes - also the distance to the closest object changes... and so on. What you can do is texel's optimisation technique.
You walk along the ray until it hits something (or gets close anyway). I think you can use that to speed things up - kind of a mix of raytrace + raymarch.
Take as an example, you have a scene with just a ground plane in it, the camera is parallel to the ground. That's kind of 'worst case' for the raymarcher, you need a lot of steps and accuracy is poor. But it's trivial to calculate the point where the ray hits the plane, in 1 step.
So, bit of pseudo-code:
The distance function no longer includes the plane at all. The plane doesn't affect the ray march, potentially speeding it up, and the plane would be more accurate. A few objects like that in the scene could be a decent performance + accuracy win.
I go try it..
Take as an example, you have a scene with just a ground plane in it, the camera is parallel to the ground. That's kind of 'worst case' for the raymarcher, you need a lot of steps and accuracy is poor. But it's trivial to calculate the point where the ray hits the plane, in 1 step.
So, bit of pseudo-code:
Code:
float planeDist = distanceToPlane(pos);
for(...){
l = min( f(pos), planeDist);
if(l < threshold){
break;
}
pos += (l * dir * .25);
planeDist -= (l * dir * .25);
}
The distance function no longer includes the plane at all. The plane doesn't affect the ray march, potentially speeding it up, and the plane would be more accurate. A few objects like that in the scene could be a decent performance + accuracy win.
I go try it..
Yep, it works well. Took a simple test scene, a sphere resting on a plane.
Getting distance to plane and sphere in the distance function: 16fps.
Pre-calcing distance to plane, sphere done in distance function: 25fps.
50%+ faster in this case, with better accuracy. The sphere could be pre-calced too, effectively making it a raytracer that can be mixed with the distance field. There's definitely a good case for doing this :)
What I need next is a bunch of calculations for determining the length of a line from a point to a sphere, cube, .... Just doing the plane was stretching my maths a bit.
Then there's intersections with repeating objects, distortion, etc... which are possible or better marched?
Getting distance to plane and sphere in the distance function: 16fps.
Pre-calcing distance to plane, sphere done in distance function: 25fps.
50%+ faster in this case, with better accuracy. The sphere could be pre-calced too, effectively making it a raytracer that can be mixed with the distance field. There's definitely a good case for doing this :)
What I need next is a bunch of calculations for determining the length of a line from a point to a sphere, cube, .... Just doing the plane was stretching my maths a bit.
Then there's intersections with repeating objects, distortion, etc... which are possible or better marched?
Calculating the distance to a plane is damn cheap anyways... doing that outside the distance function + updating all those "speed up" distances will introduce some overhead (planeDist will be stored somewhere right? I guess you will waste GPU registers that way). And I guess updating the planeDist for example will cost more that just doing the very very cheap dot product in the field function.
Ohh... I don't think this will speed up the marching in any way - because planeDist will be the same distance to the plane as if you would calculate the distance to the plane in the field function, right?
Code:
l = min( f(pos), planeDist);
Ohh... I don't think this will speed up the marching in any way - because planeDist will be the same distance to the plane as if you would calculate the distance to the plane in the field function, right?
It seems to work for some strange reason?
Code please :D
Code please :D
Code:
// length of ray from position to plane, plane is at y=0. p = cam position, d = ray dir
float tracePlane(vec3 p, vec3 d){
return length(d* p.y / d.y);
}
// distance function, maxD = dist to plane
float f(vec3 p, float maxD){
return min(
maxD,
sphere(sphere1, p)
);
}
// march function
vec3 rm(vec3 pos, vec3 dir, float threshold, float maxSteps){
vec3 startPos = pos;
float l, i;
float gd = tracePlane(pos, dir); // distance to ground plane
for(i=0.; i<1.; i+=1.0/maxSteps){
l = f(pos, gd);
if(l < threshold){
break;
}
pos += (l * dir * .25);
gd -= length(l * dir * .25);
}
return vec3(i, length(startPos - pos), l);
}
For more complex cases, lack of registers or whatever might make it slower, but it seems to work here :)
I guess the key point here is: if the ray is travelling parallel (or anywhere close) to the plane, the distance is identical each step. You will use all iterations before touching it, and if the ray is close the distance travelled will be very small. This is really bad, slow and inaccurate.
With that code, the ray step is based only on the sphere, it can travel very far in few steps, and it can hit the plane earlier.
It's not going to work in all cases I think, but it should help quite often.
With that code, the ray step is based only on the sphere, it can travel very far in few steps, and it can hit the plane earlier.
It's not going to work in all cases I think, but it should help quite often.
Code:
// expensive
gd -= length(l * dir * .25);
// cheap - dir should be unit length
gd -= l * .25;
Please try to compare the speed with something like this:
Code:
float f(vec3 p){
// p.y > 0 -- above the plane, p.y <0 below the plane... untested!
return min(
p.y,
sphere(sphere1, p)
);
}
vec3 rm(vec3 pos, vec3 dir, float threshold, float maxSteps){
vec3 startPos = pos;
float l, i;
for(i=0.; i<1.; i+=1.0/maxSteps){
l = f(pos);
if(l < threshold){
break;
}
pos += (l * dir * .25);
}
return vec3(i, length(startPos - pos), l);
}
I forgot to compare that before re-writing. Oops :) But I think this method holds huge promise for speed. I'm now trying to raytrace as much as possible so no loops are required, then march the parts that do need it. The raytracing is *MUCH* faster where it can be used.
As a quick test, I have a sphere intersecting a plane (i.e. a hole in the ground) without shadows etc., just plain lighting. On my lowly radeon 2600, it's doing ~50fps at 720p res. Precision is perfect.
Raymarching the same scene, same res, with 50 iterations: 1.7fps, with a few speckles around edges.
That's a mere 2500% increase in speed, plus an increase in quality :D The challenge now is to figure out raytracing with more objects, domain repetition etc. Hmm.. and shadows. I'd like to keep my soft shadows, but they need raymarching. Maybe just raymarch the shadows, with a lower step count and lower res?
As a quick test, I have a sphere intersecting a plane (i.e. a hole in the ground) without shadows etc., just plain lighting. On my lowly radeon 2600, it's doing ~50fps at 720p res. Precision is perfect.
Raymarching the same scene, same res, with 50 iterations: 1.7fps, with a few speckles around edges.
That's a mere 2500% increase in speed, plus an increase in quality :D The challenge now is to figure out raytracing with more objects, domain repetition etc. Hmm.. and shadows. I'd like to keep my soft shadows, but they need raymarching. Maybe just raymarch the shadows, with a lower step count and lower res?
Repeating spheres intersecting a plane. Not too exciting, but proof it's possible. 40-50fps at 720p, on a radeon 2600.
nice job psonice, i demand code :D... btw. one of the bad things... your technique does not give you iteration glow for free, right? :)
You can still have iteration glow for free, so long as the iteration count is 1 ;D On the other side, this is so much more accurate that you don't need to hide stuff in the distance :D
The basic method is the same, get distance to all objects, take minimum (or do some max(a, -b) type tricks for intersections etc.). You do it in 1 step though, no iterations. You can mix it with raymarching like in my code above. Stuff I've figured out so far:
None of that is guaranteed bug free, never mind optimised ;) Consider it more of a proof that it works. The distance function I used above is:
Fixes + optimisations welcome :)
The basic method is the same, get distance to all objects, take minimum (or do some max(a, -b) type tricks for intersections etc.). You do it in 1 step though, no iterations. You can mix it with raymarching like in my code above. Stuff I've figured out so far:
Code:
// distance to 'ground plane' (at y = 0), p = cam position, d = ray direction
float tracePlane(vec3 p, vec3 d){
return sign(p.y) + sign(d.y) == 0. ? length(d* p.y / d.y): maxD;
}
// distance to sphere, s = sphere pos + size, p = cam pos, d = ray dir, inv = invert sphere (using -sphere(..) in intersections doesn't work)
float traceSphere(vec4 s, vec3 p, vec3 d, float inv){
float distToS = length(p - s.xyz);
float rayToSphere = length(p + (d * distToS) - s.xyz);
float t = rayToSphere / s.w;
float distToSurf = distToS - (sqrt(1. -(t * t))* s.w * inv);
return rayToSphere < s.w ? distToSurf : inv * maxD;
}
// hacky sphere with domain repetition on xz
float traceRepeatedSphere(vec4 s, vec3 p, vec3 d, float inv){
float distToS = tracePlane(p,d);//length(p - s.xyz);
vec3 ray = p + (d * distToS);
float rayToSphere = length(p + (d * distToS) - s.xyz);
float rayToSphereF = length(vec3(mod(ray.x, 1.0), ray.y, mod(ray.z, 1.0)) - s.xyz);
float t = rayToSphereF / s.w;
float distToSurf = distToS - (sqrt(1. -(t * t))* s.w * inv);
return rayToSphereF < s.w ? distToSurf : inv * maxD;
}
None of that is guaranteed bug free, never mind optimised ;) Consider it more of a proof that it works. The distance function I used above is:
Code:
float gd = tracePlane(pos, dir);
float sd = traceRepeatedSphere(sphere1, pos, dir, -1.);
float dist = max(gd, sd);
Fixes + optimisations welcome :)
Ugly little thing.
Too tired for opt. But you should have a look at the step and the mix function once again :)
Too tired for opt. But you should have a look at the step and the mix function once again :)
lol. this something I should be able to do with some sphered mesh VS math transformation and a PS doing a fake ambient bump texture.
hope you get some more out of this @las
hope you get some more out of this @las
yumeji: a lot of what we're showing here is pretty trivial, but it's 'proof of concept' stuff. An intersecting cube + sphere might be trivial, but cdak was made with little more than that :)
I would like to see his implementation using a rasterized sphere ;) - I bet it will be far less elegant than the 3 lines of code for deforming a sphere + we don't need a fake ambient bump texture ;)
Yumeji: but do you really think we show you everything? :D
Yumeji: but do you really think we show you everything? :D
I don't get how you'd do it with a rasterised sphere too. It would be fairly trivial with a bump map though, yes. But how do you make cdak with bump maps and vertex shaders?
I'm not gonna try to redo cdak. And offcourse it wouldn't need bumpmapping, rather than tons of vertices and more of all sorta geometry deformation.
anyway... just do your things guys. ;)
anyway... just do your things guys. ;)
Quote:
tons of vertices and more of all sorta geometry deformation
- HA HA. ;)
He's probably talking about displacement mapping. And talking about vertices has anyone tried doing a low res marching cubes "bounding box" surrounding the intended geometry? That would spare a few steps in the marching. I know the first steps are the faster ones and the ones closer to the geometry are slower and it would make the whole thing virtually undoable on 4kbs.
Maybe using a cube of points as a mesh, triangulating it on a geometry shader, and the proceeding as usual in the fragment shader using distance information estimated on the geometry shader?
psonice: hate to point this out but the whole point of using raymarching distance fields is for making complex things you cant raytrace analytically easily. so that optimisation should get useless pretty soon when you start making some proper scenes