react-native-webrtc-kaleidoscope 2.3.0 → 2.5.0
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/README.md +12 -3
- package/android/src/main/java/com/simiancraft/kaleidoscope/gpu/ShadersGenerated.kt +354 -0
- package/catalog/composites/clouds/clouds.thumb.webp +0 -0
- package/catalog/composites/corporate-blobs/corporate-blobs.thumb.webp +0 -0
- package/catalog/composites/fairy-cave/fairy-cave.thumb.webp +0 -0
- package/catalog/composites/fairy-grotto/fairy-grotto.thumb.webp +0 -0
- package/catalog/composites/fairy-hollow/fairy-hollow.thumb.webp +0 -0
- package/catalog/composites/nebula/nebula.thumb.webp +0 -0
- package/catalog/composites/observation-deck/observation-deck.thumb.webp +0 -0
- package/catalog/composites/simianlights/simianlights.thumb.webp +0 -0
- package/catalog/composites/underwater/underwater.thumb.webp +0 -0
- package/catalog/composites/wizard-tower/wizard-tower.thumb.webp +0 -0
- package/catalog/composites/wizard-tower-night/wizard-tower-night.thumb.webp +0 -0
- package/catalog/images/debug/debug-resolutions.thumb.webp +0 -0
- package/catalog/images/home/home-dark.thumb.webp +0 -0
- package/catalog/images/home/home-light.thumb.webp +0 -0
- package/catalog/images/nature/landscape-dark.thumb.webp +0 -0
- package/catalog/images/nature/landscape-light.thumb.webp +0 -0
- package/catalog/images/office/office-dark.thumb.webp +0 -0
- package/catalog/images/office/office-light.thumb.webp +0 -0
- package/catalog/images/sci-fi/sci-fi-light.thumb.webp +0 -0
- package/catalog/images/simiancraft/simiancraft-dark.thumb.webp +0 -0
- package/catalog/images/simiancraft/simiancraft-light.thumb.webp +0 -0
- package/catalog/images/underwater/oceanscape-dark.thumb.webp +0 -0
- package/catalog/shaders/aurora-silk/aurora-silk.form.tsx +35 -0
- package/catalog/shaders/aurora-silk/aurora-silk.frag +106 -0
- package/catalog/shaders/aurora-silk/aurora-silk.ts +92 -0
- package/catalog/shaders/halftone-waves/halftone-waves.form.tsx +35 -0
- package/catalog/shaders/halftone-waves/halftone-waves.frag +84 -0
- package/catalog/shaders/halftone-waves/halftone-waves.ts +100 -0
- package/catalog/shaders/index.ts +16 -0
- package/catalog/shaders/kaleidoscope/kaleidoscope.form.tsx +35 -0
- package/catalog/shaders/kaleidoscope/kaleidoscope.frag +93 -0
- package/catalog/shaders/kaleidoscope/kaleidoscope.ts +81 -0
- package/catalog/shaders/neo-memphis/neo-memphis.form.tsx +35 -0
- package/catalog/shaders/neo-memphis/neo-memphis.frag +152 -0
- package/catalog/shaders/neo-memphis/neo-memphis.ts +84 -0
- package/dist/catalog/composites/clouds/clouds.thumb.webp +0 -0
- package/dist/catalog/composites/corporate-blobs/corporate-blobs.thumb.webp +0 -0
- package/dist/catalog/composites/fairy-cave/fairy-cave.thumb.webp +0 -0
- package/dist/catalog/composites/fairy-grotto/fairy-grotto.thumb.webp +0 -0
- package/dist/catalog/composites/fairy-hollow/fairy-hollow.thumb.webp +0 -0
- package/dist/catalog/composites/nebula/nebula.thumb.webp +0 -0
- package/dist/catalog/composites/observation-deck/observation-deck.thumb.webp +0 -0
- package/dist/catalog/composites/simianlights/simianlights.thumb.webp +0 -0
- package/dist/catalog/composites/underwater/underwater.thumb.webp +0 -0
- package/dist/catalog/composites/wizard-tower/wizard-tower.thumb.webp +0 -0
- package/dist/catalog/composites/wizard-tower-night/wizard-tower-night.thumb.webp +0 -0
- package/dist/catalog/images/debug/debug-resolutions.thumb.webp +0 -0
- package/dist/catalog/images/home/home-dark.thumb.webp +0 -0
- package/dist/catalog/images/home/home-light.thumb.webp +0 -0
- package/dist/catalog/images/nature/landscape-dark.thumb.webp +0 -0
- package/dist/catalog/images/nature/landscape-light.thumb.webp +0 -0
- package/dist/catalog/images/office/office-dark.thumb.webp +0 -0
- package/dist/catalog/images/office/office-light.thumb.webp +0 -0
- package/dist/catalog/images/sci-fi/sci-fi-light.thumb.webp +0 -0
- package/dist/catalog/images/simiancraft/simiancraft-dark.thumb.webp +0 -0
- package/dist/catalog/images/simiancraft/simiancraft-light.thumb.webp +0 -0
- package/dist/catalog/images/underwater/oceanscape-dark.thumb.webp +0 -0
- package/dist/catalog/shaders/aurora-silk/aurora-silk.d.ts +26 -0
- package/dist/catalog/shaders/aurora-silk/aurora-silk.d.ts.map +1 -0
- package/dist/catalog/shaders/aurora-silk/aurora-silk.form.d.ts +3 -0
- package/dist/catalog/shaders/aurora-silk/aurora-silk.form.d.ts.map +1 -0
- package/dist/catalog/shaders/aurora-silk/aurora-silk.form.js +13 -0
- package/dist/catalog/shaders/aurora-silk/aurora-silk.form.js.map +1 -0
- package/dist/catalog/shaders/aurora-silk/aurora-silk.js +70 -0
- package/dist/catalog/shaders/aurora-silk/aurora-silk.js.map +1 -0
- package/dist/catalog/shaders/halftone-waves/halftone-waves.d.ts +26 -0
- package/dist/catalog/shaders/halftone-waves/halftone-waves.d.ts.map +1 -0
- package/dist/catalog/shaders/halftone-waves/halftone-waves.form.d.ts +3 -0
- package/dist/catalog/shaders/halftone-waves/halftone-waves.form.d.ts.map +1 -0
- package/dist/catalog/shaders/halftone-waves/halftone-waves.form.js +13 -0
- package/dist/catalog/shaders/halftone-waves/halftone-waves.form.js.map +1 -0
- package/dist/catalog/shaders/halftone-waves/halftone-waves.js +78 -0
- package/dist/catalog/shaders/halftone-waves/halftone-waves.js.map +1 -0
- package/dist/catalog/shaders/index.d.ts +16 -0
- package/dist/catalog/shaders/index.d.ts.map +1 -1
- package/dist/catalog/shaders/index.js +9 -1
- package/dist/catalog/shaders/index.js.map +1 -1
- package/dist/catalog/shaders/kaleidoscope/kaleidoscope.d.ts +24 -0
- package/dist/catalog/shaders/kaleidoscope/kaleidoscope.d.ts.map +1 -0
- package/dist/catalog/shaders/kaleidoscope/kaleidoscope.form.d.ts +3 -0
- package/dist/catalog/shaders/kaleidoscope/kaleidoscope.form.d.ts.map +1 -0
- package/dist/catalog/shaders/kaleidoscope/kaleidoscope.form.js +14 -0
- package/dist/catalog/shaders/kaleidoscope/kaleidoscope.form.js.map +1 -0
- package/dist/catalog/shaders/kaleidoscope/kaleidoscope.js +61 -0
- package/dist/catalog/shaders/kaleidoscope/kaleidoscope.js.map +1 -0
- package/dist/catalog/shaders/neo-memphis/neo-memphis.d.ts +26 -0
- package/dist/catalog/shaders/neo-memphis/neo-memphis.d.ts.map +1 -0
- package/dist/catalog/shaders/neo-memphis/neo-memphis.form.d.ts +3 -0
- package/dist/catalog/shaders/neo-memphis/neo-memphis.form.d.ts.map +1 -0
- package/dist/catalog/shaders/neo-memphis/neo-memphis.form.js +13 -0
- package/dist/catalog/shaders/neo-memphis/neo-memphis.form.js.map +1 -0
- package/dist/catalog/shaders/neo-memphis/neo-memphis.js +62 -0
- package/dist/catalog/shaders/neo-memphis/neo-memphis.js.map +1 -0
- package/dist/web-driver/shaders.generated.d.ts +4 -0
- package/dist/web-driver/shaders.generated.d.ts.map +1 -1
- package/dist/web-driver/shaders.generated.js +351 -1
- package/dist/web-driver/shaders.generated.js.map +1 -1
- package/ios/KaleidoscopeModule/shaders/GENERATIVE.txt +4 -0
- package/ios/KaleidoscopeModule/shaders/SHADERS.txt +4 -0
- package/ios/KaleidoscopeModule/shaders/aurora-silk.metalsrc +51 -0
- package/ios/KaleidoscopeModule/shaders/halftone-waves.metalsrc +41 -0
- package/ios/KaleidoscopeModule/shaders/kaleidoscope.metalsrc +45 -0
- package/ios/KaleidoscopeModule/shaders/neo-memphis.metalsrc +181 -0
- package/package.json +36 -3
- package/tools/thumbnails/book-loader.ts +104 -0
- package/tools/thumbnails/make-thumbnails.ts +287 -0
- package/tools/thumbnails/office-fixture.webp +0 -0
- package/tools/thumbnails/render-page.ts +259 -0
package/README.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
<img src="./docs/kaleidoscope-logo.png" alt="react-native-webrtc-kaleidoscope logo" width="180" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://simiancraft.github.io/react-native-webrtc-kaleidoscope/">
|
|
7
|
+
<img src="https://img.shields.io/badge/▶%20Live%20demo-blur%20yourself%2C%20swap%20the%20room-8b5cf6?style=for-the-badge" alt="Live demo" />
|
|
8
|
+
</a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<sub>Needs a Chromium-based browser (Chrome or Edge) and webcam permission; Safari and Firefox fall back to the unprocessed track.</sub>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
5
15
|
# react-native-webrtc-kaleidoscope
|
|
6
16
|
|
|
7
17
|
[](#status)
|
|
@@ -22,7 +32,7 @@
|
|
|
22
32
|
|
|
23
33
|
## Status
|
|
24
34
|
|
|
25
|
-
**Active development; not yet production-ready.** Published to npm (the badge shows the current version, which tracks release automation rather than maturity).
|
|
35
|
+
**Active development; not yet production-ready.** Published to npm (the badge shows the current version, which tracks release automation rather than maturity). The [live web demo](https://simiancraft.github.io/react-native-webrtc-kaleidoscope/) runs every effect in the browser; native is exercised via the dev-client demo in `demo/`.
|
|
26
36
|
|
|
27
37
|
### What works today
|
|
28
38
|
|
|
@@ -42,7 +52,6 @@
|
|
|
42
52
|
### Coming soon
|
|
43
53
|
|
|
44
54
|
- **Animated image backgrounds**: a bundled still that moves (beyond the procedural shaders, which already render behind the person today). Same composite path; the new piece is a per-effect background producer for animated images.
|
|
45
|
-
- A careful pass over the npm presentation, install docs, and demo polish before any "we recommend you use this" framing.
|
|
46
55
|
|
|
47
56
|
## Install
|
|
48
57
|
|
|
@@ -317,6 +326,7 @@ The API surface is the same across platforms, but the runtimes differ in ways wo
|
|
|
317
326
|
- **Background presets ship as tree-shakeable files.** The bundled backgrounds (see [Background presets](#background-presets)) are importable per image: `import { officeDark } from 'react-native-webrtc-kaleidoscope/images/office/office-dark'`. Each image is its own file behind its own subpath export, and the package sets `sideEffects: false`, so an unused preset is dropped by web bundlers; since Metro doesn't tree-shake, it is simply never imported on native. Web resolves the bundled WebP to a URL; native loads its own bundled copy by name. Web also still accepts an arbitrary image URL or data URI. See [`catalog/images/README.md`](./catalog/images/README.md).
|
|
318
327
|
- **Segmentation model on web.** The web compositor loads MediaPipe Selfie Segmentation from the jsdelivr CDN (`cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation`) on first use. A strict Content-Security-Policy must allow that origin for `script-src`, `connect-src`, and the WASM fetch, and the effects do not work offline. The `transform` ops need no model.
|
|
319
328
|
- **Browser support on web.** Effects use Insertable Streams (`MediaStreamTrackProcessor` and `MediaStreamTrackGenerator`), which ship in Chromium-based browsers (Chrome, Edge); Safari and Firefox lack the API, so the effects throw a clear capability error and the demo falls back to the unprocessed track.
|
|
329
|
+
- **Android revokes the camera ~60 s into the background.** Android 11+ disables camera access for backgrounded apps by device policy (`ERROR_CAMERA_DISABLED`); `react-native-webrtc` logs the event but never restarts capture, so after a long background the preview stays black on resume and no effect or preset change can recover it (the frame source is dead, not the pipeline). Re-acquire `getUserMedia` when the app returns from the background; the demo's [`use-loopback-stream.ts`](./demo/src/use-loopback-stream.ts) shows the `AppState` pattern, and effects re-bind to the new track through the normal verbs.
|
|
320
330
|
|
|
321
331
|
## What this isn't
|
|
322
332
|
|
|
@@ -352,7 +362,6 @@ See [`PATTERNS.md`](./PATTERNS.md) for the file-layout conventions, texture-orie
|
|
|
352
362
|
- [catalog/shaders/README.md](./catalog/shaders/README.md): adding and extending shaders.
|
|
353
363
|
- [SECURITY.md](./SECURITY.md): security policy and reporting.
|
|
354
364
|
- [NOTICE.md](./NOTICE.md): third-party attributions.
|
|
355
|
-
- Sibling projects: [chromonym](https://github.com/simiancraft/chromonym) and [unitforge](https://github.com/simiancraft/unitforge); same OSS-hygiene template.
|
|
356
365
|
|
|
357
366
|
---
|
|
358
367
|
|
|
@@ -151,6 +151,356 @@ void main() {
|
|
|
151
151
|
// Opaque procedural background; the person is composited over it downstream.
|
|
152
152
|
oColor = vec4(mix(uColorA, uColorB, mixT), 1.0);
|
|
153
153
|
}
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
const val KALEIDOSCOPE_FRAG = """#version 300 es
|
|
157
|
+
precision highp float;
|
|
158
|
+
|
|
159
|
+
uniform float uTime; // seconds, monotonically increasing; range [0, inf)
|
|
160
|
+
uniform vec2 uResolution; // framebuffer size in pixels; both components > 0
|
|
161
|
+
uniform vec3 uColorA; // base palette color (also the calm midpoint pole)
|
|
162
|
+
uniform vec3 uColorB; // second palette color
|
|
163
|
+
uniform vec3 uColorC; // accent color, layered over the A/B field
|
|
164
|
+
uniform float uSegments; // mirror segment count; floor()ed, clamped >= 3
|
|
165
|
+
uniform float uSpeed; // source-field drift rate; 0 freezes the pattern
|
|
166
|
+
uniform float uRotate; // whole-field rotation rate; sign sets direction
|
|
167
|
+
uniform float uZoom; // pattern scale; higher = more rings of detail
|
|
168
|
+
uniform float uCalm; // 0..1 eases contrast at frame center (face zone)
|
|
169
|
+
|
|
170
|
+
in highp vec2 vUv;
|
|
171
|
+
out vec4 oColor;
|
|
172
|
+
|
|
173
|
+
const float TAU = 6.28318530718;
|
|
174
|
+
|
|
175
|
+
void main() {
|
|
176
|
+
// Aspect-correct, screen-centered coordinates (matches plasma/nebula).
|
|
177
|
+
vec2 fragCoord = vUv * uResolution;
|
|
178
|
+
vec2 uv = (fragCoord - 0.5 * uResolution) / uResolution.y;
|
|
179
|
+
float centerDist = length(uv);
|
|
180
|
+
|
|
181
|
+
// Whole-field rotation: the slow "turning the scope" motion.
|
|
182
|
+
float ra = uTime * uRotate;
|
|
183
|
+
float cs = cos(ra);
|
|
184
|
+
float sn = sin(ra);
|
|
185
|
+
uv = mat2(cs, -sn, sn, cs) * uv;
|
|
186
|
+
|
|
187
|
+
// Mirrored polar fold. The 1e-5 nudge keeps atan() off the undefined (0,0)
|
|
188
|
+
// input under Metal; it is far below one pixel at any resolution.
|
|
189
|
+
float r = centerDist * uZoom * (1.0 + 0.06 * sin(uTime * 0.23));
|
|
190
|
+
float seg = TAU / max(3.0, floor(uSegments));
|
|
191
|
+
float a = atan(uv.y, uv.x + 1e-5);
|
|
192
|
+
a = mod(a, seg);
|
|
193
|
+
a = abs(a - seg * 0.5);
|
|
194
|
+
vec2 p = r * vec2(cos(a), sin(a));
|
|
195
|
+
|
|
196
|
+
float t = uTime * uSpeed;
|
|
197
|
+
|
|
198
|
+
// Drifting source field, plasma-class: a few sines of folded position and
|
|
199
|
+
// time. The off-axis moving center in the length() term keeps the pattern
|
|
200
|
+
// evolving (non-repeating) rather than pulsing in place.
|
|
201
|
+
float f1 = sin(p.x * 6.0 + t)
|
|
202
|
+
+ sin((p.x + p.y) * 4.2 - t * 0.7)
|
|
203
|
+
+ sin(length(p - vec2(0.9 + 0.25 * sin(t * 0.31), 0.0)) * 7.0 + t * 1.1);
|
|
204
|
+
float f2 = sin(p.y * 5.0 - t * 0.9 + sin(p.x * 3.1 + t * 0.4));
|
|
205
|
+
float m1 = 0.5 + 0.5 * sin(f1);
|
|
206
|
+
float m2 = smoothstep(0.25, 0.9, 0.5 + 0.5 * sin(f2 + f1 * 0.5));
|
|
207
|
+
|
|
208
|
+
vec3 color = mix(uColorA, uColorB, m1);
|
|
209
|
+
color = mix(color, uColorC, m2 * 0.65);
|
|
210
|
+
|
|
211
|
+
// Thin darkening along both mirror lines sells the cut-glass facets.
|
|
212
|
+
float seam = smoothstep(0.035, 0.0, abs(a - seg * 0.5)) + smoothstep(0.035, 0.0, a);
|
|
213
|
+
color *= 1.0 - 0.18 * seam;
|
|
214
|
+
|
|
215
|
+
// uCalm: ease toward the palette midpoint near frame center. Spatial-only
|
|
216
|
+
// (never scales time per pixel, which would shear the field across the
|
|
217
|
+
// falloff ring).
|
|
218
|
+
vec3 mid = 0.5 * (uColorA + uColorB);
|
|
219
|
+
float calm = uCalm * (1.0 - smoothstep(0.15, 0.62, centerDist));
|
|
220
|
+
color = mix(color, mid, calm * 0.6);
|
|
221
|
+
|
|
222
|
+
// Opaque procedural background; the person is composited over it downstream.
|
|
223
|
+
oColor = vec4(color, 1.0);
|
|
224
|
+
}
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
const val NEO_MEMPHIS_FRAG = """#version 300 es
|
|
228
|
+
precision highp float;
|
|
229
|
+
|
|
230
|
+
uniform float uTime; // seconds, monotonically increasing; range [0, inf)
|
|
231
|
+
uniform vec2 uResolution; // framebuffer size in pixels; both components > 0
|
|
232
|
+
uniform vec3 uBgColor; // field color behind the shapes
|
|
233
|
+
uniform vec3 uColorA; // shape palette color 1
|
|
234
|
+
uniform vec3 uColorB; // shape palette color 2
|
|
235
|
+
uniform vec3 uColorC; // shape palette color 3
|
|
236
|
+
uniform float uScale; // hero-grid cells across frame height
|
|
237
|
+
uniform float uDensity; // probability a cell draws its shape, 0..1
|
|
238
|
+
uniform float uOutline; // probability a shape renders outlined, 0..1
|
|
239
|
+
uniform float uDrift; // scroll + rotation rate; 0 freezes
|
|
240
|
+
uniform float uCalm; // 0..1 fades shapes near frame center (face zone)
|
|
241
|
+
|
|
242
|
+
in highp vec2 vUv;
|
|
243
|
+
out vec4 oColor;
|
|
244
|
+
|
|
245
|
+
const float TAU = 6.28318530718;
|
|
246
|
+
// Antialias half-width in cell units; ~1px at the default pitch.
|
|
247
|
+
const float AA = 0.012;
|
|
248
|
+
|
|
249
|
+
float hash21(vec2 p) {
|
|
250
|
+
p = fract(p * vec2(234.34, 435.345));
|
|
251
|
+
p += dot(p, p + 34.23);
|
|
252
|
+
return fract(p.x * p.y);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
float sdBox(vec2 p, vec2 b) {
|
|
256
|
+
vec2 d = abs(p) - b;
|
|
257
|
+
return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// iq's equilateral triangle, point up, circumradius r.
|
|
261
|
+
float sdTriangle(vec2 p, float r) {
|
|
262
|
+
const float k = 1.7320508;
|
|
263
|
+
p.x = abs(p.x) - r;
|
|
264
|
+
p.y = p.y + r / k;
|
|
265
|
+
if (p.x + k * p.y > 0.0) {
|
|
266
|
+
p = vec2(p.x - k * p.y, -k * p.x - p.y) * 0.5;
|
|
267
|
+
}
|
|
268
|
+
p.x -= clamp(p.x, -2.0 * r, 0.0);
|
|
269
|
+
return -length(p) * sign(p.y);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// One cell layer: returns the shape coverage and writes its color.
|
|
273
|
+
// luv is the layer's scrolled cell-space coordinate; backfill restricts the
|
|
274
|
+
// shape menu to dots and crosses and draws smaller.
|
|
275
|
+
float memphisCell(vec2 luv, float seed, float backfill, float t, out vec3 shapeColor) {
|
|
276
|
+
vec2 id = floor(luv);
|
|
277
|
+
vec2 gv = fract(luv) - 0.5;
|
|
278
|
+
float h = hash21(id + seed);
|
|
279
|
+
shapeColor = uBgColor;
|
|
280
|
+
// Density gate: empty cells cost one hash.
|
|
281
|
+
if (h > uDensity) return 0.0;
|
|
282
|
+
|
|
283
|
+
float h2 = fract(h * 57.31);
|
|
284
|
+
float h3 = fract(h * 113.77);
|
|
285
|
+
float h4 = fract(h * 431.13);
|
|
286
|
+
float h5 = fract(h * 891.71);
|
|
287
|
+
|
|
288
|
+
// Per-cell slow spin and a small bob; both bounded so the shape stays
|
|
289
|
+
// inside its cell (max extent 0.34 + 0.04 < 0.5).
|
|
290
|
+
float ang = h2 * TAU + t * (h3 - 0.5) * 0.8;
|
|
291
|
+
float cs = cos(ang);
|
|
292
|
+
float sn = sin(ang);
|
|
293
|
+
gv -= 0.04 * vec2(sin(t * 0.6 + h * TAU), cos(t * 0.8 + h * TAU));
|
|
294
|
+
gv = mat2(cs, -sn, sn, cs) * gv;
|
|
295
|
+
|
|
296
|
+
float r = mix(0.14, 0.30, h3) * mix(1.0, 0.6, backfill);
|
|
297
|
+
float pick = h4 * 6.0;
|
|
298
|
+
float d;
|
|
299
|
+
if (backfill > 0.5) {
|
|
300
|
+
// Backfill texture: dots and crosses only.
|
|
301
|
+
d = (pick < 3.0)
|
|
302
|
+
? length(gv) - r * 0.45
|
|
303
|
+
: min(sdBox(gv, vec2(r, r * 0.22)), sdBox(gv, vec2(r * 0.22, r)));
|
|
304
|
+
} else if (pick < 1.0) {
|
|
305
|
+
d = length(gv) - r; // disc
|
|
306
|
+
} else if (pick < 2.0) {
|
|
307
|
+
d = abs(length(gv) - r * 0.8) - r * 0.18; // ring
|
|
308
|
+
} else if (pick < 3.0) {
|
|
309
|
+
d = sdTriangle(gv, r); // triangle
|
|
310
|
+
} else if (pick < 4.0) {
|
|
311
|
+
d = min(sdBox(gv, vec2(r, r * 0.24)), sdBox(gv, vec2(r * 0.24, r))); // cross
|
|
312
|
+
} else if (pick < 5.0) {
|
|
313
|
+
d = sdBox(gv, vec2(r * 0.78, r * 0.78)); // box
|
|
314
|
+
} else {
|
|
315
|
+
// Squiggle: a sine-displaced band, clipped to its run length.
|
|
316
|
+
d = max(abs(gv.y - 0.4 * r * sin(gv.x / r * 6.5)) - r * 0.17, abs(gv.x) - r);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
float fill = smoothstep(AA, -AA, d);
|
|
320
|
+
float ring = smoothstep(AA, -AA, abs(d + r * 0.06) - r * 0.09);
|
|
321
|
+
float m = (h5 < uOutline) ? ring : fill;
|
|
322
|
+
|
|
323
|
+
float colorPick = fract(h * 769.23) * 3.0;
|
|
324
|
+
shapeColor = (colorPick < 1.0) ? uColorA : (colorPick < 2.0) ? uColorB : uColorC;
|
|
325
|
+
return m;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
void main() {
|
|
329
|
+
// Aspect-correct, screen-centered coordinates (matches plasma/nebula).
|
|
330
|
+
vec2 fragCoord = vUv * uResolution;
|
|
331
|
+
vec2 uv = (fragCoord - 0.5 * uResolution) / uResolution.y;
|
|
332
|
+
float centerDist = length(uv);
|
|
333
|
+
|
|
334
|
+
float t = uTime * uDrift;
|
|
335
|
+
vec3 color = uBgColor;
|
|
336
|
+
vec3 shapeColor;
|
|
337
|
+
|
|
338
|
+
// Backfill layer first (under the hero shapes): smaller, denser, dimmer.
|
|
339
|
+
vec2 luv1 = uv * uScale * 2.3 + vec2(t * 0.045, t * -0.03) + 51.7;
|
|
340
|
+
float m1 = memphisCell(luv1, 7.0, 1.0, t, shapeColor);
|
|
341
|
+
color = mix(color, mix(uBgColor, shapeColor, 0.55), m1);
|
|
342
|
+
|
|
343
|
+
// Hero layer: the big shapes, scrolling the other way.
|
|
344
|
+
vec2 luv0 = uv * uScale + vec2(t * -0.06, t * 0.04);
|
|
345
|
+
float m0 = memphisCell(luv0, 0.0, 0.0, t, shapeColor);
|
|
346
|
+
|
|
347
|
+
// uCalm: fade shapes (not the field) near frame center.
|
|
348
|
+
float calm = 1.0 - uCalm * (1.0 - smoothstep(0.15, 0.62, centerDist));
|
|
349
|
+
color = mix(color, shapeColor, m0 * calm);
|
|
350
|
+
|
|
351
|
+
// Opaque procedural background; the person is composited over it downstream.
|
|
352
|
+
oColor = vec4(color, 1.0);
|
|
353
|
+
}
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
const val HALFTONE_WAVES_FRAG = """#version 300 es
|
|
357
|
+
precision highp float;
|
|
358
|
+
|
|
359
|
+
uniform float uTime; // seconds, monotonically increasing; range [0, inf)
|
|
360
|
+
uniform vec2 uResolution; // framebuffer size in pixels; both components > 0
|
|
361
|
+
uniform vec3 uPaper; // field color (behind the dots)
|
|
362
|
+
uniform vec3 uInk; // dot color
|
|
363
|
+
uniform float uPitch; // dot-grid cells across frame height
|
|
364
|
+
uniform float uDotSize; // base dot radius in cell units, 0..0.5
|
|
365
|
+
uniform float uWaveAmp; // radius modulation depth, 0..1
|
|
366
|
+
uniform float uSpeed; // wave travel rate; 0 freezes
|
|
367
|
+
uniform float uShape; // dot shape: 0 diamond, 1 circle, 2 square
|
|
368
|
+
uniform float uAngle; // wave direction, radians
|
|
369
|
+
uniform float uCalm; // 0..1 eases the waves at frame center (face zone)
|
|
370
|
+
|
|
371
|
+
in highp vec2 vUv;
|
|
372
|
+
out vec4 oColor;
|
|
373
|
+
|
|
374
|
+
// Antialias half-width in cell units; ~1px at the default pitch.
|
|
375
|
+
const float AA = 0.06;
|
|
376
|
+
|
|
377
|
+
void main() {
|
|
378
|
+
// Aspect-correct, screen-centered coordinates (matches plasma/nebula).
|
|
379
|
+
vec2 fragCoord = vUv * uResolution;
|
|
380
|
+
vec2 uv = (fragCoord - 0.5 * uResolution) / uResolution.y;
|
|
381
|
+
float centerDist = length(uv);
|
|
382
|
+
|
|
383
|
+
vec2 luv = uv * uPitch;
|
|
384
|
+
vec2 id = floor(luv);
|
|
385
|
+
vec2 gv = fract(luv) - 0.5;
|
|
386
|
+
// Wave phase is sampled at the CELL CENTER so a dot's radius is uniform
|
|
387
|
+
// across its own pixels (true halftone, not a warped field).
|
|
388
|
+
vec2 c = (id + 0.5) / uPitch;
|
|
389
|
+
|
|
390
|
+
float t = uTime * uSpeed;
|
|
391
|
+
vec2 dir1 = vec2(cos(uAngle), sin(uAngle));
|
|
392
|
+
vec2 dir2 = vec2(cos(uAngle + 2.2), sin(uAngle + 2.2));
|
|
393
|
+
// Two traveling waves at incommensurate frequencies: interference patterns
|
|
394
|
+
// that drift forever without visibly repeating.
|
|
395
|
+
float w = 0.5 + 0.25 * sin(dot(c, dir1) * 3.1 + t) + 0.25 * sin(dot(c, dir2) * 4.7 - t * 0.77);
|
|
396
|
+
|
|
397
|
+
// uCalm: flatten the modulation toward its midpoint near frame center.
|
|
398
|
+
float calm = uCalm * (1.0 - smoothstep(0.15, 0.62, centerDist));
|
|
399
|
+
w = mix(w, 0.5, calm);
|
|
400
|
+
|
|
401
|
+
float radius = uDotSize * mix(1.0 - uWaveAmp, 1.0 + uWaveAmp, w);
|
|
402
|
+
|
|
403
|
+
// Blended distance metric, pow-free (variable-exponent pow lowers to
|
|
404
|
+
// exp2+log2 on mobile): diamond (L1) -> circle (L2) -> square (Linf).
|
|
405
|
+
vec2 q = abs(gv);
|
|
406
|
+
float dDiamond = (q.x + q.y) * 0.7071;
|
|
407
|
+
float dCircle = length(q);
|
|
408
|
+
float dSquare = max(q.x, q.y);
|
|
409
|
+
float d = (uShape < 1.0)
|
|
410
|
+
? mix(dDiamond, dCircle, clamp(uShape, 0.0, 1.0))
|
|
411
|
+
: mix(dCircle, dSquare, clamp(uShape - 1.0, 0.0, 1.0));
|
|
412
|
+
|
|
413
|
+
float m = smoothstep(radius + AA, radius - AA, d);
|
|
414
|
+
vec3 color = mix(uPaper, uInk, m);
|
|
415
|
+
|
|
416
|
+
// Opaque procedural background; the person is composited over it downstream.
|
|
417
|
+
oColor = vec4(color, 1.0);
|
|
418
|
+
}
|
|
419
|
+
"""
|
|
420
|
+
|
|
421
|
+
const val AURORA_SILK_FRAG = """#version 300 es
|
|
422
|
+
precision highp float;
|
|
423
|
+
|
|
424
|
+
uniform float uTime; // seconds, monotonically increasing; range [0, inf)
|
|
425
|
+
uniform vec2 uResolution; // framebuffer size in pixels; both components > 0
|
|
426
|
+
uniform vec3 uColorLow; // gradient color at the flow's low side
|
|
427
|
+
uniform vec3 uColorHigh; // gradient color at the flow's high side
|
|
428
|
+
uniform vec3 uRibbonColor; // ribbon tint; shades blend toward uColorHigh
|
|
429
|
+
uniform float uRibbons; // visible ribbon count, 1..5 (MAX_RIBBONS)
|
|
430
|
+
uniform float uSoftness; // ribbon edge softness, 0..1
|
|
431
|
+
uniform float uAngle; // flow direction, radians
|
|
432
|
+
uniform float uSpeed; // drift rate; 0 freezes
|
|
433
|
+
uniform float uStyle; // 0 flat paper-cut .. 1 glowing silk
|
|
434
|
+
uniform float uCalm; // 0..1 eases ribbons at frame center (face zone)
|
|
435
|
+
|
|
436
|
+
in highp vec2 vUv;
|
|
437
|
+
out vec4 oColor;
|
|
438
|
+
|
|
439
|
+
const int MAX_RIBBONS = 5;
|
|
440
|
+
|
|
441
|
+
float hash11(float n) {
|
|
442
|
+
return fract(sin(n) * 43758.5453123);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// 1D value noise: smooth, cheap, non-repeating drift source per ribbon.
|
|
446
|
+
float vnoise(float x, float seed) {
|
|
447
|
+
float i = floor(x);
|
|
448
|
+
float f = fract(x);
|
|
449
|
+
float a = hash11(i + seed);
|
|
450
|
+
float b = hash11(i + 1.0 + seed);
|
|
451
|
+
return mix(a, b, f * f * (3.0 - 2.0 * f));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
void main() {
|
|
455
|
+
// Aspect-correct, screen-centered coordinates (matches plasma/nebula).
|
|
456
|
+
vec2 fragCoord = vUv * uResolution;
|
|
457
|
+
vec2 uv = (fragCoord - 0.5 * uResolution) / uResolution.y;
|
|
458
|
+
float centerDist = length(uv);
|
|
459
|
+
|
|
460
|
+
// Rotate into flow space: ribbons run along q.x, stack along q.y.
|
|
461
|
+
float cs = cos(uAngle);
|
|
462
|
+
float sn = sin(uAngle);
|
|
463
|
+
vec2 q = mat2(cs, -sn, sn, cs) * uv;
|
|
464
|
+
|
|
465
|
+
float t = uTime * uSpeed;
|
|
466
|
+
|
|
467
|
+
// Base: soft two-stop gradient across the stacking axis, with a slow
|
|
468
|
+
// breathing tilt so the field is alive even at uRibbons = 0 edge cases.
|
|
469
|
+
float g = clamp(q.y * 0.85 + 0.5 + 0.04 * sin(t * 0.17), 0.0, 1.0);
|
|
470
|
+
vec3 color = mix(uColorLow, uColorHigh, g);
|
|
471
|
+
|
|
472
|
+
float ribbons = clamp(uRibbons, 0.0, float(MAX_RIBBONS));
|
|
473
|
+
float calm = 1.0 - uCalm * (1.0 - smoothstep(0.15, 0.62, centerDist));
|
|
474
|
+
|
|
475
|
+
for (int i = 0; i < MAX_RIBBONS; i++) {
|
|
476
|
+
float fi = float(i);
|
|
477
|
+
if (fi >= ribbons) break;
|
|
478
|
+
|
|
479
|
+
// Stack centers across the frame, each with its own slow vertical sway.
|
|
480
|
+
float center = -0.42 + 0.84 * (fi + 0.5) / ribbons + 0.07 * sin(t * 0.19 + fi * 1.7);
|
|
481
|
+
// Lateral warp: low-frequency noise plus one sine, per-ribbon phase and
|
|
482
|
+
// rate so the bands never move in lockstep.
|
|
483
|
+
float warp = (vnoise(q.x * 1.4 + t * (0.1 + 0.04 * fi), fi * 17.0) - 0.5) * 0.5
|
|
484
|
+
+ 0.1 * sin(q.x * 2.3 + t * (0.26 + 0.06 * fi) + fi * 2.1);
|
|
485
|
+
float dy = abs(q.y - (center + warp));
|
|
486
|
+
|
|
487
|
+
float widthR = mix(0.06, 0.15, hash11(fi * 7.3 + 1.0));
|
|
488
|
+
float soft = mix(0.008, widthR * 1.6, uSoftness);
|
|
489
|
+
float band = (1.0 - smoothstep(widthR - soft, widthR + soft, dy)) * calm;
|
|
490
|
+
|
|
491
|
+
// Ribbon shade: deeper tints at the back of the stack.
|
|
492
|
+
vec3 rc = mix(uRibbonColor, uColorHigh, fi / float(MAX_RIBBONS) * 0.6);
|
|
493
|
+
|
|
494
|
+
// uStyle blends two composites of the same band: flat paint-over vs
|
|
495
|
+
// additive glow.
|
|
496
|
+
vec3 flat_ = mix(color, rc, band * 0.85);
|
|
497
|
+
vec3 glow = color + rc * band * 0.4;
|
|
498
|
+
color = mix(flat_, glow, uStyle);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Opaque procedural background; the person is composited over it downstream.
|
|
502
|
+
oColor = vec4(color, 1.0);
|
|
503
|
+
}
|
|
154
504
|
"""
|
|
155
505
|
|
|
156
506
|
const val CLOUDS_FRAG = """#version 300 es
|
|
@@ -1235,6 +1585,10 @@ void main() {
|
|
|
1235
1585
|
// an entry here automatically.
|
|
1236
1586
|
val GENERATIVE: Map<String, String> = mapOf(
|
|
1237
1587
|
"plasma" to PLASMA_FRAG,
|
|
1588
|
+
"kaleidoscope" to KALEIDOSCOPE_FRAG,
|
|
1589
|
+
"neo-memphis" to NEO_MEMPHIS_FRAG,
|
|
1590
|
+
"halftone-waves" to HALFTONE_WAVES_FRAG,
|
|
1591
|
+
"aurora-silk" to AURORA_SILK_FRAG,
|
|
1238
1592
|
"clouds" to CLOUDS_FRAG,
|
|
1239
1593
|
"nebula" to NEBULA_FRAG,
|
|
1240
1594
|
"godrays" to GODRAYS_FRAG,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Aurora-silk's editor form: the shader OWNS its control layout (the plasma
|
|
2
|
+
// pattern). One <Control uniform="…"/> per uniform in declared order.
|
|
3
|
+
// Conventional layer id: "aurora-silk".
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Control,
|
|
7
|
+
ControlForm,
|
|
8
|
+
ControlSection,
|
|
9
|
+
type KaleidoscopeControls,
|
|
10
|
+
} from '../../../src/components/preset-control-panel';
|
|
11
|
+
import { AURORA_SILK_CONTROLS } from './aurora-silk';
|
|
12
|
+
|
|
13
|
+
export function AuroraSilkForm({ uniforms, onPatch, disabled }: KaleidoscopeControls) {
|
|
14
|
+
return (
|
|
15
|
+
<ControlForm
|
|
16
|
+
id="aurora-silk"
|
|
17
|
+
uniforms={uniforms['aurora-silk'] ?? {}}
|
|
18
|
+
onPatch={onPatch}
|
|
19
|
+
disabled={disabled}
|
|
20
|
+
controls={AURORA_SILK_CONTROLS}
|
|
21
|
+
>
|
|
22
|
+
<ControlSection title="aurora-silk">
|
|
23
|
+
<Control uniform="uColorLow" />
|
|
24
|
+
<Control uniform="uColorHigh" />
|
|
25
|
+
<Control uniform="uRibbonColor" />
|
|
26
|
+
<Control uniform="uRibbons" />
|
|
27
|
+
<Control uniform="uSoftness" />
|
|
28
|
+
<Control uniform="uAngle" />
|
|
29
|
+
<Control uniform="uSpeed" />
|
|
30
|
+
<Control uniform="uStyle" />
|
|
31
|
+
<Control uniform="uCalm" />
|
|
32
|
+
</ControlSection>
|
|
33
|
+
</ControlForm>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Aurora silk: translucent ribbon bands warped by low-frequency noise,
|
|
2
|
+
// drifting diagonally over a soft two-stop gradient; spans the late-2000s OS
|
|
3
|
+
// swoosh through the modern SaaS gradient. An opaque BACKGROUND layer
|
|
4
|
+
// (issue #61).
|
|
5
|
+
//
|
|
6
|
+
// Overdrive surface: the palette is the big lever; uStyle morphs the whole
|
|
7
|
+
// aesthetic from flat paper-cut bands (hard edges, paint-over) to glowing
|
|
8
|
+
// silk (soft edges, additive); uRibbons, uAngle, and uSpeed set composition
|
|
9
|
+
// and energy. uCalm eases the ribbons near frame center, where the masked
|
|
10
|
+
// subject's face sits.
|
|
11
|
+
//
|
|
12
|
+
// Cost: a fixed-bound ribbon loop (MAX_RIBBONS = 5, dynamic break on
|
|
13
|
+
// uRibbons) of one 1D value noise (two hashes + a smooth mix) and two sines
|
|
14
|
+
// per ribbon. No 2D noise, no pow.
|
|
15
|
+
//
|
|
16
|
+
// UV convention: matches passthrough.vert. vUv = (0, 0) at bottom-left,
|
|
17
|
+
// (1, 1) at top-right. fragCoord is reconstructed as vUv * uResolution; no
|
|
18
|
+
// gl_FragCoord. Fully procedural: no input texture, zero net texture flips on
|
|
19
|
+
// every runtime.
|
|
20
|
+
//
|
|
21
|
+
// Precision: highp float. The hash11 uses the 43758.5453123 multiplier;
|
|
22
|
+
// mediump bands it (same reasoning as nebula's hash chain).
|
|
23
|
+
|
|
24
|
+
#version 300 es
|
|
25
|
+
precision highp float;
|
|
26
|
+
|
|
27
|
+
uniform float uTime; // seconds, monotonically increasing; range [0, inf)
|
|
28
|
+
uniform vec2 uResolution; // framebuffer size in pixels; both components > 0
|
|
29
|
+
uniform vec3 uColorLow; // gradient color at the flow's low side
|
|
30
|
+
uniform vec3 uColorHigh; // gradient color at the flow's high side
|
|
31
|
+
uniform vec3 uRibbonColor; // ribbon tint; shades blend toward uColorHigh
|
|
32
|
+
uniform float uRibbons; // visible ribbon count, 1..5 (MAX_RIBBONS)
|
|
33
|
+
uniform float uSoftness; // ribbon edge softness, 0..1
|
|
34
|
+
uniform float uAngle; // flow direction, radians
|
|
35
|
+
uniform float uSpeed; // drift rate; 0 freezes
|
|
36
|
+
uniform float uStyle; // 0 flat paper-cut .. 1 glowing silk
|
|
37
|
+
uniform float uCalm; // 0..1 eases ribbons at frame center (face zone)
|
|
38
|
+
|
|
39
|
+
in highp vec2 vUv;
|
|
40
|
+
out vec4 oColor;
|
|
41
|
+
|
|
42
|
+
const int MAX_RIBBONS = 5;
|
|
43
|
+
|
|
44
|
+
float hash11(float n) {
|
|
45
|
+
return fract(sin(n) * 43758.5453123);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 1D value noise: smooth, cheap, non-repeating drift source per ribbon.
|
|
49
|
+
float vnoise(float x, float seed) {
|
|
50
|
+
float i = floor(x);
|
|
51
|
+
float f = fract(x);
|
|
52
|
+
float a = hash11(i + seed);
|
|
53
|
+
float b = hash11(i + 1.0 + seed);
|
|
54
|
+
return mix(a, b, f * f * (3.0 - 2.0 * f));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
void main() {
|
|
58
|
+
// Aspect-correct, screen-centered coordinates (matches plasma/nebula).
|
|
59
|
+
vec2 fragCoord = vUv * uResolution;
|
|
60
|
+
vec2 uv = (fragCoord - 0.5 * uResolution) / uResolution.y;
|
|
61
|
+
float centerDist = length(uv);
|
|
62
|
+
|
|
63
|
+
// Rotate into flow space: ribbons run along q.x, stack along q.y.
|
|
64
|
+
float cs = cos(uAngle);
|
|
65
|
+
float sn = sin(uAngle);
|
|
66
|
+
vec2 q = mat2(cs, -sn, sn, cs) * uv;
|
|
67
|
+
|
|
68
|
+
float t = uTime * uSpeed;
|
|
69
|
+
|
|
70
|
+
// Base: soft two-stop gradient across the stacking axis, with a slow
|
|
71
|
+
// breathing tilt so the field is alive even at uRibbons = 0 edge cases.
|
|
72
|
+
float g = clamp(q.y * 0.85 + 0.5 + 0.04 * sin(t * 0.17), 0.0, 1.0);
|
|
73
|
+
vec3 color = mix(uColorLow, uColorHigh, g);
|
|
74
|
+
|
|
75
|
+
float ribbons = clamp(uRibbons, 0.0, float(MAX_RIBBONS));
|
|
76
|
+
float calm = 1.0 - uCalm * (1.0 - smoothstep(0.15, 0.62, centerDist));
|
|
77
|
+
|
|
78
|
+
for (int i = 0; i < MAX_RIBBONS; i++) {
|
|
79
|
+
float fi = float(i);
|
|
80
|
+
if (fi >= ribbons) break;
|
|
81
|
+
|
|
82
|
+
// Stack centers across the frame, each with its own slow vertical sway.
|
|
83
|
+
float center = -0.42 + 0.84 * (fi + 0.5) / ribbons + 0.07 * sin(t * 0.19 + fi * 1.7);
|
|
84
|
+
// Lateral warp: low-frequency noise plus one sine, per-ribbon phase and
|
|
85
|
+
// rate so the bands never move in lockstep.
|
|
86
|
+
float warp = (vnoise(q.x * 1.4 + t * (0.1 + 0.04 * fi), fi * 17.0) - 0.5) * 0.5
|
|
87
|
+
+ 0.1 * sin(q.x * 2.3 + t * (0.26 + 0.06 * fi) + fi * 2.1);
|
|
88
|
+
float dy = abs(q.y - (center + warp));
|
|
89
|
+
|
|
90
|
+
float widthR = mix(0.06, 0.15, hash11(fi * 7.3 + 1.0));
|
|
91
|
+
float soft = mix(0.008, widthR * 1.6, uSoftness);
|
|
92
|
+
float band = (1.0 - smoothstep(widthR - soft, widthR + soft, dy)) * calm;
|
|
93
|
+
|
|
94
|
+
// Ribbon shade: deeper tints at the back of the stack.
|
|
95
|
+
vec3 rc = mix(uRibbonColor, uColorHigh, fi / float(MAX_RIBBONS) * 0.6);
|
|
96
|
+
|
|
97
|
+
// uStyle blends two composites of the same band: flat paint-over vs
|
|
98
|
+
// additive glow.
|
|
99
|
+
vec3 flat_ = mix(color, rc, band * 0.85);
|
|
100
|
+
vec3 glow = color + rc * band * 0.4;
|
|
101
|
+
color = mix(flat_, glow, uStyle);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Opaque procedural background; the person is composited over it downstream.
|
|
105
|
+
oColor = vec4(color, 1.0);
|
|
106
|
+
}
|