11
\$\begingroup\$

I think I understand the basics of Signed Distance Field Ray Marching. You model your scene with a bunch of distance fields (such as these: http://iquilezles.org/www/articles/distfunctions/distfunctions.htm), then for each pixel you cast a ray, start from the start of the ray, find the distance to the nearest object at that point, and increment the point by the nearest distance, until you hit something. I managed to do a simple renderer, and that is where most descriptions of the technique stop.

This leaves me with some questions on how SDF Ray Marching can be used in a real world scenario:

Question 1: In a real game, the scene is usually complex and loaded on the CPU, with many dynamic objects. I understand basic occlusion culling (such as octrees), and with polygonal rendering, I would create a list (on the CPU) of items in the view frustrum to render.

So, imagine I have a very complex scene with many characters and dynamic objects moving on the screen, controlled by the CPU. How would I stream the objects I want to render to the GPU each frame? Every example has the scene hardcoded in GLSL. Can someone share an example of the level being streamed to the shader dynamically?

Question 2: How can objects have multiple colours? The distance functions only return a distance, but how do implementations commonly pass the colour back? (e.g. you hit a red sphere and not a blue cube.) If this were a CPU implementation, I could call a global function inside of the distance function when it's a hit to terminate the ray marcher, and that could also pass the hit object's texture/colour. But, how would you return the item's colour or texture in GLSL?

Thank you.

\$\endgroup\$
0
2
\$\begingroup\$

This is a minimal answer, but wanted to share the info in case you didn't get a better answer.

Regarding how real games use ray marching, they usually don't. Only in the last couple years have games started raymarching the depth buffer to do screen space reflections, but no game I'm aware of uses ray marching in the way you describe - yet?

For the other question about colors and such, people commonly associate materials with objects and use "texture coordinates" of the point where the ray hits the object to figure out the material properties at that point on the object. Common materials include things like diffuse color, specular intensity, emissive color and transparency/refraction index.

Hope that is at least some help for you! You might also get good answers from the graphics stack exchange site.

\$\endgroup\$
3
  • 2
    \$\begingroup\$ "no game I'm aware of uses ray marching in the way you describe" Media Molecule's upcoming game Dreams uses signed distance fields for user-generated content sculpting, but if I understand correctly the fields are converted to a point cloud for rendering instead of being raymarched directly. This article may have some ideas: dualshockers.com/2015/08/15/… \$\endgroup\$ – DMGregory Nov 25 '15 at 6:57
  • 1
    \$\begingroup\$ @DMGregory Nice, but I think this is not strictly Ray Marching. So the point is still valid, games don't usually use ray marching. \$\endgroup\$ – concept3d Jan 24 '16 at 13:10
  • 1
    \$\begingroup\$ Updating this thread - the upcoming game Claybook reportedly renders its scenes using rays fired through distance fields directly, rather than converting them to conventional geometry first. So the "yet?" appears to have been borne out two years on. :) \$\endgroup\$ – DMGregory Aug 16 '17 at 16:14
2
\$\begingroup\$

I am currently developing a game engine that utilises signed distance fields as a rendering technique to display smooth procedural geometry (generated with simple primitives like the ones in your link for now, looking to implement Julia and IFS fractals in the future). Since my engine is focussed on procedural generation and must define figures in a way that makes them ray-marcher friendly, I figure I'm in a good place to answer this question :P.

Regarding streaming, the simple solution is to use a typed buffer of some sort and throw it onto the GPU when you want to do your ray-marching. Each element of the buffer is a complex type (e.g. a struct in C/C++), and each type contains elements defining what function you should use to represent it, it's position, rotation, scale, etc., and an average color. The process then simplifies down to:

  1. Cull your scene into a manageable subset (note that frustum culling and occlusion culling are partly performed automatically by the ray-marching algorithm anyways)
  2. Pass the subset into your render input buffer
  3. Pass the buffer to the GPU if it isn't there already, then render your scene with ordinary traditional ray-marching. You will need to perform some kind of per-step search to evaluate which item in the input buffer is closest to each ray for each iteration of the ray-marcher, and you'll need to apply transforms to either the rays (in which case you'll need to invert figure rotations before they reach the GPU) or the distance functions themselves (moving the function origin for position changes, adjusting e.g. cubic side lengths for scale changes, etc.) The simplest approach is to just modify the rays before you pass them along to the actual core distance function.

Regarding figure colors, remember that shaders allow you to define complex types as well as primitives ;). That allows you to throw everything into a C-style struct, then pass those structs back from your distance function.

In my engine, each struct contains a distance, a color, and an ID that ties it to the corresponding figure definition in the input buffer. Each ID is inferred from the surrounding context of the relevant distance function (since my mapping function loops through the input buffer to find the closest figure to each ray for each step, I can safely treat the value of the loop counter when each SDF is called as the figure ID for that function), while distance values are defined using an arbitrary core SDF (e.g. point - figure.pos for a sphere), and colors are either defined from the average color of the appropriate element in the figure buffer (hence why it's useful to keep the figure ID's around) or through a procedural color weighted towards the stored average (one example might be taking an iteration count for some point on the Mandelbulb, mapping your "average color" from FP color space to integer color space, then using the mapped color as a palette by XOR'ing it against the iteration count).

Procedural textures are another approach, but I've never used them myself. iq has done quite a lot of research in that area and posted some interesting demonstrations on Shadertoy, so that could be one way to gather some extra information.

Regardless of whether your color is static for each figure, procedurally generated, or magically sampled from a procedural texture, the basic logic is the same: Abstract figures into some sort of intermediate complex type (e.g. a struct), store both local distance and local color in an instance of that type, then pass the complex type as a return value from your distance function. Depending on your implementation, the output color can then pass directly through to the screen, or follow the collision point into your lighting code.

I don't know if the above was clear enough or not, so don't worry about asking if anything doesn't make sense. I can't really give any GLSL/pixel-shading code samples since I'm working with HLSL and compute shading, but I'm happy to try and go over anything that I didn't write properly in the first place :).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.