## sdf staircase

**category:**code [glöplog]

Not sure if code questions are welcome here, if not, do you know a better place? So I'm learning sdf's and working on a 4k intro. I can't figure out how to get a "staircase" effect for cube heights.

I'm experimenting with variations of the following (glsl) :

const float hs[] = {1., 2., 3., 4.};

float x = hs[int(mod(abs(p.x), 4))];

float dist = sdf_cube(p, vec3(1,x,1));

With an unmodified p.x, I get everything at 1 (x = 0) except a little border. https://i.imgur.com/lENokqu.png

If I multiply p.x by 4, I get some crazy artifacts with staircasing. https://i.imgur.com/yFMG8c4.png https://i.imgur.com/aRk067X.png

I'm curious why I'd have to multiply x by 4 to get the modulo to kick in. I have no clue what is happening with the artifacts -.- Any help would be appreciated, thx

I'm experimenting with variations of the following (glsl) :

const float hs[] = {1., 2., 3., 4.};

float x = hs[int(mod(abs(p.x), 4))];

float dist = sdf_cube(p, vec3(1,x,1));

With an unmodified p.x, I get everything at 1 (x = 0) except a little border. https://i.imgur.com/lENokqu.png

If I multiply p.x by 4, I get some crazy artifacts with staircasing. https://i.imgur.com/yFMG8c4.png https://i.imgur.com/aRk067X.png

I'm curious why I'd have to multiply x by 4 to get the modulo to kick in. I have no clue what is happening with the artifacts -.- Any help would be appreciated, thx

Hiya, I am probs not speaking for everyone but please more code questions :)

It's a late weekend so without comment I'm not immediately grasping your code, apologies.

I can only redirect you to the awesome:

http://mercury.sexy/hg_sdf/

which has an fOpUnionStairs which may help you figure this one out, it is a 2D staircase operator which can probably modified to not also include the union of 2 SDFs.

Alternatively, in the case of stairs, think in 2D, and maybe try to do some stuff with shadertoy & share that for easier messing aroudn from my side.

A useful tool I found in the past is simplifying to 2D, and simplifying to visualize only sign (in-out test) without worrying about accurate distance. When you understand the logic for the inside/outside per-pixel test you can rethink the distance function with more knowledge. Not sure if that helps you, but I hope it does!

It's a late weekend so without comment I'm not immediately grasping your code, apologies.

I can only redirect you to the awesome:

http://mercury.sexy/hg_sdf/

which has an fOpUnionStairs which may help you figure this one out, it is a 2D staircase operator which can probably modified to not also include the union of 2 SDFs.

Alternatively, in the case of stairs, think in 2D, and maybe try to do some stuff with shadertoy & share that for easier messing aroudn from my side.

A useful tool I found in the past is simplifying to 2D, and simplifying to visualize only sign (in-out test) without worrying about accurate distance. When you understand the logic for the inside/outside per-pixel test you can rethink the distance function with more knowledge. Not sure if that helps you, but I hope it does!

Thx for helping :) Taking a look at hg_sdf (awesome stuff!). I'll try to get it working, I'll make a shadertoy example if I can't figure it out.

Cheers

Cheers

Your problem is overstepping!

If your ray marches like 2.5 units above your first step(1 unit high), your distance-function returns a safe-distance-to-travel of 2.5 so you march 2.5 units further on your ray and so you hit the 3rd step (3 unit high) somewhere in the middle, but overstepped your second step(2 units high) completely and stepped a half-unit into the third step already!

Maybe this explanation sucked, but i think you get what i try to say! ;)

Generally for such distance-fields sphere-marching ain´t the best way to get a good result, maybe try fixed-step-raymarching for such effects. Or you try to find a good value, resulting in just a few, hard to see, artefacts -> just dont march the whole distance your DE-function returns, but just a fraction of it, try 0.5*returned_distance, if still too many artefacts, lower it to 0.4 and so on and so on.

Try this:

return .3*sdf_cube(p+vec3(0.,floor(p.x),0.), vec3(1000.,.5,1.));

Same as:

p.y += floor(p.x);

return .3*sdf_cube(p, vec3(1000., .5, 1.));

...and forget about all your code above, you don´t need it anymore!

...but if you really want to use mod(), then try this:

p.y += floor(p.x);

p.x=mod(p.x,1);

return .3*sdf_cube(p, vec3(.5, .5, 1.));

(maybe you´ll get artefacts by using x-size=.5 here aswell, because it´s exactly the border of where you repeat space...if so, lower it to sth close to 0.5 instead!)

As soon as you use a bigger number for z (the last 1.) you´ll see many artefacts i guess! ;)

I guess Trevors hint with Mercurys fOpUnionStairs-function is the best choice indeed! but that one is also a bit harder to grasp for beginners.

