threejs-awesome-graphics-agent-skills 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/skills/threejs-raymarched-space-effects/SKILL.md +6 -0
- package/skills/threejs-raymarched-space-effects/examples/schwarzschild-geodesic-black-hole/geodesic-black-hole-effect.js +634 -0
- package/source_materials/trace-manifest.json +2 -1
package/package.json
CHANGED
|
@@ -26,6 +26,12 @@ Read the
|
|
|
26
26
|
for the inverse-square steering loop, thin disk density, front-to-back
|
|
27
27
|
accumulation, deterministic star environment, and integrator diagnostics.
|
|
28
28
|
|
|
29
|
+
Read the
|
|
30
|
+
[Schwarzschild geodesic black-hole effect](examples/schwarzschild-geodesic-black-hole/geodesic-black-hole-effect.js)
|
|
31
|
+
for RK2 null-geodesic integration, interpolated equatorial disk crossings,
|
|
32
|
+
Doppler/redshift disk emission, lensed procedural deep field, and HDR bloom
|
|
33
|
+
composition.
|
|
34
|
+
|
|
29
35
|
## Constraints
|
|
30
36
|
|
|
31
37
|
- Do not call a UV swirl “gravitational lensing.”
|
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
|
|
3
|
+
export const SCHWARZSCHILD_BLACK_HOLE_PRESETS = {
|
|
4
|
+
broadDisk: {
|
|
5
|
+
temp: 5800,
|
|
6
|
+
gain: 1,
|
|
7
|
+
beam: 3,
|
|
8
|
+
jet: 0,
|
|
9
|
+
hot: 1,
|
|
10
|
+
innerRadius: 3,
|
|
11
|
+
outerRadius: 12,
|
|
12
|
+
exposure: 1.15,
|
|
13
|
+
},
|
|
14
|
+
jetDisk: {
|
|
15
|
+
temp: 12500,
|
|
16
|
+
gain: 1.35,
|
|
17
|
+
beam: 3.4,
|
|
18
|
+
jet: 1,
|
|
19
|
+
hot: 1,
|
|
20
|
+
innerRadius: 2.7,
|
|
21
|
+
outerRadius: 14.5,
|
|
22
|
+
exposure: 1.05,
|
|
23
|
+
},
|
|
24
|
+
coolDisk: {
|
|
25
|
+
temp: 3300,
|
|
26
|
+
gain: 0.75,
|
|
27
|
+
beam: 2.6,
|
|
28
|
+
jet: 0,
|
|
29
|
+
hot: 0,
|
|
30
|
+
innerRadius: 3.2,
|
|
31
|
+
outerRadius: 9.5,
|
|
32
|
+
exposure: 1.25,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const SCHWARZSCHILD_BLACK_HOLE_QUALITIES = {
|
|
37
|
+
low: 120,
|
|
38
|
+
medium: 200,
|
|
39
|
+
high: 320,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const FULLSCREEN_VERTEX_SHADER = `
|
|
43
|
+
varying vec2 vUv;
|
|
44
|
+
void main() {
|
|
45
|
+
vUv = uv;
|
|
46
|
+
gl_Position = vec4(position.xy, 0.0, 1.0);
|
|
47
|
+
}`;
|
|
48
|
+
|
|
49
|
+
const BLACK_HOLE_FRAGMENT_SHADER = `
|
|
50
|
+
varying vec2 vUv;
|
|
51
|
+
uniform vec2 uRes;
|
|
52
|
+
uniform float uT;
|
|
53
|
+
uniform float uTd;
|
|
54
|
+
uniform vec3 uPos;
|
|
55
|
+
uniform vec3 uRt;
|
|
56
|
+
uniform vec3 uUp;
|
|
57
|
+
uniform vec3 uFw;
|
|
58
|
+
uniform float uFov;
|
|
59
|
+
uniform float uSteps;
|
|
60
|
+
uniform float uGain;
|
|
61
|
+
uniform float uTemp;
|
|
62
|
+
uniform float uBeam;
|
|
63
|
+
uniform float uIn;
|
|
64
|
+
uniform float uOut;
|
|
65
|
+
uniform float uJet;
|
|
66
|
+
uniform float uHot;
|
|
67
|
+
uniform int uDebugMode;
|
|
68
|
+
|
|
69
|
+
#define MAXS 400
|
|
70
|
+
|
|
71
|
+
float hash13(vec3 p) {
|
|
72
|
+
p = fract(p * 0.1031);
|
|
73
|
+
p += dot(p, p.yzx + 33.33);
|
|
74
|
+
return fract((p.x + p.y) * p.z);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
float vnoise(vec3 p) {
|
|
78
|
+
vec3 i = floor(p);
|
|
79
|
+
vec3 f = fract(p);
|
|
80
|
+
vec3 u = f * f * (3.0 - 2.0 * f);
|
|
81
|
+
float a = hash13(i + vec3(0.0, 0.0, 0.0));
|
|
82
|
+
float b = hash13(i + vec3(1.0, 0.0, 0.0));
|
|
83
|
+
float c = hash13(i + vec3(0.0, 1.0, 0.0));
|
|
84
|
+
float d = hash13(i + vec3(1.0, 1.0, 0.0));
|
|
85
|
+
float e = hash13(i + vec3(0.0, 0.0, 1.0));
|
|
86
|
+
float g = hash13(i + vec3(1.0, 0.0, 1.0));
|
|
87
|
+
float h = hash13(i + vec3(0.0, 1.0, 1.0));
|
|
88
|
+
float k = hash13(i + vec3(1.0, 1.0, 1.0));
|
|
89
|
+
return mix(
|
|
90
|
+
mix(mix(a, b, u.x), mix(c, d, u.x), u.y),
|
|
91
|
+
mix(mix(e, g, u.x), mix(h, k, u.x), u.y),
|
|
92
|
+
u.z
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
float fbm(vec3 p) {
|
|
97
|
+
float s = 0.0;
|
|
98
|
+
float a = 0.5;
|
|
99
|
+
for (int i = 0; i < 4; i += 1) {
|
|
100
|
+
s += a * vnoise(p);
|
|
101
|
+
p = p * 2.02 + vec3(11.7, 7.3, 5.1);
|
|
102
|
+
a *= 0.5;
|
|
103
|
+
}
|
|
104
|
+
return s;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
vec3 blackbody(float t) {
|
|
108
|
+
t = clamp(t, 1000.0, 40000.0) * 0.01;
|
|
109
|
+
float r;
|
|
110
|
+
float g;
|
|
111
|
+
float b;
|
|
112
|
+
if (t <= 66.0) {
|
|
113
|
+
r = 255.0;
|
|
114
|
+
g = 99.4708025861 * log(t) - 161.1195681661;
|
|
115
|
+
} else {
|
|
116
|
+
r = 329.698727446 * pow(t - 60.0, -0.1332047592);
|
|
117
|
+
g = 288.1221695283 * pow(t - 60.0, -0.0755148492);
|
|
118
|
+
}
|
|
119
|
+
if (t >= 66.0) {
|
|
120
|
+
b = 255.0;
|
|
121
|
+
} else if (t <= 19.0) {
|
|
122
|
+
b = 0.0;
|
|
123
|
+
} else {
|
|
124
|
+
b = 138.5177312231 * log(t - 10.0) - 305.0447927307;
|
|
125
|
+
}
|
|
126
|
+
vec3 c = clamp(vec3(r, g, b) / 255.0, 0.0, 1.0);
|
|
127
|
+
return c * c;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
vec3 stars(vec3 d) {
|
|
131
|
+
vec3 col = vec3(0.0);
|
|
132
|
+
for (int l = 0; l < 3; l += 1) {
|
|
133
|
+
float S = 90.0 + float(l) * 140.0;
|
|
134
|
+
vec3 p = d * S;
|
|
135
|
+
vec3 id = floor(p);
|
|
136
|
+
vec3 f = fract(p);
|
|
137
|
+
float h = hash13(id);
|
|
138
|
+
vec3 sp = vec3(
|
|
139
|
+
hash13(id + vec3(7.1)),
|
|
140
|
+
hash13(id + vec3(13.7)),
|
|
141
|
+
hash13(id + vec3(27.3))
|
|
142
|
+
);
|
|
143
|
+
float dist = length(f - sp);
|
|
144
|
+
float bmag = pow(h, 28.0) * (16.0 - float(l) * 4.5);
|
|
145
|
+
float tw = 0.78 + 0.22 * sin(uT * (1.5 + h * 4.0) + h * 41.0);
|
|
146
|
+
vec3 tint = mix(
|
|
147
|
+
vec3(1.0, 0.86, 0.72),
|
|
148
|
+
vec3(0.72, 0.82, 1.0),
|
|
149
|
+
hash13(id + vec3(3.3))
|
|
150
|
+
);
|
|
151
|
+
col += bmag * tw * exp(-dist * dist * (140.0 + float(l) * 160.0)) * tint;
|
|
152
|
+
}
|
|
153
|
+
return col;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
vec3 nebula(vec3 d) {
|
|
157
|
+
float n1 = fbm(d * 2.4 + vec3(3.1));
|
|
158
|
+
float n2 = fbm(d * 5.1 - vec3(7.7));
|
|
159
|
+
float n3 = fbm(d * 3.3 + vec3(9.0));
|
|
160
|
+
vec3 col =
|
|
161
|
+
vec3(0.13, 0.05, 0.27) * smoothstep(0.42, 0.95, n1) +
|
|
162
|
+
vec3(0.02, 0.11, 0.30) * smoothstep(0.45, 0.90, n3) +
|
|
163
|
+
vec3(0.45, 0.17, 0.07) * smoothstep(0.62, 0.95, n2) * 0.5;
|
|
164
|
+
vec3 gn = normalize(vec3(0.35, 1.0, 0.2));
|
|
165
|
+
float band = exp(-pow(dot(d, gn) * 4.5, 2.0));
|
|
166
|
+
col += band *
|
|
167
|
+
(vec3(0.30, 0.26, 0.22) * fbm(d * 7.0) + vec3(0.05, 0.045, 0.06)) *
|
|
168
|
+
0.8;
|
|
169
|
+
return col * 0.55;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
vec3 background(vec3 d) {
|
|
173
|
+
return nebula(d) + stars(d);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
vec4 diskShade(vec3 p, vec3 rd) {
|
|
177
|
+
float r = length(p.xz);
|
|
178
|
+
float fadeIn = smoothstep(uIn, uIn + 0.35, r);
|
|
179
|
+
float fadeOut = 1.0 - smoothstep(uOut * 0.55, uOut, r);
|
|
180
|
+
float fade = fadeIn * fadeOut;
|
|
181
|
+
if (fade < 0.002) return vec4(0.0);
|
|
182
|
+
|
|
183
|
+
float ang = atan(p.z, p.x);
|
|
184
|
+
float om = 0.7071 * pow(r, -1.5);
|
|
185
|
+
float a2 = ang - om * uTd;
|
|
186
|
+
vec3 q = vec3(cos(a2) * r, sin(a2) * r, 0.35 * r);
|
|
187
|
+
float n = fbm(q * 1.15 + vec3(0.0, 0.0, uTd * 0.05));
|
|
188
|
+
float dens = 0.30 + 0.70 * smoothstep(0.32, 0.85, n);
|
|
189
|
+
dens *= 0.78 + 0.34 * vnoise(q * 4.2);
|
|
190
|
+
|
|
191
|
+
float em =
|
|
192
|
+
pow(max(1.0 - sqrt(uIn * 0.95 / r), 0.0), 0.6) *
|
|
193
|
+
pow(uIn / r, 2.0);
|
|
194
|
+
|
|
195
|
+
float v = clamp(sqrt(0.5 / max(r - 1.0, 0.4)), 0.0, 0.92);
|
|
196
|
+
vec3 tang = normalize(vec3(-p.z, 0.0, p.x));
|
|
197
|
+
float mu = dot(tang, -rd);
|
|
198
|
+
float dop = sqrt(1.0 - v * v) / (1.0 - v * mu);
|
|
199
|
+
float gsh = sqrt(max(1.0 - 1.0 / r, 0.03));
|
|
200
|
+
float s = dop * gsh;
|
|
201
|
+
|
|
202
|
+
vec3 c = blackbody(uTemp * pow(uIn / r, 0.75) * s);
|
|
203
|
+
float I = em * dens * pow(s, uBeam);
|
|
204
|
+
vec3 col = c * I * 14.0;
|
|
205
|
+
|
|
206
|
+
if (uHot > 0.5) {
|
|
207
|
+
float rh = uIn * 1.30;
|
|
208
|
+
float ah = 0.7071 * pow(rh, -1.5) * uTd + 1.7;
|
|
209
|
+
vec2 hp = vec2(cos(ah), sin(ah)) * rh;
|
|
210
|
+
vec2 dv = p.xz - hp;
|
|
211
|
+
col +=
|
|
212
|
+
blackbody(uTemp * 1.3 * s) *
|
|
213
|
+
exp(-dot(dv, dv) * 5.0) *
|
|
214
|
+
9.0 *
|
|
215
|
+
pow(s, uBeam);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
float alpha = clamp(dens * 1.25, 0.0, 1.0) * fade;
|
|
219
|
+
return vec4(col * fade, alpha);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
void main() {
|
|
223
|
+
vec2 ndc = vUv * 2.0 - 1.0;
|
|
224
|
+
ndc.x *= uRes.x / uRes.y;
|
|
225
|
+
vec3 rd = normalize(uFw + uFov * (ndc.x * uRt + ndc.y * uUp));
|
|
226
|
+
|
|
227
|
+
vec3 p = uPos;
|
|
228
|
+
vec3 v = rd;
|
|
229
|
+
vec3 hv = cross(p, v);
|
|
230
|
+
float h2 = dot(hv, hv);
|
|
231
|
+
|
|
232
|
+
vec3 col = vec3(0.0);
|
|
233
|
+
float T = 1.0;
|
|
234
|
+
bool esc = false;
|
|
235
|
+
vec3 ed = v;
|
|
236
|
+
float stepsTaken = 0.0;
|
|
237
|
+
float closestRadius = length(p);
|
|
238
|
+
float diskOpacity = 0.0;
|
|
239
|
+
|
|
240
|
+
for (int i = 0; i < MAXS; i += 1) {
|
|
241
|
+
if (float(i) >= uSteps) break;
|
|
242
|
+
float r2 = dot(p, p);
|
|
243
|
+
float r = sqrt(r2);
|
|
244
|
+
closestRadius = min(closestRadius, r);
|
|
245
|
+
if (r < 1.02) break;
|
|
246
|
+
if (r2 > 2500.0 && dot(p, v) > 0.0) {
|
|
247
|
+
esc = true;
|
|
248
|
+
ed = normalize(v);
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
float dt = clamp((r - 1.0) * 0.28, 0.02, 0.7);
|
|
253
|
+
|
|
254
|
+
vec3 a1 = -1.5 * h2 * p / max(r2 * r2 * r, 0.05);
|
|
255
|
+
vec3 pm = p + v * (dt * 0.5);
|
|
256
|
+
vec3 vm = v + a1 * (dt * 0.5);
|
|
257
|
+
float rm2 = dot(pm, pm);
|
|
258
|
+
vec3 a2v = -1.5 * h2 * pm / max(rm2 * rm2 * sqrt(rm2), 0.05);
|
|
259
|
+
vec3 pn = p + vm * dt;
|
|
260
|
+
vec3 vn = v + a2v * dt;
|
|
261
|
+
if (dot(pn, pn) < 1.0) break;
|
|
262
|
+
|
|
263
|
+
if (uJet > 0.5) {
|
|
264
|
+
float rc2 = dot(p.xz, p.xz);
|
|
265
|
+
float jd =
|
|
266
|
+
exp(-rc2 * 1.8) *
|
|
267
|
+
smoothstep(0.7, 2.5, abs(p.y)) *
|
|
268
|
+
exp(-abs(p.y) * 0.16);
|
|
269
|
+
jd *= 0.6 + 0.5 *
|
|
270
|
+
vnoise(vec3(p.x * 3.0, p.y * 1.2 - uTd * 2.5, p.z * 3.0));
|
|
271
|
+
col += T * vec3(0.35, 0.55, 1.0) * jd * dt * 0.55;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (p.y * pn.y < 0.0) {
|
|
275
|
+
float f = p.y / (p.y - pn.y);
|
|
276
|
+
vec3 hp = mix(p, pn, f);
|
|
277
|
+
float hr = length(hp.xz);
|
|
278
|
+
if (hr > uIn * 0.98 && hr < uOut) {
|
|
279
|
+
vec4 dc = diskShade(hp, normalize(mix(v, vn, f)));
|
|
280
|
+
col += T * dc.rgb * uGain;
|
|
281
|
+
T *= 1.0 - dc.a;
|
|
282
|
+
diskOpacity = max(diskOpacity, dc.a);
|
|
283
|
+
if (T < 0.02) break;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
p = pn;
|
|
287
|
+
v = vn;
|
|
288
|
+
stepsTaken = float(i) + 1.0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (uDebugMode == 1) {
|
|
292
|
+
col = esc ? ed * 0.5 + 0.5 : vec3(0.04, 0.0, 0.0);
|
|
293
|
+
} else if (uDebugMode == 2) {
|
|
294
|
+
col = vec3(T);
|
|
295
|
+
} else if (uDebugMode == 3) {
|
|
296
|
+
col = vec3(diskOpacity, diskOpacity * 0.35, diskOpacity * 0.04);
|
|
297
|
+
} else if (uDebugMode == 4) {
|
|
298
|
+
float approach = 1.0 - smoothstep(1.0, 14.0, closestRadius);
|
|
299
|
+
col = vec3(approach, approach * approach, 1.0 - approach);
|
|
300
|
+
} else if (uDebugMode == 5) {
|
|
301
|
+
col = vec3(clamp(stepsTaken / max(uSteps, 1.0), 0.0, 1.0));
|
|
302
|
+
} else {
|
|
303
|
+
if (esc) col += T * background(ed);
|
|
304
|
+
col = min(col, vec3(64.0));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (any(notEqual(col, col))) col = vec3(0.0);
|
|
308
|
+
gl_FragColor = vec4(col, 1.0);
|
|
309
|
+
}`;
|
|
310
|
+
|
|
311
|
+
const BRIGHT_FRAGMENT_SHADER = `
|
|
312
|
+
varying vec2 vUv;
|
|
313
|
+
uniform sampler2D tex;
|
|
314
|
+
uniform float uThresh;
|
|
315
|
+
void main() {
|
|
316
|
+
vec3 c = texture2D(tex, vUv).rgb;
|
|
317
|
+
c = min(max(c, vec3(0.0)), vec3(48.0));
|
|
318
|
+
if (any(notEqual(c, c))) c = vec3(0.0);
|
|
319
|
+
float l = max(max(c.r, c.g), c.b);
|
|
320
|
+
float k = max(l - uThresh, 0.0);
|
|
321
|
+
k = k / (k + 0.6);
|
|
322
|
+
gl_FragColor = vec4(c * k, 1.0);
|
|
323
|
+
}`;
|
|
324
|
+
|
|
325
|
+
const BLUR_FRAGMENT_SHADER = `
|
|
326
|
+
varying vec2 vUv;
|
|
327
|
+
uniform sampler2D tex;
|
|
328
|
+
uniform vec2 uDir;
|
|
329
|
+
void main() {
|
|
330
|
+
vec3 s = texture2D(tex, vUv).rgb * 0.2270270270;
|
|
331
|
+
vec2 o1 = uDir * 1.3846153846;
|
|
332
|
+
vec2 o2 = uDir * 3.2307692308;
|
|
333
|
+
s += texture2D(tex, vUv + o1).rgb * 0.3162162162;
|
|
334
|
+
s += texture2D(tex, vUv - o1).rgb * 0.3162162162;
|
|
335
|
+
s += texture2D(tex, vUv + o2).rgb * 0.0702702703;
|
|
336
|
+
s += texture2D(tex, vUv - o2).rgb * 0.0702702703;
|
|
337
|
+
gl_FragColor = vec4(s, 1.0);
|
|
338
|
+
}`;
|
|
339
|
+
|
|
340
|
+
const COMPOSITE_FRAGMENT_SHADER = `
|
|
341
|
+
varying vec2 vUv;
|
|
342
|
+
uniform sampler2D tScene;
|
|
343
|
+
uniform sampler2D tB1;
|
|
344
|
+
uniform sampler2D tB2;
|
|
345
|
+
uniform float uExp;
|
|
346
|
+
uniform float uBloom;
|
|
347
|
+
uniform float uTn;
|
|
348
|
+
uniform vec2 uRes2;
|
|
349
|
+
|
|
350
|
+
vec3 aces(vec3 x) {
|
|
351
|
+
return clamp(
|
|
352
|
+
(x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14),
|
|
353
|
+
0.0,
|
|
354
|
+
1.0
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
float h21(vec2 p) {
|
|
359
|
+
p = fract(p * vec2(123.34, 456.21));
|
|
360
|
+
p += dot(p, p + 45.32);
|
|
361
|
+
return fract(p.x * p.y);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
void main() {
|
|
365
|
+
vec2 uv = vUv;
|
|
366
|
+
vec2 cc = uv - 0.5;
|
|
367
|
+
float r2 = dot(cc, cc);
|
|
368
|
+
float ca = r2 * 0.055;
|
|
369
|
+
vec3 col;
|
|
370
|
+
col.r = texture2D(tScene, uv + cc * ca).r;
|
|
371
|
+
col.g = texture2D(tScene, uv).g;
|
|
372
|
+
col.b = texture2D(tScene, uv - cc * ca).b;
|
|
373
|
+
vec3 bl = texture2D(tB1, uv).rgb * 0.9 + texture2D(tB2, uv).rgb * 0.75;
|
|
374
|
+
col += bl * uBloom;
|
|
375
|
+
col *= uExp;
|
|
376
|
+
col = aces(col);
|
|
377
|
+
col = pow(col, vec3(0.92));
|
|
378
|
+
col *= 1.0 - 0.32 * smoothstep(0.15, 0.62, r2);
|
|
379
|
+
col += (h21(uv * uRes2 + vec2(uTn, uTn * 1.7)) - 0.5) * 0.022;
|
|
380
|
+
gl_FragColor = vec4(col, 1.0);
|
|
381
|
+
}`;
|
|
382
|
+
|
|
383
|
+
function createRenderTarget(width, height, type) {
|
|
384
|
+
return new THREE.WebGLRenderTarget(width, height, {
|
|
385
|
+
minFilter: THREE.LinearFilter,
|
|
386
|
+
magFilter: THREE.LinearFilter,
|
|
387
|
+
format: THREE.RGBAFormat,
|
|
388
|
+
type,
|
|
389
|
+
depthBuffer: false,
|
|
390
|
+
stencilBuffer: false,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function createPass(fragmentShader, uniforms) {
|
|
395
|
+
const scene = new THREE.Scene();
|
|
396
|
+
const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
|
397
|
+
const geometry = new THREE.PlaneGeometry(2, 2);
|
|
398
|
+
const material = new THREE.ShaderMaterial({
|
|
399
|
+
vertexShader: FULLSCREEN_VERTEX_SHADER,
|
|
400
|
+
fragmentShader,
|
|
401
|
+
uniforms,
|
|
402
|
+
depthWrite: false,
|
|
403
|
+
depthTest: false,
|
|
404
|
+
});
|
|
405
|
+
scene.add(new THREE.Mesh(geometry, material));
|
|
406
|
+
return { scene, camera, geometry, material, uniforms };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function disposePass(pass) {
|
|
410
|
+
pass.geometry.dispose();
|
|
411
|
+
pass.material.dispose();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function runPass(renderer, pass, target) {
|
|
415
|
+
renderer.setRenderTarget(target);
|
|
416
|
+
renderer.render(pass.scene, pass.camera);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function selectRenderTargetType(renderer, fallbackType) {
|
|
420
|
+
if (fallbackType) return fallbackType;
|
|
421
|
+
if (
|
|
422
|
+
renderer?.capabilities?.isWebGL2 ||
|
|
423
|
+
(
|
|
424
|
+
renderer?.extensions?.get("OES_texture_half_float") &&
|
|
425
|
+
renderer?.extensions?.get("OES_texture_half_float_linear")
|
|
426
|
+
)
|
|
427
|
+
) {
|
|
428
|
+
return THREE.HalfFloatType;
|
|
429
|
+
}
|
|
430
|
+
return THREE.UnsignedByteType;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export function createSchwarzschildGeodesicBlackHoleEffect({
|
|
434
|
+
renderer = null,
|
|
435
|
+
preset = "broadDisk",
|
|
436
|
+
quality = "medium",
|
|
437
|
+
bloom = 1,
|
|
438
|
+
renderTargetType = null,
|
|
439
|
+
} = {}) {
|
|
440
|
+
const targetType = selectRenderTargetType(renderer, renderTargetType);
|
|
441
|
+
const bhUniforms = {
|
|
442
|
+
uRes: { value: new THREE.Vector2(2, 2) },
|
|
443
|
+
uT: { value: 0 },
|
|
444
|
+
uTd: { value: 0 },
|
|
445
|
+
uPos: { value: new THREE.Vector3() },
|
|
446
|
+
uRt: { value: new THREE.Vector3() },
|
|
447
|
+
uUp: { value: new THREE.Vector3() },
|
|
448
|
+
uFw: { value: new THREE.Vector3() },
|
|
449
|
+
uFov: { value: Math.tan(THREE.MathUtils.degToRad(30)) },
|
|
450
|
+
uSteps: { value: SCHWARZSCHILD_BLACK_HOLE_QUALITIES.medium },
|
|
451
|
+
uGain: { value: 1 },
|
|
452
|
+
uTemp: { value: 5800 },
|
|
453
|
+
uBeam: { value: 3 },
|
|
454
|
+
uIn: { value: 3 },
|
|
455
|
+
uOut: { value: 12 },
|
|
456
|
+
uJet: { value: 0 },
|
|
457
|
+
uHot: { value: 1 },
|
|
458
|
+
uDebugMode: { value: 0 },
|
|
459
|
+
};
|
|
460
|
+
const brightUniforms = {
|
|
461
|
+
tex: { value: null },
|
|
462
|
+
uThresh: { value: 0.85 },
|
|
463
|
+
};
|
|
464
|
+
const blurUniforms = {
|
|
465
|
+
tex: { value: null },
|
|
466
|
+
uDir: { value: new THREE.Vector2() },
|
|
467
|
+
};
|
|
468
|
+
const compositeUniforms = {
|
|
469
|
+
tScene: { value: null },
|
|
470
|
+
tB1: { value: null },
|
|
471
|
+
tB2: { value: null },
|
|
472
|
+
uExp: { value: 1.15 },
|
|
473
|
+
uBloom: { value: bloom },
|
|
474
|
+
uTn: { value: 0 },
|
|
475
|
+
uRes2: { value: new THREE.Vector2(2, 2) },
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const blackHolePass = createPass(BLACK_HOLE_FRAGMENT_SHADER, bhUniforms);
|
|
479
|
+
const brightPass = createPass(BRIGHT_FRAGMENT_SHADER, brightUniforms);
|
|
480
|
+
const blurPass = createPass(BLUR_FRAGMENT_SHADER, blurUniforms);
|
|
481
|
+
const compositePass = createPass(COMPOSITE_FRAGMENT_SHADER, compositeUniforms);
|
|
482
|
+
|
|
483
|
+
let width = 2;
|
|
484
|
+
let height = 2;
|
|
485
|
+
let frame = 0;
|
|
486
|
+
let debugMode = "final";
|
|
487
|
+
let sceneTarget = createRenderTarget(width, height, targetType);
|
|
488
|
+
let halfA = createRenderTarget(width, height, targetType);
|
|
489
|
+
let halfB = createRenderTarget(width, height, targetType);
|
|
490
|
+
let quarterA = createRenderTarget(width, height, targetType);
|
|
491
|
+
let quarterB = createRenderTarget(width, height, targetType);
|
|
492
|
+
|
|
493
|
+
const debugModes = new Map([
|
|
494
|
+
["final", 0],
|
|
495
|
+
["escape-direction", 1],
|
|
496
|
+
["transmittance", 2],
|
|
497
|
+
["disk-crossing", 3],
|
|
498
|
+
["closest-approach", 4],
|
|
499
|
+
["step-count", 5],
|
|
500
|
+
]);
|
|
501
|
+
|
|
502
|
+
function setSize(nextWidth, nextHeight) {
|
|
503
|
+
width = Math.max(1, Math.round(nextWidth));
|
|
504
|
+
height = Math.max(1, Math.round(nextHeight));
|
|
505
|
+
const halfWidth = Math.max(1, width >> 1);
|
|
506
|
+
const halfHeight = Math.max(1, height >> 1);
|
|
507
|
+
const quarterWidth = Math.max(1, width >> 2);
|
|
508
|
+
const quarterHeight = Math.max(1, height >> 2);
|
|
509
|
+
sceneTarget.setSize(width, height);
|
|
510
|
+
halfA.setSize(halfWidth, halfHeight);
|
|
511
|
+
halfB.setSize(halfWidth, halfHeight);
|
|
512
|
+
quarterA.setSize(quarterWidth, quarterHeight);
|
|
513
|
+
quarterB.setSize(quarterWidth, quarterHeight);
|
|
514
|
+
bhUniforms.uRes.value.set(width, height);
|
|
515
|
+
compositeUniforms.uRes2.value.set(width, height);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function applyPreset(name) {
|
|
519
|
+
const nextPreset =
|
|
520
|
+
SCHWARZSCHILD_BLACK_HOLE_PRESETS[name] ??
|
|
521
|
+
SCHWARZSCHILD_BLACK_HOLE_PRESETS.broadDisk;
|
|
522
|
+
bhUniforms.uTemp.value = nextPreset.temp;
|
|
523
|
+
bhUniforms.uGain.value = nextPreset.gain;
|
|
524
|
+
bhUniforms.uBeam.value = nextPreset.beam;
|
|
525
|
+
bhUniforms.uJet.value = nextPreset.jet;
|
|
526
|
+
bhUniforms.uHot.value = nextPreset.hot;
|
|
527
|
+
bhUniforms.uIn.value = nextPreset.innerRadius;
|
|
528
|
+
bhUniforms.uOut.value = nextPreset.outerRadius;
|
|
529
|
+
compositeUniforms.uExp.value = nextPreset.exposure;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function setQuality(name) {
|
|
533
|
+
bhUniforms.uSteps.value =
|
|
534
|
+
SCHWARZSCHILD_BLACK_HOLE_QUALITIES[name] ??
|
|
535
|
+
SCHWARZSCHILD_BLACK_HOLE_QUALITIES.medium;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
applyPreset(preset);
|
|
539
|
+
setQuality(quality);
|
|
540
|
+
|
|
541
|
+
return {
|
|
542
|
+
uniforms: {
|
|
543
|
+
blackHole: bhUniforms,
|
|
544
|
+
bright: brightUniforms,
|
|
545
|
+
blur: blurUniforms,
|
|
546
|
+
composite: compositeUniforms,
|
|
547
|
+
},
|
|
548
|
+
get debugMode() {
|
|
549
|
+
return debugMode;
|
|
550
|
+
},
|
|
551
|
+
get stepCount() {
|
|
552
|
+
return bhUniforms.uSteps.value;
|
|
553
|
+
},
|
|
554
|
+
setSize,
|
|
555
|
+
setPreset: applyPreset,
|
|
556
|
+
setQuality,
|
|
557
|
+
setPost({ exposure, bloom: nextBloom } = {}) {
|
|
558
|
+
if (Number.isFinite(exposure)) compositeUniforms.uExp.value = exposure;
|
|
559
|
+
if (Number.isFinite(nextBloom)) compositeUniforms.uBloom.value = nextBloom;
|
|
560
|
+
},
|
|
561
|
+
setDebugMode(mode) {
|
|
562
|
+
debugMode = debugModes.has(mode) ? mode : "final";
|
|
563
|
+
bhUniforms.uDebugMode.value = debugModes.get(debugMode) ?? 0;
|
|
564
|
+
},
|
|
565
|
+
updateCamera(camera) {
|
|
566
|
+
camera.updateMatrixWorld(true);
|
|
567
|
+
camera.getWorldDirection(bhUniforms.uFw.value);
|
|
568
|
+
bhUniforms.uPos.value.copy(camera.position);
|
|
569
|
+
bhUniforms.uRt.value
|
|
570
|
+
.set(1, 0, 0)
|
|
571
|
+
.applyQuaternion(camera.quaternion)
|
|
572
|
+
.normalize();
|
|
573
|
+
bhUniforms.uUp.value
|
|
574
|
+
.set(0, 1, 0)
|
|
575
|
+
.applyQuaternion(camera.quaternion)
|
|
576
|
+
.normalize();
|
|
577
|
+
if (camera.isPerspectiveCamera) {
|
|
578
|
+
bhUniforms.uFov.value = Math.tan(
|
|
579
|
+
THREE.MathUtils.degToRad(camera.fov) * 0.5,
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
update(time, diskTime = time) {
|
|
584
|
+
bhUniforms.uT.value = time;
|
|
585
|
+
bhUniforms.uTd.value = diskTime;
|
|
586
|
+
compositeUniforms.uTn.value = (frame % 1024) * 0.618;
|
|
587
|
+
frame += 1;
|
|
588
|
+
},
|
|
589
|
+
render(renderer) {
|
|
590
|
+
if (debugMode !== "final") {
|
|
591
|
+
runPass(renderer, blackHolePass, null);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
runPass(renderer, blackHolePass, sceneTarget);
|
|
596
|
+
|
|
597
|
+
brightUniforms.tex.value = sceneTarget.texture;
|
|
598
|
+
runPass(renderer, brightPass, halfA);
|
|
599
|
+
|
|
600
|
+
for (let iteration = 0; iteration < 2; iteration += 1) {
|
|
601
|
+
const radius = 1.0 + iteration * 1.2;
|
|
602
|
+
blurUniforms.tex.value = halfA.texture;
|
|
603
|
+
blurUniforms.uDir.value.set(radius / halfA.width, 0);
|
|
604
|
+
runPass(renderer, blurPass, halfB);
|
|
605
|
+
blurUniforms.tex.value = halfB.texture;
|
|
606
|
+
blurUniforms.uDir.value.set(0, radius / halfA.height);
|
|
607
|
+
runPass(renderer, blurPass, halfA);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
blurUniforms.tex.value = halfA.texture;
|
|
611
|
+
blurUniforms.uDir.value.set(1.6 / quarterA.width, 0);
|
|
612
|
+
runPass(renderer, blurPass, quarterA);
|
|
613
|
+
blurUniforms.tex.value = quarterA.texture;
|
|
614
|
+
blurUniforms.uDir.value.set(0, 1.6 / quarterA.height);
|
|
615
|
+
runPass(renderer, blurPass, quarterB);
|
|
616
|
+
|
|
617
|
+
compositeUniforms.tScene.value = sceneTarget.texture;
|
|
618
|
+
compositeUniforms.tB1.value = halfA.texture;
|
|
619
|
+
compositeUniforms.tB2.value = quarterB.texture;
|
|
620
|
+
runPass(renderer, compositePass, null);
|
|
621
|
+
},
|
|
622
|
+
dispose() {
|
|
623
|
+
disposePass(blackHolePass);
|
|
624
|
+
disposePass(brightPass);
|
|
625
|
+
disposePass(blurPass);
|
|
626
|
+
disposePass(compositePass);
|
|
627
|
+
sceneTarget.dispose();
|
|
628
|
+
halfA.dispose();
|
|
629
|
+
halfB.dispose();
|
|
630
|
+
quarterA.dispose();
|
|
631
|
+
quarterB.dispose();
|
|
632
|
+
},
|
|
633
|
+
};
|
|
634
|
+
}
|
|
@@ -517,7 +517,8 @@
|
|
|
517
517
|
"src/three/scenes/08MillersPlanetScene.js": "0b8c160297d69db5b204668357bb18a2286829bd29e9f4be1ea55bf012eac0fe",
|
|
518
518
|
"src/three/scenes/09MannsPlanetScene.js": "cc9b8cc6d91876f3890ef0b7ead6d38b76bea4353012174681966496df50bc5e",
|
|
519
519
|
"src/three/scenes/10SpinDockingScene.js": "63d6bd03083a6caf90110adb99aa379e48faac95780ca033d58fe6eb54b6d47a",
|
|
520
|
-
"src/three/scenes/11GargantuaSlingshotScene.js": "ab7b3d76323061ac8ed7092ab4cf8492a6821b95e5217a08833d083a77973382"
|
|
520
|
+
"src/three/scenes/11GargantuaSlingshotScene.js": "ab7b3d76323061ac8ed7092ab4cf8492a6821b95e5217a08833d083a77973382",
|
|
521
|
+
"blackhole.html": "da289ff15c8ea31db03efefdcb4b8dbbe8cd3a83095a329d3835e4b52ef926bb"
|
|
521
522
|
},
|
|
522
523
|
"assets": {
|
|
523
524
|
"public/textures/noise_deep.png": "bd5a1339eff491cc769c7832013ad29051afa43b7e64bd6ba799bd83fc33d729"
|