Raymarch Simple Question
category: code [glöplog]
Each step I can either
(1) Add abs(dist) (or half dist, or some other fraction of it) to the current recorded distance.
Or
(2) I can take dist<0 to mean I have overshot the surface, and commence a bisection to find dist==0.
Then the problem with (1) is that you can overshoot surfaces, resulting in patches, especially when the surfaces are thin. This is what I tried to overcome with (2).
But the problem with (2) is that when the ray should not actually hit the object at all, so that you'd fall through to rendering the background (sky or something), it gets stuck around ray.z=object_center.z and you end up with this interesting, but undesirable, blur effect.
Is there a (3)... a (4)... ? How do I overcome this?
(1) Patches in the object due to overshooting:
(2) Getting stuck with ray.z is around the center of the object (backside of the object is missing!)
These screenshots are from my shader toy here: https://www.shadertoy.com/view/MsjSDm
(1) Add abs(dist) (or half dist, or some other fraction of it) to the current recorded distance.
Or
(2) I can take dist<0 to mean I have overshot the surface, and commence a bisection to find dist==0.
Then the problem with (1) is that you can overshoot surfaces, resulting in patches, especially when the surfaces are thin. This is what I tried to overcome with (2).
But the problem with (2) is that when the ray should not actually hit the object at all, so that you'd fall through to rendering the background (sky or something), it gets stuck around ray.z=object_center.z and you end up with this interesting, but undesirable, blur effect.
Is there a (3)... a (4)... ? How do I overcome this?
(1) Patches in the object due to overshooting:
(2) Getting stuck with ray.z is around the center of the object (backside of the object is missing!)
These screenshots are from my shader toy here: https://www.shadertoy.com/view/MsjSDm
You are assuming that the distance function is returning the distance but how you implemented it its actually not...
The distance function can't change more than one unit of it's sampled at two points which are one unit away.
In other words if the distance function returns 2.0, you move the ray forward by 2.0 units and then if your distance function returns -0.4 then something went wrong.
This happens when you stretch the space.
If distance function is x*2.0 - 4.0 it's obvious that the distance function. For space stretching to work you need to normalize the derivative to 1.
For the above example you need to divide by two.
(x*2.0 - 4.0) / 2.0 is the proper distance function. You can also rewrite it as x - 2.0
Another not so obvious example:
x+sin(y)*4.0
To properly fix it you need to implement distance from sine function, or use the lazy mode and divide by 4.
x*.25 + sin(y) becomes a better aproximation of the distance function if x is small otherwise x - 1.0 is a better aproximation if x is big.
Try to implement a generic stretchable cube. That should make you understand this concept.
This is your shader with the distance function divided by 10 and iterations increased to 200
The distance function can't change more than one unit of it's sampled at two points which are one unit away.
In other words if the distance function returns 2.0, you move the ray forward by 2.0 units and then if your distance function returns -0.4 then something went wrong.
This happens when you stretch the space.
If distance function is x*2.0 - 4.0 it's obvious that the distance function. For space stretching to work you need to normalize the derivative to 1.
For the above example you need to divide by two.
(x*2.0 - 4.0) / 2.0 is the proper distance function. You can also rewrite it as x - 2.0
Another not so obvious example:
x+sin(y)*4.0
To properly fix it you need to implement distance from sine function, or use the lazy mode and divide by 4.
x*.25 + sin(y) becomes a better aproximation of the distance function if x is small otherwise x - 1.0 is a better aproximation if x is big.
Try to implement a generic stretchable cube. That should make you understand this concept.
This is your shader with the distance function divided by 10 and iterations increased to 200
FWIW, your first picture looks a lot more interesting than most raymarchers that work well.
You have a bug in the cartesian to polar coordinate transform (the second atan). Also, for the marcher, maybe you can try this:
Code:
vec2 dist(vec3 pos) {
vec3 center = vec3(0.0, 0.0, 3.0);
float d = 0.0;
float g = 0.0;
for (int i = 0; i < 1; ++i) {
vec3 delta = pos - center;
float theta = iGlobalTime * 0.1 + atan(delta.x, delta.z);
float phi = iGlobalTime * 0.1 + acos(delta.y/length(delta));
g = 0.5 + 0.5 * sin(theta*10.0) * sin(phi*10.0);
g = pow(g, 4.0);
float rad = 0.5 + 0.5* g;
d = length(delta) - rad;
}
return vec2(d, g);
}
void main(void) {
vec2 uv = (gl_FragCoord.xy + 0.5) / iResolution.xy * 2.0 - 1.0;
uv.y *= iResolution.y / iResolution.x;
vec3 ray = normalize(vec3(uv, 1.0));
float t = 0.0;
float g = 0.0;
float hit = 0.0;
for (int i = 0; i <256; ++i)
{
vec3 pos = ray * t;
vec2 d = dist(pos);
if (d.x < 0.001)
{
hit = 1.0;
g = d.y;
break;
}
g = d.y;
t += d.x*0.1;
}
vec3 col = vec3(0.0);
if( hit>0.5 )
{
float diff = 1.0 - 1.0 / (1.0 + g * 10.0);
float fog = 1.0 / (1.0 + t * 0.1);
col = diff * vec3(fog, fog, fog);
}
gl_FragColor = vec4(col,1.0);
}
Wow thanks guys it's working much better now!
looks nice :)
I actually like the way the first "buggy" screenshot looks like.
What Preacher and kbi said :)
Sure the broken image it looks interesting, but don't mislead him.
The problem with bug-art is that most of the times you don't understand it (as clearly proven by the questions he made). And if you don't understand it you cannot control it. And I'm not going to ask if it is legitimate art if you don't direct it.
I think it's better he controls the image to be what he wants (especially if he's a beginner), and only afterwards can he "break" the rules and introduce bugs to make it artsy... Sticking to the bug at this stage because it looks cool won't led him too far.
But sure, that noisy image is more interesting than the perfect octopus, it's more fun for your brain to try to interpret. Plus of course, noise/broken stuff is always "cool". And easier.
The problem with bug-art is that most of the times you don't understand it (as clearly proven by the questions he made). And if you don't understand it you cannot control it. And I'm not going to ask if it is legitimate art if you don't direct it.
I think it's better he controls the image to be what he wants (especially if he's a beginner), and only afterwards can he "break" the rules and introduce bugs to make it artsy... Sticking to the bug at this stage because it looks cool won't led him too far.
But sure, that noisy image is more interesting than the perfect octopus, it's more fun for your brain to try to interpret. Plus of course, noise/broken stuff is always "cool". And easier.
btw, if you're using a signed distance field, if you overshoot the distance is negative. Drop that abs() and the next step will be backwards to the surface, not further inside the object :)
Thanks psonice, but it seems that with long extrusions this doesn't behave so well.
What iq suggests is to actually terminate the trace if the distance becomes negative, since this indicates surface penetration.
After the trace I tried a second refinement step by bisection to try to find dist=0 exactly, but this did not fix any of the artifacts.
I'm now wondering how to render long thin tubes with this technique to give better tentacles. Perhaps I'd get better results from stretching a cylinder and make it go to a point at one end? Thinking of something like the legs in slisesix.
What iq suggests is to actually terminate the trace if the distance becomes negative, since this indicates surface penetration.
Code:
for (int i = 0; i < 64; ++i) {
vec3 pos = ray * t;
vec2 d = dist(pos);
if (d.x<0.1) {
g = d.y;
hit = 1.0;
break;
}
g = d.y;
t += d.x * 0.1;
}
After the trace I tried a second refinement step by bisection to try to find dist=0 exactly, but this did not fix any of the artifacts.
I'm now wondering how to render long thin tubes with this technique to give better tentacles. Perhaps I'd get better results from stretching a cylinder and make it go to a point at one end? Thinking of something like the legs in slisesix.
I like iq's screenshot the most.
dila, better (and faster) than a capped dylinder, you can use the distance to a segment:
where p is your point in space, a and b are the end points of the segment/capsule, and r is it's thickness. You can see that being used here and here
Code:
float sdSegment( vec3 p, vec3 a, vec3 b, float r )
{
vec3 pa = p - a;
vec3 ba = b - a;
float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1. );
return length( pa - ba*h ) - r;
}
where p is your point in space, a and b are the end points of the segment/capsule, and r is it's thickness. You can see that being used here and here
Something like this:
Code:
float sdSegment( vec3 p, vec3 a, vec3 b, float r )
{
vec3 pa = p - a;
vec3 ba = b - a;
float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1. );
return length( pa - ba*h ) - r;
}
vec2 dist(vec3 pos) {
vec3 center = vec3(0.0, 0.0, 4.0);
vec3 delta = pos - center;
float rad = length(delta);
float the = atan(delta.x,delta.z)+3.1416 + 0.1*iGlobalTime;
float phi = acos(delta.y/rad) + 0.1*iGlobalTime;
the = mod( the/6.2831, 0.04 ) - 0.02;
phi = mod( phi/6.2831, 0.04 ) - 0.02;
float d = sdSegment( vec3(the,phi,rad), vec3(0.0), vec3(0.0,0.0,1.5), 0.01 );
return vec2(d, rad / 1.5);
}
void main( void )
{
vec2 uv = (-iResolution.xy+2.0*gl_FragCoord.xy)/iResolution.y;
vec3 ray = normalize( vec3(uv, 2.0) );
float t = 0.0;
float g = 0.0;
float hit = 0.0;
for (int i = 0; i < 256; ++i)
{
vec3 pos = ray * t;
vec2 d = dist(pos);
if (d.x<0.001)
{
g = d.y;
hit = 1.0;
break;
}
g = d.y;
t += d.x;
}
float diff = g;
float fog = 1.0 / (1.0 + t * 0.1);
vec3 col = hit * diff * vec3(fog, fog, fog);
gl_FragColor = vec4(col,1.0);
}
Thanks iq, that works perfectly:
Awesome.
I couldn't resist playing with the idea myself. The mod() trick to create many arms out of one single actual tube is not working very well on the poles of the sphere (you can see lots of noise there). Well. At least it's cheap.
Realtime version: https://www.shadertoy.com/view/XdBSzG
I couldn't resist playing with the idea myself. The mod() trick to create many arms out of one single actual tube is not working very well on the poles of the sphere (you can see lots of noise there). Well. At least it's cheap.
Realtime version: https://www.shadertoy.com/view/XdBSzG
"The problem with bug-art is that most of the times you don't understand it (as clearly proven by the questions he made). And if you don't understand it you cannot control it. And I'm not going to ask if it is legitimate art if you don't direct it."
Sometimes the fact that you cannot control it is a lot more interesting than the fact that you can. Chance and chaos can yield many interesting results.
Sometimes the fact that you cannot control it is a lot more interesting than the fact that you can. Chance and chaos can yield many interesting results.
My attempt at the same thing:
(Realtime version: https://www.shadertoy.com/view/MsjSDm)
I can't help but notice it looks really bad next to iq's creation. But it's midnight and i need to sleep :)
I can't help but notice it looks really bad next to iq's creation. But it's midnight and i need to sleep :)
Added shadows.
i hope this will be part of your next demo/intro :)
Hmm not sure about that :D
If only there were more bytes in 256b!
If only there were more bytes in 256b!
Has anyone seen this kind of artifact before when implementing soft shadows?
I've been stuck here for a few hours. Can't figure out what I'm doing wrong:
I've been stuck here for a few hours. Can't figure out what I'm doing wrong:
there are more bytes in 512b :D
Looks like regular floating point errors that can easily be fixed by adding a small bias when sampling the shadow map (or the whatever you're getting your pixel-light distance from)