1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(irradiance_lib.glsl)
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
/* Volume slice to view space depth. */
float volume_z_to_view_z(float z)
{
if (ProjectionMatrix[3][3] == 0.0) {
/* Exponential distribution */
return (exp2(z / volDepthParameters.z) - volDepthParameters.x) / volDepthParameters.y;
}
else {
/* Linear distribution */
return mix(volDepthParameters.x, volDepthParameters.y, z);
}
}
float view_z_to_volume_z(float depth)
{
if (ProjectionMatrix[3][3] == 0.0) {
/* Exponential distribution */
return volDepthParameters.z * log2(depth * volDepthParameters.y + volDepthParameters.x);
}
else {
/* Linear distribution */
return (depth - volDepthParameters.x) * volDepthParameters.z;
}
}
/* Volume texture normalized coordinates to NDC (special range [0, 1]). */
vec3 volume_to_ndc(vec3 cos)
{
cos.z = volume_z_to_view_z(cos.z);
cos.z = get_depth_from_view_z(cos.z);
cos.xy /= volCoordScale.xy;
return cos;
}
vec3 ndc_to_volume(vec3 cos)
{
cos.z = get_view_z_from_depth(cos.z);
cos.z = view_z_to_volume_z(cos.z);
cos.xy *= volCoordScale.xy;
return cos;
}
float phase_function_isotropic()
{
return 1.0 / (4.0 * M_PI);
}
float phase_function(vec3 v, vec3 l, float g)
{
/* Henyey-Greenstein */
float cos_theta = dot(v, l);
g = clamp(g, -1.0 + 1e-3, 1.0 - 1e-3);
float sqr_g = g * g;
return (1 - sqr_g) / max(1e-8, 4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0));
}
vec3 light_volume(LightData ld, vec4 l_vector)
{
float power = 1.0;
if (ld.l_type != SUN) {
/**
* Using "Point Light Attenuation Without Singularity" from Cem Yuksel
* http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf
* http://www.cemyuksel.com/research/pointlightattenuation/
**/
float d = l_vector.w;
float d_sqr = sqr(d);
float r_sqr = sqr(ld.l_radius);
/* Using reformulation that has better numerical percision. */
power = 2.0 / (d_sqr + r_sqr + d * sqrt(d_sqr + r_sqr));
if (ld.l_type == AREA_RECT || ld.l_type == AREA_ELLIPSE) {
/* Modulate by light plane orientation / solid angle. */
power *= saturate(dot(-ld.l_forward, l_vector.xyz / l_vector.w));
}
}
return ld.l_color * ld.l_volume * power;
}
vec3 light_volume_light_vector(LightData ld, vec3 P)
{
if (ld.l_type == SUN) {
return -ld.l_forward;
}
else {
return ld.l_position - P;
}
}
#define VOLUMETRIC_SHADOW_MAX_STEP 128.0
vec3 participating_media_extinction(vec3 wpos, sampler3D volume_extinction)
{
/* Waiting for proper volume shadowmaps and out of frustum shadow map. */
vec3 ndc = project_point(ViewProjectionMatrix, wpos);
vec3 volume_co = ndc_to_volume(ndc * 0.5 + 0.5);
/* Let the texture be clamped to edge. This reduce visual glitches. */
return texture(volume_extinction, volume_co).rgb;
}
vec3 light_volume_shadow(LightData ld, vec3 ray_wpos, vec4 l_vector, sampler3D volume_extinction)
{
#if defined(VOLUME_SHADOW)
/* Heterogeneous volume shadows */
float dd = l_vector.w / volShadowSteps;
vec3 L = l_vector.xyz / volShadowSteps;
vec3 shadow = vec3(1.0);
for (float s = 1.0; s < VOLUMETRIC_SHADOW_MAX_STEP && s <= volShadowSteps; s += 1.0) {
vec3 pos = ray_wpos + L * s;
vec3 s_extinction = participating_media_extinction(pos, volume_extinction);
shadow *= exp(-s_extinction * dd);
}
return shadow;
#else
return vec3(1.0);
#endif /* VOLUME_SHADOW */
}
vec3 irradiance_volumetric(vec3 wpos)
{
#ifdef IRRADIANCE_HL2
IrradianceData ir_data = load_irradiance_cell(0, vec3(1.0));
vec3 irradiance = ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2];
ir_data = load_irradiance_cell(0, vec3(-1.0));
irradiance += ir_data.cubesides[0] + ir_data.cubesides[1] + ir_data.cubesides[2];
irradiance *= 0.16666666; /* 1/6 */
return irradiance;
#else
return vec3(0.0);
#endif
}
uniform sampler3D inScattering;
uniform sampler3D inTransmittance;
void volumetric_resolve(vec2 frag_uvs,
float frag_depth,
out vec3 transmittance,
out vec3 scattering)
{
vec3 volume_cos = ndc_to_volume(vec3(frag_uvs, frag_depth));
scattering = texture(inScattering, volume_cos).rgb;
transmittance = texture(inTransmittance, volume_cos).rgb;
}
|