-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
reproject.frag
277 lines (228 loc) · 7.46 KB
/
reproject.frag
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
// This shader requires the following #DEFINEs:
//
// PROCESS_FUNC - must be one of reproject, prefilter
// DECODE_FUNC - must be one of decodeRGBM, decodeRGBE, decodeGamma or decodeLinear
// ENCODE_FUNC - must be one of encodeRGBM, encodeRGBE, encideGamma or encodeLinear
// SOURCE_FUNC - must be one of sampleCubemap, sampleEquirect, sampleOctahedral
// TARGET_FUNC - must be one of getDirectionCubemap, getDirectionEquirect, getDirectionOctahedral
//
// When filtering:
// NUM_SAMPLES - number of samples
// NUM_SAMPLES_SQRT - sqrt of number of samples
varying vec2 vUv0;
// source
uniform sampler2D sourceTex;
uniform samplerCube sourceCube;
// params:
// x - target cubemap face 0..6
// y - specular power (when prefiltering)
// z - source cubemap seam scale (0 to disable)
// w - target cubemap size for seam calc (0 to disable)
uniform vec4 params;
float targetFace() { return params.x; }
float specularPower() { return params.y; }
float sourceCubeSeamScale() { return params.z; }
float targetCubeSeamScale() { return params.w; }
float PI = 3.141592653589793;
float saturate(float x) {
return clamp(x, 0.0, 1.0);
}
//-- supported codings
vec3 decodeLinear(vec4 source) {
return source.rgb;
}
vec4 encodeLinear(vec3 source) {
return vec4(source, 1.0);
}
vec3 decodeGamma(vec4 source) {
return pow(source.xyz, vec3(2.2));
}
vec4 encodeGamma(vec3 source) {
return vec4(pow(source + 0.0000001, vec3(1.0 / 2.2)), 1.0);
}
vec3 decodeRGBM(vec4 rgbm) {
vec3 color = (8.0 * rgbm.a) * rgbm.rgb;
return color * color;
}
vec4 encodeRGBM(vec3 source) { // modified RGBM
vec4 result;
result.rgb = pow(source.rgb, vec3(0.5));
result.rgb *= 1.0 / 8.0;
result.a = saturate( max( max( result.r, result.g ), max( result.b, 1.0 / 255.0 ) ) );
result.a = ceil(result.a * 255.0) / 255.0;
result.rgb /= result.a;
return result;
}
vec3 decodeRGBE(vec4 source) {
if (source.a == 0.0) {
return vec3(0.0, 0.0, 0.0);
} else {
return source.xyz * pow(2.0, source.w * 255.0 - 128.0);
}
}
vec4 encodeRGBE(vec3 source) {
float maxVal = max(source.x, max(source.y, source.z));
if (maxVal < 1e-32) {
return vec4(0, 0, 0, 0);
} else {
float e = ceil(log2(maxVal));
return vec4(source / pow(2.0, e), (e + 128.0) / 255.0);
}
}
//-- supported projections
vec3 modifySeams(vec3 dir, float amount) {
if (amount != 1.0) {
vec3 adir = abs(dir);
float M = max(max(adir.x, adir.y), adir.z);
if (adir.x == M) {
dir.y *= amount;
dir.z *= amount;
}
else if (adir.y == M) {
dir.x *= amount;
dir.z *= amount;
} else {
dir.x *= amount;
dir.y *= amount;
}
}
return dir;
}
vec2 toSpherical(vec3 dir) {
return vec2(atan(dir.z, dir.x), asin(dir.y));
}
vec3 fromSpherical(vec2 uv) {
return vec3(cos(uv.y) * cos(uv.x),
sin(uv.y),
cos(uv.y) * sin(uv.x));
}
vec4 sampleEquirect(vec2 sph) {
vec2 uv = sph / vec2(PI * 2.0, PI) + 0.5;
return texture2D(sourceTex, vec2(uv.x, 1.0 - uv.y));
}
vec4 sampleEquirect(vec3 dir) {
return sampleEquirect(toSpherical(dir));
}
vec4 sampleCubemap(vec3 dir) {
return textureCube(sourceCube, modifySeams(dir, sourceCubeSeamScale()));
}
vec4 sampleCubemap(vec2 sph) {
return sampleCubemap(fromSpherical(sph));
}
vec3 getDirectionEquirect() {
return fromSpherical((vec2(vUv0.x, 1.0 - vUv0.y) * 2.0 - 1.0) * vec2(PI, PI * 0.5));
}
// octahedral code, based on http://jcgt.org/published/0003/02/01
// "Survey of Efficient Representations for Independent Unit Vectors" by Cigolle, Donow, Evangelakos, Mara, McGuire, Meyer
float signNotZero(float k){
return(k >= 0.0) ? 1.0 : -1.0;
}
vec2 signNotZero(vec2 v) {
return vec2(signNotZero(v.x), signNotZero(v.y));
}
// Returns a unit vector. Argument o is an octahedral vector packed via octEncode, on the [-1, +1] square
vec3 octDecode(vec2 o) {
vec3 v = vec3(o.x, 1.0 - abs(o.x) - abs(o.y), o.y);
if (v.y < 0.0) {
v.xz = (1.0 - abs(v.zx)) * signNotZero(v.xz);
}
return normalize(v);
}
vec3 getDirectionOctahedral() {
return octDecode(vec2(vUv0.x, 1.0 - vUv0.y) * 2.0 - 1.0);
}
// Assumes that v is a unit vector. The result is an octahedral vector on the [-1, +1] square
vec2 octEncode(in vec3 v) {
float l1norm = abs(v.x) + abs(v.y) + abs(v.z);
vec2 result = v.xz * (1.0 / l1norm);
if (v.y < 0.0) {
result = (1.0 - abs(result.yx)) * signNotZero(result.xy);
}
return result;
}
vec4 sampleOctahedral(vec3 dir) {
vec2 uv = octEncode(dir) * 0.5 + 0.5;
return texture2D(sourceTex, vec2(uv.x, 1.0 - uv.y));
}
vec4 sampleOctahedral(vec2 sph) {
return sampleOctahedral(fromSpherical(sph));
}
/////////////////////////////////////////////////////////////////////
vec3 getDirectionCubemap() {
vec2 st = vUv0 * 2.0 - 1.0;
float face = targetFace();
vec3 vec;
if (face == 0.0) {
vec = vec3(1, -st.y, -st.x);
} else if (face == 1.0) {
vec = vec3(-1, -st.y, st.x);
} else if (face == 2.0) {
vec = vec3(st.x, 1, st.y);
} else if (face == 3.0) {
vec = vec3(st.x, -1, -st.y);
} else if (face == 4.0) {
vec = vec3(st.x, -st.y, 1);
} else {
vec = vec3(-st.x, -st.y, -1);
}
return normalize(modifySeams(vec, 1.0 / targetCubeSeamScale()));
}
mat3 matrixFromVector(vec3 n) { // frisvad
float a = 1.0 / (1.0 + n.z);
float b = -n.x * n.y * a;
vec3 b1 = vec3(1.0 - n.x * n.x * a, b, -n.x);
vec3 b2 = vec3(b, 1.0 - n.y * n.y * a, -n.y);
return mat3(b1, b2, n);
}
mat3 matrixFromVectorSlow(vec3 n) {
vec3 a = normalize(cross(n, vec3(0, 1, 0)));
vec3 b = cross(n, a);
return mat3(a, b, n);
}
float rnd(int i) {
float sini = sin(float(i));
float cosi = cos(float(i));
return fract(sin(dot(vec2(sini, cosi), vec2(12.9898, 78.233) * 2.0)) * 43758.5453);
}
vec3 hemisphereSamplePhong(vec2 uv, float specPow) {
float phi = uv.y * 2.0 * PI;
float cosTheta = pow(1.0 - uv.x, 1.0 / (specPow + 1.0));
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
return vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
}
vec4 reproject() {
if (NUM_SAMPLES <= 1) {
// single sample
return ENCODE_FUNC(DECODE_FUNC(SOURCE_FUNC(TARGET_FUNC())));
} else {
// multi sample
vec2 sph = toSpherical(TARGET_FUNC());
vec2 sphu = dFdx(sph);
vec2 sphv = dFdy(sph);
vec3 result = vec3(0.0);
for (float u = 0.0; u < NUM_SAMPLES_SQRT; ++u) {
for (float v = 0.0; v < NUM_SAMPLES_SQRT; ++v) {
result += DECODE_FUNC(SOURCE_FUNC(sph +
sphu * (u / NUM_SAMPLES_SQRT - 0.5) +
sphv * (v / NUM_SAMPLES_SQRT - 0.5)));
}
}
return ENCODE_FUNC(result / (NUM_SAMPLES_SQRT * NUM_SAMPLES_SQRT));
}
}
vec4 prefilter() {
// get the target direction
vec3 vec = TARGET_FUNC();
// construct vector space given target direction
mat3 vecSpace = matrixFromVectorSlow(vec);
vec3 result = vec3(0.0);
for (int i=0; i<NUM_SAMPLES; ++i) {
vec2 uv = vec2(float(i) / float(NUM_SAMPLES), rnd(i));
vec3 dir = vecSpace * hemisphereSamplePhong(uv, specularPower());
result += DECODE_FUNC(SOURCE_FUNC(dir));
}
return ENCODE_FUNC(result / float(NUM_SAMPLES));
}
void main(void) {
gl_FragColor = PROCESS_FUNC();
}