Raymarching Beginners' Thread
category: code [glöplog]
This a polar repetition with smoothness aka "Smooth Symmetric Polar Mod"
Made a Shadetoy with it https://shadertoy.com/view/NdS3Dh
Made a Shadetoy with it https://shadertoy.com/view/NdS3Dh
Code:
//SmoothSymmetricPolarMod aka smoothRot
//
//s repetitions
//m smoothness (0-1)
//c correction (0-1)
//d object displace from center
//
vec2 smoothRot(vec2 p,float s,float m,float c,float d){
s*=0.5;
float k=length(p);
float x=asin(sin(atan(p.x,p.y)*s)*(1.0-m))*k;
float ds=k*s;
float y=mix(ds,2.0*ds-sqrt(x*x+ds*ds),c);
return vec2(x/s,y/s-d);
}
Hi everyone! I'm writing here in the hope to get some help on a (newbie) raymarching issue.
I'm trying to implement planar hexagonal grid traversal with 3D raymarching in each tile, where the SDF of each tile can be different.
I came up with an algorithm for hexagonal tiling traversal (not the most efficient but at least I understand it), which works well when combined with 2D raymarching.
See Shadertoy id X3XSR4 (sorry I don't have enough glöps yet to post links :c)
However, when I scale it up to 3D, with a hexagonal tiling in the XZ plane (Y is up), it seem to work only when the SDF within each tile is the same. As soon as I add any variation, ugly artifacts appear.
See Shadertoy id 4XsXzH
I can't seem to figure out what is going on... especially since it appears to work in 2D. Of course, there are other hexagon raymarching examples on Shadertoy, but I would like to know what is wrong with my approach.
Thanks in advance for any help!
I'm trying to implement planar hexagonal grid traversal with 3D raymarching in each tile, where the SDF of each tile can be different.
I came up with an algorithm for hexagonal tiling traversal (not the most efficient but at least I understand it), which works well when combined with 2D raymarching.
See Shadertoy id X3XSR4 (sorry I don't have enough glöps yet to post links :c)
However, when I scale it up to 3D, with a hexagonal tiling in the XZ plane (Y is up), it seem to work only when the SDF within each tile is the same. As soon as I add any variation, ugly artifacts appear.
See Shadertoy id 4XsXzH
I can't seem to figure out what is going on... especially since it appears to work in 2D. Of course, there are other hexagon raymarching examples on Shadertoy, but I would like to know what is wrong with my approach.
Thanks in advance for any help!
That's your sdf leaking.
Non-continuous sdfs do this (try cubes and displace them per cell for example).
This can be avoided by constraining sdfs to the grid cells you're using (Then you need a mechanism for advancing the traversal as well, like make your bounds slightly bigger than the grid cell) or by modifying your marcher to respect cell boundaries (by t += min(d, .5 * cellSize)).
Non-continuous sdfs do this (try cubes and displace them per cell for example).
This can be avoided by constraining sdfs to the grid cells you're using (Then you need a mechanism for advancing the traversal as well, like make your bounds slightly bigger than the grid cell) or by modifying your marcher to respect cell boundaries (by t += min(d, .5 * cellSize)).
in general, pay as much attention to designing continuous or at least lipschitz sdfs, this decreases the number of possible rendering bugs immensely
I am actually constraining the raymarcher to grid cells, doing both a grid traversal and raymarching. So the SDF is continuous during the raymarching within a cell, but changes only when going to the next cell if it didn't hit in the current one. I already implemented such a thing for square tiles, and it worked as expected, but breaks for the hexagonal tiles.
The code of the traversal (line 92 of my 3D example):
and the SDF (map function, line 80):
Moreover, if there was an SDF discontinuity issue, why would it work in my 2D example (where I simulate/vizualize a ray traversing and raymarching the grid) which do have discontinuous SDFs (randomly offset spheres in each hexagon) ?
The code of the traversal (line 92 of my 3D example):
Code:
float traverse(vec3 ro, vec3 rd) { // ray origin, ray direction
float t1 = 0.; // total distance from ray origin
for(int i = 0; i < 10; i++){ // traversing hexagonal tiling, 10 cells at most
vec3 p = ro + rd*t1;
vec4 hex = hexCell(p.xz);
// hex.xy = hexagon cell center in world coordinates
// hex.zw = relative coordinates of p.xz to hexagon center
float t2max = hexIntersect(hex.zw, rd.xz); // computes next hexagon intersection (distance to current point)
float t2 = 0.; // distance within tile during raymarching
for(int j = 0; j < 32 && t2 < t2max; j++){ // raymarching, stop when exiting the tile
vec3 q = p + rd*t2;
float d = map(q);
if(d < 0.0001) { // hit
return t1 + t2;
}
t2 += d;
}
t1 += t2max + 0.0001; // go to next hexgaon (slighly past its boundary)
}
return -1.;
}
and the SDF (map function, line 80):
Code:
float map(vec3 p) {
vec4 hex = hexCell(p.xz);
vec2 c = hex.xy;
vec3 q = p - vec3(c.x, 0., c.y);
q.xz += 0.2*(2.*hash12(c)-1.); // random XZ offset
float d = length(q) - 0.2;
return d;
}
Moreover, if there was an SDF discontinuity issue, why would it work in my 2D example (where I simulate/vizualize a ray traversing and raymarching the grid) which do have discontinuous SDFs (randomly offset spheres in each hexagon) ?
dude. you posted your sdf and it's clearly not continuous?
pro tip: use iqs distance visualizer and plot aslice of your sdf. check if the circles have the correct size. if size jumps anywhere, voila continuity problems
Quote:
dude. you posted your sdf and it's clearly not continuous?
Yes, it is discontinuous globally, but *within* a hexagonal cell (or taking independently within a cell) it is continuous. Or at least it should be, or am I missing something?
Quote:
pro tip: use iqs distance visualizer and plot aslice of your sdf. check if the circles have the correct size. if size jumps anywhere, voila continuity problems
I'll try that, but again, I visualized the hit detection with a ray in 2D and it works perfectly fine, at least when the ray lays in the XZ plane.
Quote:
Yes, it is discontinuous globally, but *within* a hexagonal cell (or taking independently within a cell) it is continuous. Or at least it should be, or am I missing something?
This is the exact cause of the problem.
Fix the ("global") discontinuity in the SDF and the bugs will vanish, I promise.
Another possibility to do this:
determine the nearest neighbours, cycle through those and use min(.) with their sdf to determine a continuous sdf.
determine the nearest neighbours, cycle through those and use min(.) with their sdf to determine a continuous sdf.
(that is, if you have something in every other cell)
Empty cells are more difficult
Empty cells are more difficult
BTW: 2D->3D is not an allowed generalization for marching problems.
You do not have "I didn't hit" in the 2D case. Holes in the geometry are "I didn't hit but I should have."
You do not have "I didn't hit" in the 2D case. Holes in the geometry are "I didn't hit but I should have."
Quote:
This is the exact cause of the problem.
Fix the ("global") discontinuity in the SDF and the bugs will vanish, I promise.
I definitely agree that this is an issue if you just do raymarching in such a discontinuous SDF (e.g. using modulo repetition, or such tiling). iq has a recent article about that (domain repetition) which explains the overshooting problem that occurs in such case, and which causes these rendering bugs. Fixing such discontinuity issue is also something I've done in the past.
This is why, to my understanding, we combine raymarching with grid traversal. This is something I experimented several times with square grids and for which it worked well (and this is what other "infinite 3D grid" shaders do, one of my working attempt here: shadertoy.com/view/mtVXWh). Each cell has a different SDF, but traversing the grid cells one by one along the ray, and raymarching in the constrained volume of each cell encountered (see my traverse function above), should fix overshooting problems.
Quote:
Another possibility to do this:
determine the nearest neighbours, cycle through those and use min(.) with their sdf to determine a continuous sdf.
This is what I used to do before I learned about the grid traversal technique. e.g. for square grid I would check the 8 neighboring cells. But it has a pretty big cost in performance and still requires your SDFs to be relatively similar (otherwise you get the overshooting problem again).
Quote:
BTW: 2D->3D is not an allowed generalization for marching problems.
You do not have "I didn't hit" in the 2D case. Holes in the geometry are "I didn't hit but I should have."
If you look at my 2D shader I mentioned previously, I simulate a single ray and visualize the raymarching steps (sample points, radius of the SDF at each point). I scaled to 3D roughly the same way I did for square grids, for which it worked just fine. There was a slight issue with a function expecting a normalized vector, but I fixed that and didn't change the artifacts much. But you are right that there are probably other 2D->3D shenanigans I didn't notice yet for this specific case.
did you actually try any of my suggestions or did you come here to argue? :) ok your superior grid traversal doesn't seem to be able to overcome the discontinuities in your sdf, so try another approach
also bonus points for posting in a beginner's thread to attract a skilled coder's review, while clearly not being a beginner at all.
Yes I did try your initial proposition (t += min(d, .5 * cellSize), with other smaller coefficients than .5), for both a simple raymarching algorithm and raymarching + grid traversal. In the first case it made different artifacts which reduced if I make the coefficient smaller. In the second one it didn't seem to change much the result.
I also tried visualizing the SDF. Of course, the global discontinuities are clear, but the hexagonal boundaries of the discontinuities match the boundaries of the grid.
I don't see how to fix the global discontinuity (or reducing it at least) other than doing the neighbors check. I'll try that, but I expect there would be a limitation on the SDFs (can't have huge differences between two cells).
I agree my approach is currently not working, but I'm trying to understand where/why it breaks! Even if I end up using another one.
I also tried visualizing the SDF. Of course, the global discontinuities are clear, but the hexagonal boundaries of the discontinuities match the boundaries of the grid.
I don't see how to fix the global discontinuity (or reducing it at least) other than doing the neighbors check. I'll try that, but I expect there would be a limitation on the SDFs (can't have huge differences between two cells).
I agree my approach is currently not working, but I'm trying to understand where/why it breaks! Even if I end up using another one.
Quote:
also bonus points for posting in a beginner's thread to attract a skilled coder's review, while clearly not being a beginner at all.
I didn't really know where to put it... :c I still consider myself as a beginner. And mostly, a post on the previous page here discussed this idea of grid traversal.