1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <GLES3/gl3.h>
20 #include <math/vec2.h>
21 #include <math/vec3.h>
22 #include <math/vec4.h>
23 
24 static const char* VERTEX_SHADER = R"SHADER__(#version 300 es
25 precision highp float;
26 
27 layout(location = 0) in vec4 mesh_position;
28 
29 void main() {
30     gl_Position = mesh_position;
31 }
32 )SHADER__";
33 
34 static const char* FRAGMENT_SHADER = R"SHADER__(#version 300 es
35 precision highp float;
36 
37 layout(location = 0) uniform vec4 resolution;
38 layout(location = 1) uniform float time;
39 layout(location = 2) uniform vec3[4] SPHERICAL_HARMONICS;
40 
41 layout(location = 0) out vec4 fragColor;
42 
43 #define saturate(x) clamp(x, 0.0, 1.0)
44 #define PI 3.14159265359
45 
46 //------------------------------------------------------------------------------
47 // Distance field functions
48 //------------------------------------------------------------------------------
49 
50 float sdPlane(in vec3 p) {
51     return p.y;
52 }
53 
54 float sdSphere(in vec3 p, float s) {
55     return length(p) - s;
56 }
57 
58 float sdTorus(in vec3 p, in vec2 t) {
59     return length(vec2(length(p.xz) - t.x, p.y)) - t.y;
60 }
61 
62 vec2 opUnion(vec2 d1, vec2 d2) {
63     return d1.x < d2.x ? d1 : d2;
64 }
65 
66 vec2 scene(in vec3 position) {
67     vec2 scene = opUnion(
68           vec2(sdPlane(position), 1.0),
69           vec2(sdSphere(position - vec3(0.0, 0.4, 0.0), 0.4), 12.0)
70     );
71     return scene;
72 }
73 
74 //------------------------------------------------------------------------------
75 // Ray casting
76 //------------------------------------------------------------------------------
77 
78 float shadow(in vec3 origin, in vec3 direction, in float tmin, in float tmax) {
79     float hit = 1.0;
80 
81     for (float t = tmin; t < tmax; ) {
82         float h = scene(origin + direction * t).x;
83         if (h < 0.001) return 0.0;
84         t += h;
85         hit = min(hit, 10.0 * h / t);
86     }
87 
88     return clamp(hit, 0.0, 1.0);
89 }
90 
91 vec2 traceRay(in vec3 origin, in vec3 direction) {
92     float tmin = 0.02;
93     float tmax = 20.0;
94 
95     float material = -1.0;
96     float t = tmin;
97 
98     for ( ; t < tmax; ) {
99         vec2 hit = scene(origin + direction * t);
100         if (hit.x < 0.002 || t > tmax) break;
101         t += hit.x;
102         material = hit.y;
103     }
104 
105     if (t > tmax) {
106         material = -1.0;
107     }
108 
109     return vec2(t, material);
110 }
111 
112 vec3 normal(in vec3 position) {
113     vec3 epsilon = vec3(0.001, 0.0, 0.0);
114     vec3 n = vec3(
115           scene(position + epsilon.xyy).x - scene(position - epsilon.xyy).x,
116           scene(position + epsilon.yxy).x - scene(position - epsilon.yxy).x,
117           scene(position + epsilon.yyx).x - scene(position - epsilon.yyx).x);
118     return normalize(n);
119 }
120 
121 //------------------------------------------------------------------------------
122 // BRDF
123 //------------------------------------------------------------------------------
124 
125 float pow5(float x) {
126     float x2 = x * x;
127     return x2 * x2 * x;
128 }
129 
130 float D_GGX(float linearRoughness, float NoH, const vec3 h) {
131     // Walter et al. 2007, "Microfacet Models for Refraction through Rough Surfaces"
132     float oneMinusNoHSquared = 1.0 - NoH * NoH;
133     float a = NoH * linearRoughness;
134     float k = linearRoughness / (oneMinusNoHSquared + a * a);
135     float d = k * k * (1.0 / PI);
136     return d;
137 }
138 
139 float V_SmithGGXCorrelated(float linearRoughness, float NoV, float NoL) {
140     // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"
141     float a2 = linearRoughness * linearRoughness;
142     float GGXV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2);
143     float GGXL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2);
144     return 0.5 / (GGXV + GGXL);
145 }
146 
147 vec3 F_Schlick(const vec3 f0, float VoH) {
148     // Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"
149     return f0 + (vec3(1.0) - f0) * pow5(1.0 - VoH);
150 }
151 
152 float F_Schlick(float f0, float f90, float VoH) {
153     return f0 + (f90 - f0) * pow5(1.0 - VoH);
154 }
155 
156 float Fd_Burley(float linearRoughness, float NoV, float NoL, float LoH) {
157     // Burley 2012, "Physically-Based Shading at Disney"
158     float f90 = 0.5 + 2.0 * linearRoughness * LoH * LoH;
159     float lightScatter = F_Schlick(1.0, f90, NoL);
160     float viewScatter  = F_Schlick(1.0, f90, NoV);
161     return lightScatter * viewScatter * (1.0 / PI);
162 }
163 
164 float Fd_Lambert() {
165     return 1.0 / PI;
166 }
167 
168 //------------------------------------------------------------------------------
169 // Indirect lighting
170 //------------------------------------------------------------------------------
171 
172 vec3 Irradiance_SphericalHarmonics(const vec3 n) {
173     return max(
174           SPHERICAL_HARMONICS[0]
175         + SPHERICAL_HARMONICS[1] * (n.y)
176         + SPHERICAL_HARMONICS[2] * (n.z)
177         + SPHERICAL_HARMONICS[3] * (n.x)
178         , 0.0);
179 }
180 
181 vec2 PrefilteredDFG_Karis(float roughness, float NoV) {
182     // Karis 2014, "Physically Based Material on Mobile"
183     const vec4 c0 = vec4(-1.0, -0.0275, -0.572,  0.022);
184     const vec4 c1 = vec4( 1.0,  0.0425,  1.040, -0.040);
185 
186     vec4 r = roughness * c0 + c1;
187     float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
188 
189     return vec2(-1.04, 1.04) * a004 + r.zw;
190 }
191 
192 //------------------------------------------------------------------------------
193 // Tone mapping and transfer functions
194 //------------------------------------------------------------------------------
195 
196 vec3 Tonemap_ACES(const vec3 x) {
197     // Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
198     const float a = 2.51;
199     const float b = 0.03;
200     const float c = 2.43;
201     const float d = 0.59;
202     const float e = 0.14;
203     return (x * (a * x + b)) / (x * (c * x + d) + e);
204 }
205 
206 vec3 OECF_sRGBFast(const vec3 linear) {
207     return pow(linear, vec3(1.0 / 2.2));
208 }
209 
210 //------------------------------------------------------------------------------
211 // Rendering
212 //------------------------------------------------------------------------------
213 
214 vec3 render(in vec3 origin, in vec3 direction, out float distance) {
215     // Sky gradient
216     vec3 color = vec3(0.65, 0.85, 1.0) + direction.y * 0.72;
217 
218     // (distance, material)
219     vec2 hit = traceRay(origin, direction);
220     distance = hit.x;
221     float material = hit.y;
222 
223     // We've hit something in the scene
224     if (material > 0.0) {
225         vec3 position = origin + distance * direction;
226 
227         vec3 v = normalize(-direction);
228         vec3 n = normal(position);
229         vec3 l = normalize(vec3(0.6, 0.7, -0.7));
230         vec3 h = normalize(v + l);
231         vec3 r = normalize(reflect(direction, n));
232 
233         float NoV = abs(dot(n, v)) + 1e-5;
234         float NoL = saturate(dot(n, l));
235         float NoH = saturate(dot(n, h));
236         float LoH = saturate(dot(l, h));
237 
238         vec3 baseColor = vec3(0.0);
239         float roughness = 0.0;
240         float metallic = 0.0;
241 
242         float intensity = 2.0;
243         float indirectIntensity = 0.64;
244 
245         if (material < 4.0)  {
246             // Checkerboard floor
247             float f = mod(floor(6.0 * position.z) + floor(6.0 * position.x), 2.0);
248             baseColor = 0.4 + f * vec3(0.6);
249             roughness = 0.1;
250         } else if (material < 16.0) {
251             // Metallic objects
252             baseColor = vec3(0.3, 0.0, 0.0);
253             roughness = 0.2;
254         }
255 
256         float linearRoughness = roughness * roughness;
257         vec3 diffuseColor = (1.0 - metallic) * baseColor.rgb;
258         vec3 f0 = 0.04 * (1.0 - metallic) + baseColor.rgb * metallic;
259 
260         float attenuation = shadow(position, l, 0.02, 2.5);
261 
262         // specular BRDF
263         float D = D_GGX(linearRoughness, NoH, h);
264         float V = V_SmithGGXCorrelated(linearRoughness, NoV, NoL);
265         vec3  F = F_Schlick(f0, LoH);
266         vec3 Fr = (D * V) * F;
267 
268         // diffuse BRDF
269         vec3 Fd = diffuseColor * Fd_Burley(linearRoughness, NoV, NoL, LoH);
270 
271         color = Fd + Fr;
272         color *= (intensity * attenuation * NoL) * vec3(0.98, 0.92, 0.89);
273 
274         // diffuse indirect
275         vec3 indirectDiffuse = Irradiance_SphericalHarmonics(n) * Fd_Lambert();
276 
277         vec2 indirectHit = traceRay(position, r);
278         vec3 indirectSpecular = vec3(0.65, 0.85, 1.0) + r.y * 0.72;
279         if (indirectHit.y > 0.0) {
280             if (indirectHit.y < 4.0)  {
281                 vec3 indirectPosition = position + indirectHit.x * r;
282                 // Checkerboard floor
283                 float f = mod(floor(6.0 * indirectPosition.z) + floor(6.0 * indirectPosition.x), 2.0);
284                 indirectSpecular = 0.4 + f * vec3(0.6);
285             } else if (indirectHit.y < 16.0) {
286                 // Metallic objects
287                 indirectSpecular = vec3(0.3, 0.0, 0.0);
288             }
289         }
290 
291         // indirect contribution
292         vec2 dfg = PrefilteredDFG_Karis(roughness, NoV);
293         vec3 specularColor = f0 * dfg.x + dfg.y;
294         vec3 ibl = diffuseColor * indirectDiffuse + indirectSpecular * specularColor;
295 
296         color += ibl * indirectIntensity;
297     }
298 
299     return color;
300 }
301 
302 //------------------------------------------------------------------------------
303 // Setup and execution
304 //------------------------------------------------------------------------------
305 
306 mat3 setCamera(in vec3 origin, in vec3 target, float rotation) {
307     vec3 forward = normalize(target - origin);
308     vec3 orientation = vec3(sin(rotation), cos(rotation), 0.0);
309     vec3 left = normalize(cross(forward, orientation));
310     vec3 up = normalize(cross(left, forward));
311     return mat3(left, up, forward);
312 }
313 
314 void main() {
315     // Normalized coordinates
316     vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
317     // Aspect ratio
318     p.x *= resolution.x / resolution.y;
319 
320     // Camera position and "look at"
321     vec3 origin = vec3(0.0, 1.0, 0.0);
322     vec3 target = vec3(0.0);
323 
324     origin.x += 2.0 * cos(time * 0.2);
325     origin.z += 2.0 * sin(time * 0.2);
326 
327     mat3 toWorld = setCamera(origin, target, 0.0);
328     vec3 direction = toWorld * normalize(vec3(p.xy, 2.0));
329 
330     // Render scene
331     float distance;
332     vec3 color = render(origin, direction, distance);
333 
334     // Tone mapping
335     color = Tonemap_ACES(color);
336 
337     // Exponential distance fog
338     color = mix(color, 0.8 * vec3(0.7, 0.8, 1.0), 1.0 - exp2(-0.011 * distance * distance));
339 
340     // Gamma compression
341     color = OECF_sRGBFast(color);
342 
343     fragColor = vec4(color, 1.0);
344 }
345 )SHADER__";
346 
347 static const android::vec3 SPHERICAL_HARMONICS[4] =
348         {{0.754554516862612, 0.748542953903366, 0.790921515418539},
349          {-0.083856548007422, 0.092533500963210, 0.322764661032516},
350          {0.308152705331738, 0.366796330467391, 0.466698181299906},
351          {-0.188884931542396, -0.277402551592231, -0.377844212327557}};
352 
353 static const android::vec4 TRIANGLE[3] = {{-1.0f, -1.0f, 1.0f, 1.0f},
354                                           {3.0f, -1.0f, 1.0f, 1.0f},
355                                           {-1.0f, 3.0f, 1.0f, 1.0f}};
356