Workbench: Improve volume render by removing noise using temporal AA
[blender.git] / source / blender / draw / engines / workbench / shaders / workbench_volume_frag.glsl
1
2 uniform mat4 ProjectionMatrix;
3 uniform mat4 ModelMatrixInverse;
4 uniform mat4 ModelViewMatrixInverse;
5 uniform mat4 ModelMatrix;
6 uniform vec3 OrcoTexCoFactors[2];
7
8 uniform sampler2D depthBuffer;
9
10 uniform sampler3D densityTexture;
11 uniform sampler3D shadowTexture;
12 uniform sampler3D flameTexture;
13 uniform sampler1D flameColorTexture;
14 uniform sampler1D transferTexture;
15
16 uniform int samplesLen = 256;
17 uniform float noiseOfs = 0.0f;
18 uniform float stepLength; /* Step length in local space. */
19 uniform float densityScale; /* Simple Opacity multiplicator. */
20 uniform vec4 viewvecs[3];
21
22 uniform float slicePosition;
23 uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */
24
25 #ifdef VOLUME_SLICE
26 in vec3 localPos;
27 #endif
28
29 out vec4 fragColor;
30
31 #define M_PI  3.1415926535897932        /* pi */
32
33 float phase_function_isotropic()
34 {
35         return 1.0 / (4.0 * M_PI);
36 }
37
38 float get_view_z_from_depth(float depth)
39 {
40         if (ProjectionMatrix[3][3] == 0.0) {
41                 float d = 2.0 * depth - 1.0;
42                 return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]);
43         }
44         else {
45                 return viewvecs[0].z + depth * viewvecs[1].z;
46         }
47 }
48
49 vec3 get_view_space_from_depth(vec2 uvcoords, float depth)
50 {
51         if (ProjectionMatrix[3][3] == 0.0) {
52                 return vec3(viewvecs[0].xy + uvcoords * viewvecs[1].xy, 1.0) * get_view_z_from_depth(depth);
53         }
54         else {
55                 return viewvecs[0].xyz + vec3(uvcoords, depth) * viewvecs[1].xyz;
56         }
57 }
58
59 float max_v3(vec3 v) { return max(v.x, max(v.y, v.z)); }
60
61 float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection)
62 {
63         /* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ */
64         vec3 firstplane  = (vec3( 1.0) - lineorigin) / linedirection;
65         vec3 secondplane = (vec3(-1.0) - lineorigin) / linedirection;
66         vec3 furthestplane = min(firstplane, secondplane);
67         return max_v3(furthestplane);
68 }
69
70 void volume_properties(vec3 ls_pos, out vec3 scattering, out float extinction)
71 {
72         vec3 co = ls_pos * 0.5 + 0.5;
73 #ifdef USE_COBA
74         float val = texture(densityTexture, co).r;
75         vec4 tval = texture(transferTexture, val) * densityScale;
76         tval.rgb = pow(tval.rgb, vec3(2.2));
77         scattering = tval.rgb * 1500.0;
78         extinction = max(1e-4, tval.a * 50.0);
79 #else
80         float flame = texture(flameTexture, co).r;
81         vec4 emission = texture(flameColorTexture, flame);
82         float shadows = texture(shadowTexture, co).r;
83         vec4 density = texture(densityTexture, co); /* rgb: color, a: density */
84
85         scattering = density.rgb * density.a * densityScale;
86         extinction = max(1e-4, dot(scattering, vec3(0.33333)));
87
88         scattering *= shadows * M_PI;
89         /* 800 is arbitrary and here to mimic old viewport. TODO make it a parameter */
90         scattering += pow(emission.rgb, vec3(2.2)) * emission.a * 800.0;
91 #endif
92 }
93
94 void eval_volume_step(inout vec3 Lscat, float extinction, float step_len, out float Tr)
95 {
96         Lscat *= phase_function_isotropic();
97         /* Evaluate Scattering */
98         Tr = exp(-extinction * step_len);
99         /* integrate along the current step segment */
100         Lscat = (Lscat - Lscat * Tr) / extinction;
101 }
102
103 #define P(x) ((x + 0.5) * (1.0 / 16.0))
104 const vec4 dither_mat[4] = vec4[4](
105         vec4( P(0.0),  P(8.0),  P(2.0), P(10.0)),
106         vec4(P(12.0),  P(4.0), P(14.0),  P(6.0)),
107         vec4( P(3.0), P(11.0),  P(1.0),  P(9.0)),
108         vec4(P(15.0),  P(7.0), P(13.0),  P(5.0))
109 );
110
111 vec4 volume_integration(
112         vec3 ray_ori, vec3 ray_dir, float ray_inc, float ray_max, float step_len)
113 {
114         /* Start with full transmittance and no scattered light. */
115         vec3 final_scattering = vec3(0.0);
116         float final_transmittance = 1.0;
117
118         ivec2 tx = ivec2(gl_FragCoord.xy) % 4;
119         float noise = fract(dither_mat[tx.x][tx.y] + noiseOfs);
120
121         float ray_len = noise * ray_inc;
122         for (int i = 0; i < samplesLen && ray_len < ray_max; ++i, ray_len += ray_inc) {
123                 vec3 ls_pos = ray_ori + ray_dir * ray_len;
124
125                 vec3 Lscat;
126                 float s_extinction, Tr;
127                 volume_properties(ls_pos, Lscat, s_extinction);
128                 eval_volume_step(Lscat, s_extinction, step_len, Tr);
129                 /* accumulate and also take into account the transmittance from previous steps */
130                 final_scattering += final_transmittance * Lscat;
131                 final_transmittance *= Tr;
132         }
133
134         return vec4(final_scattering, final_transmittance);
135 }
136
137 void main()
138 {
139 #ifdef VOLUME_SLICE
140         /* Manual depth test. TODO remove. */
141         float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r;
142         if (gl_FragCoord.z >= depth) {
143                 discard;
144         }
145
146         ivec3 volume_size = textureSize(densityTexture, 0);
147         float step_len;
148         if (sliceAxis == 0) {
149                 step_len = float(volume_size.x);
150         }
151         else if (sliceAxis == 1) {
152                 step_len = float(volume_size.y);
153         }
154         else {
155                 step_len = float(volume_size.z);
156         }
157         /* FIXME Should be in world space but is in local space. */
158         step_len = 1.0 / step_len;
159
160         vec3 Lscat;
161         float s_extinction, Tr;
162         volume_properties(localPos, Lscat, s_extinction);
163         eval_volume_step(Lscat, s_extinction, step_len, Tr);
164
165         fragColor = vec4(Lscat, Tr);
166 #else
167         vec2 screen_uv = gl_FragCoord.xy / vec2(textureSize(depthBuffer, 0).xy);
168         bool is_persp = ProjectionMatrix[3][3] == 0.0;
169
170         vec3 volume_center = ModelMatrix[3].xyz;
171
172         float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r;
173         float depth_end = min(depth, gl_FragCoord.z);
174         vec3 vs_ray_end = get_view_space_from_depth(screen_uv, depth_end);
175         vec3 vs_ray_ori = get_view_space_from_depth(screen_uv, 0.0);
176         vec3 vs_ray_dir = (is_persp) ? (vs_ray_end - vs_ray_ori) : vec3(0.0, 0.0, -1.0);
177         vs_ray_dir /= abs(vs_ray_dir.z);
178
179         vec3 ls_ray_dir = mat3(ModelViewMatrixInverse) * vs_ray_dir * OrcoTexCoFactors[1] * 2.0;
180         vec3 ls_ray_ori = (ModelViewMatrixInverse * vec4(vs_ray_ori, 1.0)).xyz;
181         vec3 ls_ray_end = (ModelViewMatrixInverse * vec4(vs_ray_end, 1.0)).xyz;
182
183         ls_ray_ori = (OrcoTexCoFactors[0] + ls_ray_ori * OrcoTexCoFactors[1]) * 2.0 - 1.0;
184         ls_ray_end = (OrcoTexCoFactors[0] + ls_ray_end * OrcoTexCoFactors[1]) * 2.0 - 1.0;
185
186         /* TODO: Align rays to volume center so that it mimics old behaviour of slicing the volume. */
187
188         float dist = line_unit_box_intersect_dist(ls_ray_ori, ls_ray_dir);
189         if (dist > 0.0) {
190                 ls_ray_ori = ls_ray_dir * dist + ls_ray_ori;
191         }
192
193         vec3 ls_vol_isect = ls_ray_end - ls_ray_ori;
194         if (dot(ls_ray_dir, ls_vol_isect) < 0.0) {
195                 /* Start is further away than the end.
196                  * That means no volume is intersected. */
197                 discard;
198         }
199
200         fragColor = volume_integration(ls_ray_ori, ls_ray_dir, stepLength,
201                                        length(ls_vol_isect) / length(ls_ray_dir),
202                                        length(vs_ray_dir) * stepLength);
203 #endif
204
205         /* Convert transmitance to alpha so we can use premul blending. */
206         fragColor.a = 1.0 - fragColor.a;
207 }