1 of 77

Volumetric Rendering

Advanced Computer Graphics

LTAT.05.035

Raimond Tunnel

2 of 77

What is Volumetric Rendering?

2 / 77

3 of 77

3 / 77

4 of 77

4 / 77

5 of 77

5 / 77

6 of 77

6 / 77

7 of 77

Volumetric Rendering

7 / 77

8 of 77

Volumetric Rendering

8 / 77

9 of 77

Volumetric Rendering

9 / 77

10 of 77

Volumetric Rendering

10 / 77

Δs

11 of 77

Volumetric Rendering

11 / 77

Δs

PHOTON DOES�NOT TRANSMIT

PHOTON TRANSMITS

12 of 77

Ray Marching a Volume

12 / 77

vec4 raymarch(vec3 samplePos, vec3 marchDir, int stepCount, float Δs) {

float transmittance = 1.0;

float illumination = 0.0;

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;

transmittance *= exp(-density(samplePos) * σextinction * Δs);

...� illumination += transmittance * currentLight * Δs;

}

return vec4(vec3(illumination), 1.0 - transmittance);

}

13 of 77

Ray Marching a Volume

13 / 77

vec4 raymarch(vec3 samplePos, vec3 marchDir, int stepCount, float Δs) {

float transmittance = 1.0;

float illumination = 0.0;

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;

transmittance *= exp(-density(samplePos) * σextinction * Δs);

...� illumination += transmittance * currentLight * Δs;

}

return vec4(vec3(illumination), 1.0 - transmittance);

}

14 of 77

Ray Marching a Volume

14 / 77

vec4 raymarch(vec3 samplePos, vec3 marchDir, int stepCount, float Δs) {

float transmittance = 1.0;

float illumination = 0.0;

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;

transmittance *= exp(-density(samplePos) * σextinction * Δs);

...� illumination += transmittance * currentLight * Δs;

}

return vec4(vec3(illumination), 1.0 - transmittance);

}

15 of 77

Ray Marching a Volume

15 / 77

vec4 raymarch(vec3 samplePos, vec3 marchDir, int stepCount, float Δs) {

float transmittance = 1.0;

float illumination = 0.0;

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;

transmittance *= exp(-density(samplePos) * σextinction * Δs);

...� illumination += transmittance * currentLight * Δs;

}

return vec4(vec3(illumination), 1.0 - transmittance);

}

16 of 77

Ray Marching a Volume

16 / 77

vec4 raymarch(vec3 samplePos, vec3 marchDir, int stepCount, float Δs) {

float transmittance = 1.0;

float illumination = 0.0;

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;

transmittance *= exp(-density(samplePos) * σextinction * Δs);

...� illumination += transmittance * currentLight * Δs;

}

return vec4(vec3(illumination), 1.0 - transmittance);

}

17 of 77

Ray Marching a Volume

17 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

18 of 77

Ray Marching a Volume

18 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

Current density

19 of 77

Ray Marching a Volume

19 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

Distance

travelled

20 of 77

The Extinction Coefficient

20 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

Material / medium�coefficient

21 of 77

The Extinction Coefficient

21 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

σextinction = σabsorption + σscattering

(unit-1)

22 of 77

The Extinction Coefficient

22 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

How much light is absorbed?

σextinction = σabsorption + σscattering

(unit-1)

23 of 77

The Extinction Coefficient

23 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

How much light is scattered?

σextinction = σabsorption + σscattering

(unit-1)

24 of 77

The Extinction Coefficient

24 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

For a dense cloud the extinction coefficient could be about 0.04 m-1 = 40 km-1. [ref]

σextinction = σabsorption + σscattering

(unit-1)

25 of 77

Beer’s Law

25 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

26 of 77

Beer’s Law

26 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

Beer’s Law

27 of 77

Beer’s Law

27 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

Beer’s Law

28 of 77

Beer’s Law

28 / 77

transmittance *= exp(-density(samplePos) * σextinction * Δs);

Beer’s Law

