Welcome to the second part of the ray marching tutorial series. If you've seen the first part, you are already familiar with the idea, so I won't go into much details regarding the basics. Lets jump straight into the deep water:
In this part we are going to achieve these shiny bunnies from a volume texture which is available to download here:https://www.dropbox.com/s/kxz8g08lr63p1t6/bunny_volume.tga?dl=0
Please use default compression with sRGB turned off, for the least memory consumption. I also need to mention that there is no any optim involved in this very basic code, as its focusing on a basic implementation. Some errors may appear, and rendering speed isn't the best compared to the code length. Actually, longer code may provide better performance, you will see in some later chapters.
Now lets dive in, and explain what is what here, and what happens. As I mentioned in the previous chapter, the entire tutorial series requires you to have at least basic understanding in one shading language so keep that in mind.
float stepsize = 1.0 / Samples;
float opacity = 0;
for (int i=0; i<Samples; i++)
opacity+= PseudoVolumeTexture(T , TSampler, P - (CV * (stepsize * i)), 16 , 256);
On the left side nodes we can see the inputs of a basic 3d texture ray marcher. We are going to ray march only opacity for now. Thats a surprisingly short code right? Yeah, at its very basics, thats what happens. We pre calculate a step size based on our sample count. This will determine how many slices ("clippings") we will be making. We also make an empty "buffer" that we will use to accumulate our render. The next step is the actually interesting one. Again, you need to be familiar with some basic programming to understand this one, but basically we iterate the sampling of our 3d texture using the built in volume sampler which is called like this:
You may have notices that "SlicesPerSideOnTheTexture" can be automatically calculated using square root. The reason for why its kept as an input is that you usually use this sampler in a loop and using sqrt there could get expensive. You can make your own 3d sampler if you wish, using a method of extending Unreals shader code, from here (please scroll a little bit up in the link):https://polycount.com/discussion/157279/the-wonders-of-technical-art-unreal-engine#latest
You can see that on each step, I moved the volume texture towards the camera ray (also explained on the image in the caption). And at the end, we return the accumulation buffer. There are several errors that may not be so apparent first. The most obvious one is that the bunny kinda tiles. This is because we are using stacked 2d texture. The second one is that the return value of the accumulation will be very high, it will be nearly equal to the iteration count. This is because the input texture has integer values, and we added them on itself many times. You can use a clamp or "saturate" after the custom node to get it into 0-1 range, but this is not the correct way to handle this. But for now for this tutorial, this is good enough as we are not really focusing on this kind of stuff. All of these will be solved in the next chapter. The ideal sample count really depends on the visual requirements, and on your hardware, but as you can see in the Horizon example in the first chapter, around 60 can be handled without a problem even on a console. That may still show slicing artifacts, but don't worry, there are ways to reduce them later.
Again, if you have questions or I wrote something wrong, please let me know, but like I said I most likely won't reply to "I have a normalmap error" level of posts.
Have a nice day, thanks for stopping by, and stay tuned for further updates.