pouët.net

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
BB Image
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!
added on the 2024-02-29 00:55:09 by Krafpy Krafpy
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)).
added on the 2024-02-29 08:50:31 by NR4 NR4
in general, pay as much attention to designing continuous or at least lipschitz sdfs, this decreases the number of possible rendering bugs immensely
added on the 2024-02-29 08:51:50 by NR4 NR4
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):

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) ?
added on the 2024-02-29 09:32:11 by Krafpy Krafpy
dude. you posted your sdf and it's clearly not continuous?
added on the 2024-02-29 09:55:06 by NR4 NR4
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
added on the 2024-02-29 09:58:08 by NR4 NR4
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.
added on the 2024-02-29 10:20:46 by Krafpy Krafpy
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.
added on the 2024-02-29 11:19:49 by NR4 NR4
Another possibility to do this:
determine the nearest neighbours, cycle through those and use min(.) with their sdf to determine a continuous sdf.
added on the 2024-02-29 11:21:06 by NR4 NR4
(that is, if you have something in every other cell)

Empty cells are more difficult
added on the 2024-02-29 11:22:30 by NR4 NR4
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."
added on the 2024-02-29 11:24:21 by NR4 NR4
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.
added on the 2024-02-29 12:03:20 by Krafpy Krafpy
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
added on the 2024-02-29 12:28:27 by NR4 NR4
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.
added on the 2024-02-29 13:04:03 by NR4 NR4
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.
added on the 2024-02-29 13:06:27 by Krafpy Krafpy
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.
added on the 2024-02-29 13:09:04 by Krafpy Krafpy

login