Given some light extinction value for the medium, �how much light gets transmitted over a distance (step)?

29 of 77

Ray Marching a Volume

29 / 77

vec4 raymarch(vec3 samplePos, vec3 marchDir, int stepCount, float Δs) {

float transmittance = 1.0;

float illumination = 0.0;

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;

transmittance *= exp(-density(samplePos) * σextinction * Δs);

...� illumination += transmittance * currentLight * Δs;

}

return vec4(vec3(illumination), 1.0 - transmittance);

}

30 of 77

Ray Marching a Volume

30 / 77

illumination += transmittance * currentLight * Δs;

31 of 77

Ray Marching a Volume

31 / 77

illumination += transmittance * currentLight * Δs;

32 of 77

Ray Marching a Volume

32 / 77

illumination += transmittance * currentLight * Δs;

Δs

Δs

33 of 77

Ray Marching a Volume

33 / 77

illumination += transmittance * currentLight * Δs;

Δs

34 of 77

Ray Marching a Volume

34 / 77

vec4 raymarch(vec3 samplePos, vec3 marchDir, int stepCount, float Δs) {

float transmittance = 1.0;

float illumination = 0.0;

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;

transmittance *= exp(-density(samplePos) * σextinction * Δs);

...� illumination += transmittance * currentLight * Δs;

}

return vec4(vec3(illumination), 1.0 - transmittance);

}

35 of 77

Ray Marching a Volume

35 / 77

vec4 raymarch(vec3 samplePos, vec3 marchDir, int stepCount, float Δs) {

float transmittance = 1.0;

float illumination = 0.0;

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;

transmittance *= exp(-density(samplePos) * σextinction * Δs);

...� illumination += transmittance * currentLight * Δs;

}

return vec4(vec3(illumination), 1.0 - transmittance);

}

36 of 77

In-Scattering

36 / 77

37 of 77

Out-Scattering

37 / 77

38 of 77

Ambient Light

38 / 77

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;�� float currentDensity = density(samplePosition);

transmittance *= exp(-currentDensity * σextinction * Δs);

float inScattering = ambientLight;

float outScattering = σscattering * currentDensity;

vec3 currentLight = inScattering * outScattering;� illumination += transmittance * currentLight * Δs;

}

39 of 77

Ambient Light

39 / 77

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;�� float currentDensity = density(samplePosition);

transmittance *= exp(-currentDensity * σextinction * Δs);

float inScattering = ambientLight;

float outScattering = σscattering * currentDensity;

vec3 currentLight = inScattering * outScattering;� illumination += transmittance * currentLight * Δs;

}

40 of 77

Ambient Light

40 / 77

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;�� float currentDensity = density(samplePosition);

transmittance *= exp(-currentDensity * σextinction * Δs);

float inScattering = ambientLight;

float outScattering = σscattering * currentDensity;

vec3 currentLight = inScattering * outScattering;� illumination += transmittance * currentLight * Δs;

}

41 of 77

Ambient Light

41 / 77

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;�� float currentDensity = density(samplePosition);

transmittance *= exp(-currentDensity * σextinction * Δs);

float inScattering = ambientLight;

float outScattering = σscattering * currentDensity;

vec3 currentLight = inScattering * outScattering;� illumination += transmittance * currentLight * Δs;

}

42 of 77

Ambient Light

42 / 77

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;�� float currentDensity = density(samplePosition);

transmittance *= exp(-currentDensity * σextinction * Δs);

float inScattering = ambientLight;

float outScattering = σscattering * currentDensity;

vec3 currentLight = inScattering * outScattering;� illumination += transmittance * currentLight * Δs;

}

43 of 77

Ambient Light

43 / 77

44 of 77

Direct Light

44 / 77

45 of 77

Direct Light

45 / 77

46 of 77

Direct Light

46 / 77

47 of 77

Phase Function

47 / 77

Tells how much light coming at an incident direction scatters towards another direction at angle θ.

θ

48 of 77

Phase Function

48 / 77

Henyey-Greenstein phase function

θ

θ

49 of 77

