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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
|
#define M_PI 3.14159265358979323846 /* pi */
#define M_PI_2 1.57079632679489661923 /* pi/2 */
#define M_1_PI 0.318309886183790671538 /* 1/pi */
#define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */
#define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */
/* ------- Structures -------- */
struct LightData {
vec4 position_influence; /* w : InfluenceRadius */
vec4 color_spec; /* w : Spec Intensity */
vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */
vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */
vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */
vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Lamp Type */
};
/* convenience aliases */
#define l_color color_spec.rgb
#define l_spec color_spec.a
#define l_position position_influence.xyz
#define l_influence position_influence.w
#define l_sizex rightvec_sizex.w
#define l_sizey upvec_sizey.w
#define l_right rightvec_sizex.xyz
#define l_up upvec_sizey.xyz
#define l_forward forwardvec_type.xyz
#define l_type forwardvec_type.w
#define l_spot_size spotdata_radius_shadow.x
#define l_spot_blend spotdata_radius_shadow.y
#define l_radius spotdata_radius_shadow.z
#define l_shadowid spotdata_radius_shadow.w
struct ShadowCubeData {
vec4 near_far_bias;
};
/* convenience aliases */
#define sh_cube_near near_far_bias.x
#define sh_cube_far near_far_bias.y
#define sh_cube_bias near_far_bias.z
struct ShadowMapData {
mat4 shadowmat;
vec4 near_far_bias;
};
/* convenience aliases */
#define sh_map_near near_far_bias.x
#define sh_map_far near_far_bias.y
#define sh_map_bias near_far_bias.z
#ifndef MAX_CASCADE_NUM
#define MAX_CASCADE_NUM 1
#endif
struct ShadowCascadeData {
mat4 shadowmat[MAX_CASCADE_NUM];
/* arrays of float are not aligned so use vec4 */
vec4 split_distances;
vec4 bias;
};
struct AreaData {
vec3 corner[4];
float solid_angle;
};
struct ShadingData {
vec3 V; /* View vector */
vec3 N; /* World Normal of the fragment */
vec3 W; /* World Position of the fragment */
vec3 R; /* Reflection vector */
vec3 L; /* Current Light vector (normalized) */
vec3 spec_dominant_dir; /* dominant direction of the specular rays */
vec3 l_vector; /* Current Light vector */
float l_distance; /* distance(l_position, W) */
AreaData area_data; /* If current light is an area light */
};
/* ------- Convenience functions --------- */
vec3 mul(mat3 m, vec3 v) { return m * v; }
mat3 mul(mat3 m1, mat3 m2) { return m1 * m2; }
float saturate(float a) { return clamp(a, 0.0, 1.0); }
vec2 saturate(vec2 a) { return vec2(saturate(a.x), saturate(a.y)); }
vec3 saturate(vec3 a) { return vec3(saturate(a.x), saturate(a.y), saturate(a.z)); }
vec4 saturate(vec4 a) { return vec4(saturate(a.x), saturate(a.y), saturate(a.z), saturate(a.w)); }
float distance_squared(vec2 a, vec2 b) { a -= b; return dot(a, a); }
float distance_squared(vec3 a, vec3 b) { a -= b; return dot(a, a); }
float hypot(float x, float y) { return sqrt(x*x + y*y); }
float inverse_distance(vec3 V) { return max( 1 / length(V), 1e-8); }
float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
{
return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection);
}
vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
{
float dist = line_plane_intersect_dist(lineorigin, linedirection, planeorigin, planenormal);
return lineorigin + linedirection * dist;
}
float line_aligned_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
{
/* aligned plane normal */
vec3 L = planeorigin - lineorigin;
float diskdist = length(L);
vec3 planenormal = -normalize(L);
return -diskdist / dot(planenormal, linedirection);
}
vec3 line_aligned_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
{
float dist = line_aligned_plane_intersect_dist(lineorigin, linedirection, planeorigin);
if (dist < 0) {
/* if intersection is behind we fake the intersection to be
* really far and (hopefully) not inside the radius of interest */
dist = 1e16;
}
return lineorigin + linedirection * dist;
}
/* -- Tangent Space conversion -- */
vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B)
{
return T * vector.x + B * vector.y + N * vector.z;
}
vec3 world_to_tangent(vec3 vector, vec3 N, vec3 T, vec3 B)
{
return vec3( dot(T, vector), dot(B, vector), dot(N, vector));
}
void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B)
{
vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0,0.0,1.0) : vec3(1.0,0.0,0.0);
T = normalize( cross(UpVector, N) );
B = cross(N, T);
}
/* ---- Opengl Depth conversion ---- */
float linear_depth(bool is_persp, float z, float zf, float zn)
{
if (is_persp) {
return (zn * zf) / (z * (zn - zf) + zf);
}
else {
return (z * 2.0 - 1.0) * zf;
}
}
float buffer_depth(bool is_persp, float z, float zf, float zn)
{
if (is_persp) {
return (zf * (zn - z)) / (z * (zn - zf));
}
else {
return (z / (zf * 2.0)) + 0.5;
}
}
#define spherical_harmonics spherical_harmonics_L2
/* http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */
vec3 spherical_harmonics_L1(vec3 N, vec3 shcoefs[9])
{
vec3 sh = vec3(0.0);
sh += 0.282095 * shcoefs[0];
sh += -0.488603 * N.z * shcoefs[1];
sh += 0.488603 * N.y * shcoefs[2];
sh += -0.488603 * N.x * shcoefs[3];
return sh;
}
vec3 spherical_harmonics_L2(vec3 N, vec3 shcoefs[9])
{
vec3 sh = vec3(0.0);
sh += 0.282095 * shcoefs[0];
sh += -0.488603 * N.z * shcoefs[1];
sh += 0.488603 * N.y * shcoefs[2];
sh += -0.488603 * N.x * shcoefs[3];
sh += 1.092548 * N.x * N.z * shcoefs[4];
sh += -1.092548 * N.z * N.y * shcoefs[5];
sh += 0.315392 * (3.0 * N.y * N.y - 1.0) * shcoefs[6];
sh += -1.092548 * N.x * N.y * shcoefs[7];
sh += 0.546274 * (N.x * N.x - N.z * N.z) * shcoefs[8];
return sh;
}
float rectangle_solid_angle(AreaData ad)
{
vec3 n0 = normalize(cross(ad.corner[0], ad.corner[1]));
vec3 n1 = normalize(cross(ad.corner[1], ad.corner[2]));
vec3 n2 = normalize(cross(ad.corner[2], ad.corner[3]));
vec3 n3 = normalize(cross(ad.corner[3], ad.corner[0]));
float g0 = acos(dot(-n0, n1));
float g1 = acos(dot(-n1, n2));
float g2 = acos(dot(-n2, n3));
float g3 = acos(dot(-n3, n0));
return max(0.0, (g0 + g1 + g2 + g3 - 2.0 * M_PI));
}
vec3 get_specular_dominant_dir(vec3 N, vec3 R, float roughness)
{
float smoothness = 1.0 - roughness;
float fac = smoothness * (sqrt(smoothness) + roughness);
return normalize(mix(N, R, fac));
}
/* From UE4 paper */
vec3 mrp_sphere(LightData ld, ShadingData sd, vec3 dir, inout float roughness, out float energy_conservation)
{
roughness = max(3e-3, roughness); /* Artifacts appear with roughness below this threshold */
/* energy preservation */
float sphere_angle = saturate(ld.l_radius / sd.l_distance);
energy_conservation = pow(roughness / saturate(roughness + 0.5 * sphere_angle), 2.0);
/* sphere light */
float inter_dist = dot(sd.l_vector, dir);
vec3 closest_point_on_ray = inter_dist * dir;
vec3 center_to_ray = closest_point_on_ray - sd.l_vector;
/* closest point on sphere */
vec3 closest_point_on_sphere = sd.l_vector + center_to_ray * saturate(ld.l_radius * inverse_distance(center_to_ray));
return normalize(closest_point_on_sphere);
}
vec3 mrp_area(LightData ld, ShadingData sd, vec3 dir, inout float roughness, out float energy_conservation)
{
roughness = max(3e-3, roughness); /* Artifacts appear with roughness below this threshold */
/* FIXME : This needs to be fixed */
energy_conservation = pow(roughness / saturate(roughness + 0.5 * sd.area_data.solid_angle), 2.0);
vec3 refproj = line_plane_intersect(sd.W, dir, ld.l_position, ld.l_forward);
/* Project the point onto the light plane */
vec3 refdir = refproj - ld.l_position;
vec2 mrp = vec2(dot(refdir, ld.l_right), dot(refdir, ld.l_up));
/* clamp to light shape bounds */
vec2 area_half_size = vec2(ld.l_sizex, ld.l_sizey);
mrp = clamp(mrp, -area_half_size, area_half_size);
/* go back in world space */
vec3 closest_point_on_rectangle = sd.l_vector + mrp.x * ld.l_right + mrp.y * ld.l_up;
float len = length(closest_point_on_rectangle);
energy_conservation /= len * len;
return closest_point_on_rectangle / len;
}
/* Fresnel */
vec3 F_schlick(vec3 f0, float cos_theta)
{
float fac = pow(1.0 - cos_theta, 5);
return f0 + (1.0 - f0) * fac;
}
/* GGX */
float D_ggx_opti(float NH, float a2)
{
float tmp = (NH * a2 - NH) * NH + 1.0;
return M_PI * tmp*tmp; /* Doing RCP and mul a2 at the end */
}
float G1_Smith_GGX(float NX, float a2)
{
/* Using Brian Karis approach and refactoring by NX/NX
* this way the (2*NL)*(2*NV) in G = G1(V) * G1(L) gets canceled by the brdf denominator 4*NL*NV
* Rcp is done on the whole G later
* Note that this is not convenient for the transmition formula */
return NX + sqrt(NX * (NX - NX * a2) + a2);
/* return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function */
}
float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness)
{
float a = roughness;
float a2 = a * a;
vec3 H = normalize(L + V);
float NH = max(dot(N, H), 1e-8);
float NL = max(dot(N, L), 1e-8);
float NV = max(dot(N, V), 1e-8);
float G = G1_Smith_GGX(NV, a2) * G1_Smith_GGX(NL, a2); /* Doing RCP at the end */
float D = D_ggx_opti(NH, a2);
/* Denominator is canceled by G1_Smith */
/* bsdf = D * G / (4.0 * NL * NV); /* Reference function */
return NL * a2 / (D * G); /* NL to Fit cycles Equation : line. 345 in bsdf_microfacet.h */
}
|