You can find all the post in this series here:
- Part 1. Volumetric Atmospheric Scattering
- Part 2. The Theory Behind Atmospheric Scattering
- Part 3. The Mathematics of Rayleigh Scattering
- Part 4. A Journey Through the Atmosphere
- Part 5. A Shader for the Atmospheric Sphere
- Part 6. Intersecting The Atmosphere
- Part 7. Atmospheric Scattering Shader
- 🔒 Part 8. An Introduction to Mie Theory
You can download the Unity package for this tutorial at the bottom of the page.
Intersecting the Atmosphere
As discussed before, the only way we can calculate the optical depth of a segment that passes through the atmosphere, is via a numerical integration. This means dividing out interval in smaller segments of length , and calculating the optical depth of each one assuming its density is constant.
In the image above, the optical depth of the is calculated with 4 samples, each one only taking into account the density at the centre of the segment itself.
The first step is, obviously, finding the points and . If we operate under the assumption that the objects we are rendering a sphere, Unity will try to render its surface. Each pixel on the screen corresponds to a point on the sphere. In the diagram below, that point is called for origin. In a surface shader, corresponds to the worldPos
variable inside the Input
structure. This is how far the shader goes; the only piece of information available to us are , the direction which indicates the direction of the view ray, and the atmospheric sphere centred at with radius . The challenge is to calculate and . The fastest way is to use a geometrical approach, reducing the problem to finding the intersection between the atmospheric sphere and the view ray from the camera.
First of all, we should notice that , and are all lying on the view ray. This means that we can refer to their position not as a point in 3D space, but as the distance on the view ray from the origin. While is the actual point (represented in the shader as a float3
), will be its distance from the origin (as a float
). Both and are two equally valid ways to indicate the same point, and it holds that:
where the overline notation indicates the length of the segment between some arbitrary points and .
For efficiency reasons, in the shader code we will use and , and calculate it from :
We should also notice that the segments and have the same length. What we need now to find the intersection points is to calculate and .
The segment the easiest to calculate. If we look at the diagram above, we can see that is the projection of the vector onto the view ray. Mathematically this projection can be done with the dot product. If you are familiar with shaders you might know the dot product as a measure of how “aligned” two directions are. When it is applied to two vectors and the second one has unitary length, it becomes a projection operator:
One should notice that is a 3D vector, not the length of the segment between and .
What we need to calculate next is the length of the segment . This can be calculated using Pythagoras’ theorem on the triangle . In fact, it holds that:
which means that:
The length of is still unknown. However, it can be calculated by applying once again Pythagoras’ theorem on the triangle :
We know have all the quantities that we need. To sum it up:
That set of equation contains square roots. They are only defined on non-negative numbers. If , then there is no solution, meaning that the view ray does not intersect the sphere.
We can translate this into the following Cg function:
bool rayIntersect ( // Ray float3 O, // Origin float3 D, // Direction // Sphere float3 C, // Centre float R, // Radius out float AO, // First intersection time out float BO // Second intersection time ) { float3 L = C - O; float DT = dot (L, D); float R2 = R * R; float CT2 = dot(L,L) - DT*DT; // Intersection point outside the circle if (CT2 > R2) return false; float AT = sqrt(R2 - CT2); float BT = AT; AO = DT - AT; BO = DT + BT; return true; }
There is not a single value, but three to return: , and whether or not there is an intersection. The lengths of those two segments are returned using the out
keywords, which makes any change the function does on those parameters persistent after its termination.
⭐ Recommended Unity Assets
Unity is free, but you can upgrade to Unity Pro or Unity Plus subscription plans to get more functionalities and training resources for your games.
Colliding with the Planet
There is an additional issue that we have to take into account. Certain view rays hit the planet, hence their journey through the atmosphere reaches an early termination. One approach could be to revise the derivation presented above.
An easier, yet less efficient approach, is to run rayIntersect
twice, and then to adjust the ending point if needed.
This translates to the following code:
// Intersections with the atmospheric sphere float tA; // Atmosphere entry point (worldPos + V * tA) float tB; // Atmosphere exit point (worldPos + V * tB) if (!rayIntersect(O, D, _PlanetCentre, _AtmosphereRadius, tA, tB)) return fixed4(0,0,0,0); // The view rays is looking into deep space // Is the ray passing through the planet core? float pA, pB; if (rayIntersect(O, D, _PlanetCentre, _PlanetRadius, pA, pB)) tB = pA;
Coming Next…
This post showed how it is possible to find intersections between a sphere and a ray. We will use this in the next post to calculate the entrance and exit points of the view ray with the atmospheric sphere.
You can find all the post in this series here:
- Part 1. Volumetric Atmospheric Scattering
- Part 2. The Theory Behind Atmospheric Scattering
- Part 3. The Mathematics of Rayleigh Scattering
- Part 4. A Journey Through the Atmosphere
- Part 5. A Shader for the Atmospheric Sphere
- Part 6. Intersecting The Atmosphere
- Part 7. Atmospheric Scattering Shader
- 🔒 Part 8. An Introduction to Mie Theory
Download
Become a Patron!
You can download all the assets necessary to reproduce the volumetric atmospheric scattering presented in this tutorial.
Leave a Reply