Phase Function

49 / 77

Henyey-Greenstein phase function

Schlick’s approximation

θ

θ

θ

θ

50 of 77

Phase Function

50 / 77

Henyey-Greenstein phase function

Schlick’s approximation

θ

θ

θ

θ

(AN)ISOTROPY PARAMETER

51 of 77

52 of 77

Direct Light

52 / 77

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;�� float currentDensity = density(samplePosition);

transmittance *= exp(-currentDensity * σextinction * Δs);

float inScattering = ambientLight + directLight * phase(v, -l);

float outScattering = σscattering * currentDensity;

vec3 currentLight = inScattering * outScattering;� illumination += transmittance * currentLight * Δs;

}

53 of 77

Direct Light

53 / 77

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;�� float currentDensity = density(samplePosition);

transmittance *= exp(-currentDensity * σextinction * Δs);

float inScattering = ambientLight + directLight * phase(v, -l);

float outScattering = σscattering * currentDensity;

vec3 currentLight = inScattering * outScattering;� illumination += transmittance * currentLight * Δs;

}

54 of 77

Direct Light

54 / 77

55 of 77

Direct Light

55 / 77

56 of 77

Opacity Shadow Map

56 / 77

57 of 77

Opacity Shadow Map

57 / 77

58 of 77

Opacity Shadow Map

58 / 77

59 of 77

Opacity Shadow Map

59 / 77

60 of 77

Opacity Shadow Map

60 / 77

vec3 pos = projectPointToPlane(fragPos,

vec3(0.0, 0.0, -zmin), // Origin of the plane in camera space

vec3(0.0, 0.0, -1.0) // Camera plane normal in camera space

);

vec3 localPos = (inverse(modelViewMatrix) * vec4(pos, 1.0)).xyz;

float Δz = (zmax - zmin) / float(sliceCount);

...

for (int i = 0; i < sliceCount; i++) {

currentDensity = density(localPos - float(i) * l * Δz);

totalDesnity += currentDensity;

layer[i] = exp(-totalDesnity * Δz * σextinction);

}

61 of 77

Opacity Shadow Map

61 / 77

vec3 pos = projectPointToPlane(fragPos,

vec3(0.0, 0.0, -zmin), // Origin of the plane in camera space

vec3(0.0, 0.0, -1.0) // Camera plane normal in camera space

);

vec3 localPos = (inverse(modelViewMatrix) * vec4(pos, 1.0)).xyz;

float Δz = (zmax - zmin) / float(sliceCount);

...

for (int i = 0; i < sliceCount; i++) {

currentDensity = density(localPos - float(i) * l * Δz);

totalDesnity += currentDensity;

layer[i] = exp(-totalDesnity * Δz * σextinction);

}

62 of 77

Opacity Shadow Map

62 / 77

63 of 77

Opacity Shadow Map

63 / 77

vec3 pos = projectPointToPlane(fragPos,

vec3(0.0, 0.0, -zmin), // Origin of the plane in camera space

vec3(0.0, 0.0, -1.0) // Camera plane normal in camera space

);

vec3 localPos = (inverse(modelViewMatrix) * vec4(pos, 1.0)).xyz;

float Δz = (zmax - zmin) / float(sliceCount);

...

for (int i = 0; i < sliceCount; i++) {

currentDensity = density(localPos - float(i) * l * Δz);

totalDesnity += currentDensity;

layer[i] = exp(-totalDesnity * Δz * σextinction);

}

64 of 77

Opacity Shadow Map

64 / 77

vec3 pos = projectPointToPlane(fragPos,

vec3(0.0, 0.0, -zmin), // Origin of the plane in camera space

vec3(0.0, 0.0, -1.0) // Camera plane normal in camera space

);

vec3 localPos = (inverse(modelViewMatrix) * vec4(pos, 1.0)).xyz;

float Δz = (zmax - zmin) / float(sliceCount);

...

for (int i = 0; i < sliceCount; i++) {

currentDensity = density(localPos - float(i) * l * Δz);

totalDesnity += currentDensity;

layer[i] = exp(-totalDesnity * Δz * σextinction);

}

