Raymarching Toolbox Thread
category: code [glöplog]
I applaud.
Thanks Mercury!
Amazing stuff! Thankyou.
Just by cursory glance, is it nvidia only? It's full of invalid glsl statements (like float x; return x<0?-1:1).
Let me rephrase it before someone thinks im flaming mercury (thanks for releasing it!): although implicit type conversion is not invalid from version 120, wouldn't it make sense to change the literals thus making it more compatible? (like with opengl es and webgl)
Great! Thank you a lot for sharing this. There are some nice things in there.
reptile: here it compiles (and works) without warnings on latest nvidia, amd, and the khronos reference compiler (glslang). which #version string are you using? maybe we should state a minimum #version in the faq, sorry. 430 should be enough.
(I hate typing 1.0 instead of 1 so much I always give up on shadertoy after 5.0 minutes)
Oh, apparently I did miss there is a separate thread;)
So again,
here quick ST port (not fully tested) of the lib.
Also its not ShaderToy problem - its just passing stuff directly to WebGL shader translator that obviously sucks (and has more issues, i.e.couldn't make multi-line defines working as well?!).
So again,
here quick ST port (not fully tested) of the lib.
Also its not ShaderToy problem - its just passing stuff directly to WebGL shader translator that obviously sucks (and has more issues, i.e.couldn't make multi-line defines working as well?!).
Yes, angle is a real problem. Having your shader go though yet another layer of compilation makes debugging way harder when strange things happen. To be fair, the HLSL compiler are probably better than their GLSL counterparts. Still, every added layer just makes for more guesswork about what's really going on and when you hit the driver weirdness one layer is bad enough.
tomkh, thanks for the port! Might I suggest naming the last function fField instead of dField to keep the lib's (and the papers') naming convention? I don't really care about what letter we use as long as it is consistent. I'm just so tired of shadertoy's "map()" functions...
tomkh, thanks for the port! Might I suggest naming the last function fField instead of dField to keep the lib's (and the papers') naming convention? I don't really care about what letter we use as long as it is consistent. I'm just so tired of shadertoy's "map()" functions...
cupe: <3<3<3<3
thanks!
very interesting read so far..
very interesting read so far..
cupe: naming convention fixed.
I have also rewritten GDF platonic primitives to fully unrolled macro version as constant array initialization is not working in WebGL/ES 2.0. I just hope GLSL compiler can optimize constant expressions with function calls, i.e. normalize(vec3(0,1.,PHI+1.)).
I have also rewritten GDF platonic primitives to fully unrolled macro version as constant array initialization is not working in WebGL/ES 2.0. I just hope GLSL compiler can optimize constant expressions with function calls, i.e. normalize(vec3(0,1.,PHI+1.)).
cupe: fantastic initiative, great presentation!
Was asked for it, so I got over myself and made a shadertoy of the scale-invariant distance meter thingy we use for debugging distance fields: https://www.shadertoy.com/view/ldK3zD
Hey! no post here for 2 years
Comparative screenshots from one of those days with/without the prepass optimization. Red: primary ray iteration count, Green: shadow ray iteration count. 1.0 = 50 iterations.
(scene mask bits for this scene are ground, trees, houses and skis)
And a shadertoy showing how it's one.
The main observation is the use of sufficently negative sdf values to say that the whole cone/beam is now inside the object, meaning we have total occlusion / a maximum trace length for the cone.
That means that our tile optimization is no longer limited to primary rays but can do even better for the shadows.
Comparative screenshots from one of those days with/without the prepass optimization. Red: primary ray iteration count, Green: shadow ray iteration count. 1.0 = 50 iterations.
(scene mask bits for this scene are ground, trees, houses and skis)
And a shadertoy showing how it's one.
The main observation is the use of sufficently negative sdf values to say that the whole cone/beam is now inside the object, meaning we have total occlusion / a maximum trace length for the cone.
That means that our tile optimization is no longer limited to primary rays but can do even better for the shadows.
Psycho: awesome stuff.
I stumble upon the similar problem with my 8k, where I wanted to place around hundreds of objects in arbitrary positions. My solution was to raymarch inside bounding boxes, but it caused quite a bit of slowdown when many objects were overlapping (you have probable seen this write up).
In your method, I can see you are storing a bit mask for 8x8 screen-space regions, where every bit encodes in fact a class of objects; not really an individual object, as you would quickly ran out of bits ;)
Using this you obviously have to use other tricks to place individual objects. But I see you have found simple work-around for that, e.g. tree positions reveal usual grid+hash pattern :) I guess you also have to lookup terrain texture to find individual object elevation?
Anyway, big thanks for sharing this. I will surely play around with this technique!
I stumble upon the similar problem with my 8k, where I wanted to place around hundreds of objects in arbitrary positions. My solution was to raymarch inside bounding boxes, but it caused quite a bit of slowdown when many objects were overlapping (you have probable seen this write up).
In your method, I can see you are storing a bit mask for 8x8 screen-space regions, where every bit encodes in fact a class of objects; not really an individual object, as you would quickly ran out of bits ;)
Using this you obviously have to use other tricks to place individual objects. But I see you have found simple work-around for that, e.g. tree positions reveal usual grid+hash pattern :) I guess you also have to lookup terrain texture to find individual object elevation?
Anyway, big thanks for sharing this. I will surely play around with this technique!
yes, part of the fixes for the new version of the intro is also looking up ground gradient (it's in the ground texture) for the tree center positions - trees can't grow where it's too step, but that shouldn't be determined pr evaluation position as it was ;)
For doing less obvious grids I would usually layer several grids on top of each other to get possible cell overlap and thus much better variation. Marching through grids is also quite slow (you'll never have a decent distance) and there the coarse depth + masking helps a lot. I would definitely have done your rocks as layered grids.
For doing many objects of the same kind I did a singlepass compute shader solution in traskogen where each pixel in a threadgroup test against primitive and then they syncronize and share their findings before each marches the primitives touching the tile.
For doing less obvious grids I would usually layer several grids on top of each other to get possible cell overlap and thus much better variation. Marching through grids is also quite slow (you'll never have a decent distance) and there the coarse depth + masking helps a lot. I would definitely have done your rocks as layered grids.
For doing many objects of the same kind I did a singlepass compute shader solution in traskogen where each pixel in a threadgroup test against primitive and then they syncronize and share their findings before each marches the primitives touching the tile.
Since these functions of mine have already been adopted, used and spread by a certain group of people I figured I might just as well post them here:
Octahedron:
Note that s is the distance of the faces from the center point, so with s=1 the vertices of the octahedron will be at sqrt(2), the epsilon value(1e-6) is there to combat artifacts for GPUs whose sign function returns a zero vector for zero vector inputs.
Chamfered box:
Enjoy!
Octahedron:
Code:
float so(vec3 p,float s) {
return dot(p,normalize(sign(p+1e-6)))-s;
}
Note that s is the distance of the faces from the center point, so with s=1 the vertices of the octahedron will be at sqrt(2), the epsilon value(1e-6) is there to combat artifacts for GPUs whose sign function returns a zero vector for zero vector inputs.
Chamfered box:
Code:
float sbc(vec3 p,vec3 d,float c) {
p=max(abs(p)-d,0.);
return so(p,c);
}
Enjoy!
Neat. About that epsilon, shouldn't it maybe be
Code:
instead? That would avoid the corner case where p == vec3(1e-6). I tend to define my own sgn(x) functions to avoid the zero-case (which is specified in the respective APIs and should behave "bad" across all recent GPU/Driver combinations. If you happen to have something that doesn't do sign(0) = 0 - their implementation probably doesn't adhere to the specification).sign(p)+1e-6
Typo, the corner case is obviously p == -vec3(1e-6).
Despite it being specced to behave like that I only experienced it on Intel cards so far, maybe thats due to precision though, since thinking about it I can see people relying on the (imho pretty inconvenient) zero case. I agree that moving the epsilon out is cleaner, that being said having it in the sign already does "the trick" for me, probably due to my "standard" projection setup and marching epsilon not getting close enough.
Damn, i didn´t even know shadercode acccepts exponents like "1e-6", i still use "0.000001" instead.
Having just admitted i am a lamer, i do wonder if my standard-way of doing it ain´t the better one, considering we want the cruncher to have some yummy repetitions! ;) 4 completely different asciis vs. 3 (very often used across the complete shader) different asciis. maybe i´ll test this later...
Having just admitted i am a lamer, i do wonder if my standard-way of doing it ain´t the better one, considering we want the cruncher to have some yummy repetitions! ;) 4 completely different asciis vs. 3 (very often used across the complete shader) different asciis. maybe i´ll test this later...
well first of all you could drop that leading zero, then I think once you're at the stage where its 1e-6 vs .000001 you might just as well begin to move stuff around hoping that it gives you that one byte more or less :)
Sure, you are right! The same i keep telling people myself! (shifting random lines to gain many bytes by chance/luck)
But then i find myself using only numbers like 999. or .777 all over my introcodes again...i guess i had some luck finding missing bytes that way in the past and now i can´t stop looking for minimal byte-winning repetitions! ;) I just like the idea of writing crunch-friendly-code if possible...i guess i think like a cruncher myself pretty often by now, haha!
"Please, the cruncher thing" - The Cruncher Monster 2018
But then i find myself using only numbers like 999. or .777 all over my introcodes again...i guess i had some luck finding missing bytes that way in the past and now i can´t stop looking for minimal byte-winning repetitions! ;) I just like the idea of writing crunch-friendly-code if possible...i guess i think like a cruncher myself pretty often by now, haha!
"Please, the cruncher thing" - The Cruncher Monster 2018