“Cheap” Diamond Rendering

Viktor Zatorskyi
Oct 1, 2018 · 3 min read

I was surfing the web and found this wonderful ShaderToy Diamond. I’d immediately wanted to create something for conventional meshes and not analytical one. This “cheap” solution is still a bit heavy, up to 7 cubemap samples per pixel and yet it should be faster than raymarching/raytracing considering that we should calculate diamond SDF on each step and then 4 more SDFs to get a normal on hit. BTW, my method is not a physically correct rendering, but with a help of the Local Cubemaps it can be though.

What is the deal with diamonds and where all this sparkles and shininess come from? Reflection, Refraction and Dispersion — the very important part is a diamond cut, with the right cut a ray of light can be trapped inside and make multiple bounces to create complex visual patterns (good video on the perfect cut). However, if the cut is bad it’s still possible to save situation by artificially decreasing the critical angle of the diamond. In the real world, critical angle is about 24 degrees. To trap the ray inside the diamond we can make it 3 to 10 degrees. This was the case with my first diamond which I found on a 3D stock for free (thanks to my wife, she created the right diamond for me later). After the ray enters the diamond it splits into many rays by color and this is where the color shifting comes from. There is still a small portion of the Fresnel effect which gives a nice bright outline.

Here is my implementation:

  • To calculate the internal bounces I created a Normal Cubemap captured from inside of the object
Back-face rendering shader with inverted normals
Image for post
Image for post
  • We are going to need some consts for the fragment shader. Different refraction indices and spreads for different color channels. Max bounces — basically the max amount of internal cubemap samples. The cosine of 24 degrees critical angle.
  • At first, we calculate the Fresnel factor and reflection with an environment cubemap
  • The biggest part where most of the magic happens.
  • In a perfect scenario we should split colors on the first entry and calculate RGB rays independently, but it’s too expensive so I decided to fake it on the exit.
  • I also tried Local Cubemaps to make rendering a bit more realistic, but it didn’t give enough improvements to justify costs. I basically was not able to spot any difference.

In theory, it’s possible to make a physically correct simulation with this method. One need to bake not only the normals to cubemap but also a distance from the centre of a diamond into the alpha channel. This way it’s possible to calculate precise location of each bounce.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store