Raymarching: Simulating a Black Hole

Johan Svensson
dotcrossdot
Published in
5 min readMay 14, 2019
Final result (skybox downloaded from unity Asset Store).

This is simulation/visualization of a black hole and its gravitational lensing effect. It’s probably one of the more useless things I have made (when it comes to game development), but I think it’s kinda cool so I decided to post it here anyway! I’ll post all the source code below. Keep in mind that it’s far from optimized and can be improved in many ways. Also, I will not explain how raymarching and signed distance functions works. There are a lot of very good tutorials for this out there. I used this one for the basic Unity setup: http://flafla2.github.io/2016/10/01/raymarching.html

Final result (skybox downloaded from unity Asset Store).

DISCLAIMER: All the “science” involved in this article is grossly oversimplified. Don’t take my word for how these things work. I mainly tried to make something that looked cool like the black hole in Interstellar.

What happens to light near a black hole?

Alright, I’ll give it a go in explaining this at the most basic level. Basically objects of very high mass will bend light due to gravity (e.g. black holes). Light itself is not affected by gravity, but since gravity affects the curvature of space, it also affects light that travels through that it (see image below). Since we see things by registering rays of light hitting our eyes, we’ll experience some pretty weird things if that light gets bent along the way.

Source: link

Should you ever look at a black hole, you would then see a distorted view of its surrounding and background (like looking through a lens). This is what is called gravitational lensing, which is what I wanted to simulate using ray-marching. If you want a much better explanation of this phenomenon, check out the YouTube video in Sources at the bottom of the page!

In the GIF below you can see the galaxy behind the black hole being “bent”. Also parts of it can be seen at the bottom of the black hole. The accretion disk is also seen on top and below the black hole as light beams being bent around the black hole hits it.

Final result (skybox downloaded from unity Asset Store).

Implementation

So this is the basic setup: Use ray marching to render a accretion disk and a skybox around a defined point (black hole). Let the ray directions get affected by gravity along the way and see what they eventually hit!

Steps:

  1. Set up a standard ray marching scene in Unity.
  2. Modify the ray marching algorithm in the shader to have fixed distance steps. This is needed for volumetric effects (accretion disk).
  3. Modify the ray marching algorithm to make the direction of the rays get affected by the distance to the black hole (gravity). This will cause the gravitational lensing effect.
  4. Let the rays either sample the skybox at their final step, or get swallowed by the black hole if inside Schwarzschild radius (event horizon).
  5. Let rays traveling through volume (accretion disk) accumulate light along the way if inside the accretion disk.

Changing the direction of the rays

Looking att he code in the raymarching shader below you’ll see a pretty strange function that might need some explanation:

Light redirecting function.

This functions assumes that you have calculated two vectors for your ray at its current point; one heading in the current direction, and one pointing straight towards the black hole. Given a few parameters, the function above will then give a value that can be used to interpolate between those two vectors. The ray will then march on in the interpolated vectors direction.

The main point of this function is to return a value ≥1 when the input parameter distanceToSingularity is less than or equal to schwarzchildRadius. This means that any ray inside the event horizon of the black hole will never be able to escape. The parameter spaceDistortion is a completely arbirtrary value that will control the falloff of the curve. This is what the curve looks like for different distanceToSingularity input values (x-axis).

schwarzchildRadius = 0.5, spaceDistortion = 2

Again… this is a very rough approximation of how these things work. But it’s good enough for this visualization!

The code

To create this effect and experiment with it on your own, you simply add the Raymarch.cs script to the main camera of the scene. Create a material with the BlackHoleRaymarching shader and try out some values. I videos above are recorded with Space Distortion = 2.76 and SchwarzchildRadius = 0.5. You can try out whichever tiling noise texture you like. The rest of the tweak values are not exposed in the material yet. Feel free to expose them if you ever want to experiment with this code!

Suggestions for improvements:

  • The performance is pretty bad at high resolutions. This is mainly due to the fixed step size of the raymarching function. This can surly be improved. I don’t have time to do it right now though.
  • The GetSpaceDistortionLerpValue function does not take into account the step size. This causes different step sizes to create different sized black hole shadows. I’m sure there is a way to solve this as well.
  • The light from the accretion disk is additive, meaning it will burn out in some cases, even with dark colors. This could be changes as well.

Source code:

Sources:

--

--