If your ray marches like 2.5 units above your first step(1 unit high), your distance-function returns a safe-distance-to-travel of 2.5 so you march 2.5 units further on your ray and so you hit the 3rd step (3 unit high) somewhere in the middle, but overstepped your second step(2 units high) completely and stepped a half-unit into the third step already!

Maybe this explanation sucked, but i think you get what i try to say! ;)

Generally for such distance-fields sphere-marching ain´t the best way to get a good result, maybe try fixed-step-raymarching for such effects. Or you try to find a good value, resulting in just a few, hard to see, artefacts -> just dont march the whole distance your DE-function returns, but just a fraction of it, try 0.5*returned_distance, if still too many artefacts, lower it to 0.4 and so on and so on.

Try this:

return .3*sdf_cube(p+vec3(0.,floor(p.x),0.), vec3(1000.,.5,1.));

Same as:

p.y += floor(p.x);

return .3*sdf_cube(p, vec3(1000., .5, 1.));

...and forget about all your code above, you don´t need it anymore!

...but if you really want to use mod(), then try this:

p.y += floor(p.x);

p.x=mod(p.x,1);

return .3*sdf_cube(p, vec3(.5, .5, 1.));

(maybe you´ll get artefacts by using x-size=.5 here aswell, because it´s exactly the border of where you repeat space...if so, lower it to sth close to 0.5 instead!)

As soon as you use a bigger number for z (the last 1.) you´ll see many artefacts i guess! ;)

I guess Trevors hint with Mercurys fOpUnionStairs-function is the best choice indeed! but that one is also a bit harder to grasp for beginners.

fixed-stepsize-raymarching*

-> dont return the distance to closest object, but only return if you hit or not hit it.

if you dont hit only march a small step on your ray, as in 0.01 or even lower.

if you hit continue as you already do: calc normal, color, break loop, return pixel.

-> dont return the distance to closest object, but only return if you hit or not hit it.

if you dont hit only march a small step on your ray, as in 0.01 or even lower.

if you hit continue as you already do: calc normal, color, break loop, return pixel.

to make clear why it oversteps:

if your ray is above your first step at x=0 or alike, it only knows about the space for this exact one cube because of the repetition (in case of mod()) or the hack of changing position every unit (in case of floor(p.x)), it just doesnt see anything else in this calculation, it can´t know there will be more cubes at other positions of x.

if your ray is above your first step at x=0 or alike, it only knows about the space for this exact one cube because of the repetition (in case of mod()) or the hack of changing position every unit (in case of floor(p.x)), it just doesnt see anything else in this calculation, it can´t know there will be more cubes at other positions of x.

**Quote:**

With an unmodified p.x, I get everything at 1 (x = 0) except a little border. https://i.imgur.com/lENokqu.png

after testing your code i am certain you must have a second mod() somewhere in your code! ;)

with your cube-x-size of 1, you should get just one cube this way, with that little border of where it touches the next domain at x=1.0 (where y would be 2.).

in your code, change

float dist = sdf_cube(p, vec3(1,x,1));

to

float dist = sdf_cube(p, vec3(1000,x,1));

and also remove that additional mod() you´ve got somewhere, you should get sth different than in your other screenshots, but no artefacts atleast!

this is what it should look like:

>>>>>><<<<<<

so maybe your approach was even better than mine?! :D (fewer steps to march = better fps!)

but i guess it will fail once you try the same staircase in z-direction, the same direction the ray travels!

exchange your two lines:

const float hs[] = {1., 2., 3., 4.};

float x = hs[int(mod(abs(p.x), 4))];

with:

float x = floor(mod(abs(p.x),4.))+1;

floor() does the same as int(), returns the integer-part of a floating-point-number, it just cuts the part after the dot.

and you don´t need to have/access an array, the floor() (and also your int()-cast) returns the same numbers you got in your array anyway! -> well, i guess you want that up/down-thingie, maybe try some sin(), lesser code and also should look better ;)

or just simply change your code at two points:

float dist = sdf_cube(p, vec3(1,x,1));

to

float dist = sdf_cube(p, vec3(4,x,1));

and your second mod() from

p.x=mod(p.x,1);

to

p.x=mod(p.x,4);

this should yield what you´ve got in your other screenshots, without multiplying by 4 and without artefacts (maybe!) :)

(didn´t code it, i dont know your code except your 3 lines, but in my head this works atleast!)

float dist = sdf_cube(p, vec3(1,x,1));

to

float dist = sdf_cube(p, vec3(4,x,1));

and your second mod() from

p.x=mod(p.x,1);

to

p.x=mod(p.x,4);

this should yield what you´ve got in your other screenshots, without multiplying by 4 and without artefacts (maybe!) :)

(didn´t code it, i dont know your code except your 3 lines, but in my head this works atleast!)

Hey everyone, never had a chance to say thank you. So thank you for the help! :)