65 of 77

Opacity Shadow Map

65 / 77

vec3 pos = projectPointToPlane(fragPos,

vec3(0.0, 0.0, -zmin), // Origin of the plane in camera space

vec3(0.0, 0.0, -1.0) // Camera plane normal in camera space

);

vec3 localPos = (inverse(modelViewMatrix) * vec4(pos, 1.0)).xyz;

float Δz = (zmax - zmin) / float(sliceCount);

...

for (int i = 0; i < sliceCount; i++) {

currentDensity = density(localPos - float(i) * l * Δz);

totalDesnity += currentDensity;

layer[i] = exp(-totalDesnity * Δz * σextinction);

}

66 of 77

Opacity Shadow Map

66 / 77

vec3 pos = projectPointToPlane(fragPos,

vec3(0.0, 0.0, -zmin), // Origin of the plane in camera space

vec3(0.0, 0.0, -1.0) // Camera plane normal in camera space

);

vec3 localPos = (inverse(modelViewMatrix) * vec4(pos, 1.0)).xyz;

float Δz = (zmax - zmin) / float(sliceCount);

...

for (int i = 0; i < sliceCount; i++) {

currentDensity = density(localPos - float(i) * l * Δz);

totalDesnity += currentDensity;

layer[i] = exp(-totalDesnity * Δz * σextinction);

}

67 of 77

Opacity Shadow Map

67 / 77

vec3 pos = projectPointToPlane(fragPos,

vec3(0.0, 0.0, -zmin), // Origin of the plane in camera space

vec3(0.0, 0.0, -1.0) // Camera plane normal in camera space

);

vec3 localPos = (inverse(modelViewMatrix) * vec4(pos, 1.0)).xyz;

float Δz = (zmax - zmin) / float(sliceCount);

...

for (int i = 0; i < sliceCount; i++) {

currentDensity = density(localPos - float(i) * l * Δz);

totalDesnity += currentDensity;

layer[i] = exp(-totalDesnity * Δz * σextinction);

}

68 of 77

Opacity Shadow Map

68 / 77

layout(location = 0) out vec4 outColor0;

...

layout(location = 7) out vec4 outColor7;

void main() {

float layer[8] = float[](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);

...

outColor0 = vec4(layer[0]);

...

outColor7 = vec4(layer[7]);

}

69 of 77

69 / 77

70 of 77

Direct Light

70 / 77

for (int i = 0; i < stepCount; i++) {

samplePos += marchDir * Δs;�� float currentDensity = density(samplePosition);

transmittance *= exp(-currentDensity * σextinction * Δs);

float inScattering = ambientLight + directLight * phase(v, -l) *� sampleInScattering(samplePos);

float outScattering = σscattering * currentDensity;

vec3 currentLight = inScattering * outScattering;� illumination += transmittance * currentLight * Δs;

}

71 of 77

Volumetric Rendering

71 / 77

72 of 77

72 / 77

DEMO

73 of 77

References

73 / 77

Presentation Slides

Volumetric Lighting for Many Lights in Lords of the Fallen (2014) – Benjamin Glatzel

Volume and Participating Media (2009) – Yung-Yu Chuang

Volumetric Fog (2014) – Bartlomiej Wronski, Ubisoft Montreal

Learning Materials

74 of 77

References

74 / 77

Theses

Volumetric Fog Rendering (2019) – Siim Raudsepp

Volumetric Clouds Rendering (2019) – Jaagup Kuhi

Volumetric Atmospheric Effects Rendering (2018) – Dean Babić

Real-Time Volumetric Shadows (2011) – Alexandru–Teodor Voicu

Projects

Volumetric Clouds (2019) – Abhishek Arora

Creating a Volumetric Ray Marcher (2016) – Ryan Brucks

75 of 77

References

75 / 77

Papers

76 of 77

76 / 77

Thank You

77 of 77

Bugs

(or Art?)

77 / 77