@basementstudio/shader-lab 0.1.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/.biome/plugins/README.md +21 -0
- package/.biome/plugins/no-anchor-element.grit +12 -0
- package/.biome/plugins/no-relative-parent-imports.grit +10 -0
- package/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
- package/.changeset/README.md +17 -0
- package/.changeset/config.json +11 -0
- package/.editorconfig +40 -0
- package/.env.example +81 -0
- package/.gitattributes +19 -0
- package/.github/workflows/canary.yml +80 -0
- package/.github/workflows/ci.yml +37 -0
- package/.github/workflows/release.yml +56 -0
- package/.tldrignore +84 -0
- package/.vscode/extensions.json +20 -0
- package/.vscode/settings.json +105 -0
- package/README.md +119 -0
- package/biome.json +249 -0
- package/bun.lock +1224 -0
- package/next.config.ts +131 -0
- package/package.json +73 -0
- package/packages/shader-lab-react/CHANGELOG.md +9 -0
- package/packages/shader-lab-react/README.md +119 -0
- package/packages/shader-lab-react/assets/patterns/bars/1.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/bars/2.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/bars/3.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/bars/4.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/bars/5.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/bars/6.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/candles/1.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/candles/2.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/candles/3.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/candles/4.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/shapes/1.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/shapes/2.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/shapes/3.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/shapes/4.svg +4 -0
- package/packages/shader-lab-react/assets/patterns/shapes/5.svg +3 -0
- package/packages/shader-lab-react/assets/patterns/shapes/6.svg +4 -0
- package/packages/shader-lab-react/assets/textures/blue-noise.png +0 -0
- package/packages/shader-lab-react/package.json +36 -0
- package/packages/shader-lab-react/scripts/fix-esm-specifiers.mjs +57 -0
- package/packages/shader-lab-react/scripts/prepare-dist.mjs +4 -0
- package/packages/shader-lab-react/src/ambient/three-tsl.d.ts +146 -0
- package/packages/shader-lab-react/src/ambient/three-webgpu.d.ts +51 -0
- package/packages/shader-lab-react/src/easings.ts +4 -0
- package/packages/shader-lab-react/src/index.ts +35 -0
- package/packages/shader-lab-react/src/lib/editor/custom-shader/shared.ts +2 -0
- package/packages/shader-lab-react/src/renderer/ascii-atlas.ts +83 -0
- package/packages/shader-lab-react/src/renderer/ascii-pass.ts +416 -0
- package/packages/shader-lab-react/src/renderer/asset-url.ts +3 -0
- package/packages/shader-lab-react/src/renderer/blend-modes.ts +229 -0
- package/packages/shader-lab-react/src/renderer/contracts.ts +54 -0
- package/packages/shader-lab-react/src/renderer/create-webgpu-renderer.ts +48 -0
- package/packages/shader-lab-react/src/renderer/crt-pass.ts +1040 -0
- package/packages/shader-lab-react/src/renderer/custom-shader-pass.ts +108 -0
- package/packages/shader-lab-react/src/renderer/custom-shader-runtime.ts +309 -0
- package/packages/shader-lab-react/src/renderer/dither-textures.ts +99 -0
- package/packages/shader-lab-react/src/renderer/dithering-pass.ts +322 -0
- package/packages/shader-lab-react/src/renderer/gradient-pass.ts +521 -0
- package/packages/shader-lab-react/src/renderer/halftone-pass.ts +932 -0
- package/packages/shader-lab-react/src/renderer/ink-pass.ts +802 -0
- package/packages/shader-lab-react/src/renderer/live-pass.ts +194 -0
- package/packages/shader-lab-react/src/renderer/media-pass.ts +187 -0
- package/packages/shader-lab-react/src/renderer/media-texture.ts +66 -0
- package/packages/shader-lab-react/src/renderer/particle-grid-pass.ts +389 -0
- package/packages/shader-lab-react/src/renderer/pass-node.ts +209 -0
- package/packages/shader-lab-react/src/renderer/pattern-atlas.ts +133 -0
- package/packages/shader-lab-react/src/renderer/pattern-pass.ts +552 -0
- package/packages/shader-lab-react/src/renderer/pipeline-manager.ts +369 -0
- package/packages/shader-lab-react/src/renderer/pixel-sorting-pass.ts +277 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/color/tonemapping.ts +87 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/cosine-palette.ts +9 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/common.ts +31 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/curl-noise-3d.ts +36 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/curl-noise-4d.ts +36 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/fbm.ts +13 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/perlin-noise-3d.ts +96 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/ridge-noise.ts +24 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/simplex-noise-3d.ts +79 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/simplex-noise-4d.ts +89 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/turbulence.ts +56 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/value-noise-3d.ts +32 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/voronoi-noise-3d.ts +60 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/bloom-edge-pattern.ts +15 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/bloom.ts +11 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/canvas-weave-pattern.ts +24 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/grain-texture-pattern.ts +9 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/repeating-pattern.ts +11 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/atan2.ts +9 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-conj.ts +9 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-cos.ts +10 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-div.ts +11 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-log.ts +7 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-mobius.ts +12 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-mul.ts +9 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-pow.ts +16 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-sin.ts +10 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-sqrt.ts +18 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-tan.ts +12 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-to-polar.ts +10 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/hyperbolic.ts +20 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/index.ts +48 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/rotate.ts +15 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/screen-aspect-uv.ts +15 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-box-2d.ts +6 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-diamond.ts +6 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-rhombus.ts +27 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-sphere.ts +6 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/smax.ts +7 -0
- package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/smin.ts +7 -0
- package/packages/shader-lab-react/src/renderer/text-pass.ts +176 -0
- package/packages/shader-lab-react/src/runtime-clock.ts +42 -0
- package/packages/shader-lab-react/src/runtime-frame.ts +29 -0
- package/packages/shader-lab-react/src/shader-lab-composition.tsx +163 -0
- package/packages/shader-lab-react/src/timeline.ts +283 -0
- package/packages/shader-lab-react/src/types/editor.ts +5 -0
- package/packages/shader-lab-react/src/types.ts +141 -0
- package/packages/shader-lab-react/tsconfig.build.json +8 -0
- package/packages/shader-lab-react/tsconfig.json +21 -0
- package/postcss.config.mjs +5 -0
- package/public/assets/fonts/msdf/geist-mono/GeistMono-Regular-msdf-atlas.png +0 -0
- package/public/assets/fonts/msdf/geist-mono/GeistMono-Regular-msdf.json +1412 -0
- package/public/assets/patterns/bars/1.svg +3 -0
- package/public/assets/patterns/bars/2.svg +3 -0
- package/public/assets/patterns/bars/3.svg +3 -0
- package/public/assets/patterns/bars/4.svg +3 -0
- package/public/assets/patterns/bars/5.svg +3 -0
- package/public/assets/patterns/bars/6.svg +3 -0
- package/public/assets/patterns/candles/1.svg +3 -0
- package/public/assets/patterns/candles/2.svg +3 -0
- package/public/assets/patterns/candles/3.svg +3 -0
- package/public/assets/patterns/candles/4.svg +3 -0
- package/public/assets/patterns/shapes/1.svg +3 -0
- package/public/assets/patterns/shapes/2.svg +3 -0
- package/public/assets/patterns/shapes/3.svg +3 -0
- package/public/assets/patterns/shapes/4.svg +4 -0
- package/public/assets/patterns/shapes/5.svg +3 -0
- package/public/assets/patterns/shapes/6.svg +4 -0
- package/public/fonts/geist/Geist-Mono.woff2 +0 -0
- package/public/textures/blue-noise.png +0 -0
- package/public/textures/crt-mask.png +0 -0
- package/src/app/design/page.tsx +398 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +280 -0
- package/src/app/layout.tsx +89 -0
- package/src/app/page.tsx +20 -0
- package/src/app/robots.ts +13 -0
- package/src/app/sitemap.ts +13 -0
- package/src/components/editor/editor-canvas-viewport.tsx +116 -0
- package/src/components/editor/editor-export-dialog.tsx +1177 -0
- package/src/components/editor/editor-timeline-overlay.tsx +983 -0
- package/src/components/editor/editor-topbar.tsx +287 -0
- package/src/components/editor/layer-sidebar.tsx +738 -0
- package/src/components/editor/properties-sidebar-content.tsx +574 -0
- package/src/components/editor/properties-sidebar-fields.tsx +389 -0
- package/src/components/editor/properties-sidebar-utils.ts +178 -0
- package/src/components/editor/properties-sidebar.tsx +421 -0
- package/src/components/ui/button/index.tsx +57 -0
- package/src/components/ui/color-picker/index.tsx +358 -0
- package/src/components/ui/glass-panel/index.tsx +45 -0
- package/src/components/ui/icon-button/index.tsx +46 -0
- package/src/components/ui/select/index.tsx +136 -0
- package/src/components/ui/slider/index.tsx +192 -0
- package/src/components/ui/toggle/index.tsx +34 -0
- package/src/components/ui/typography/index.tsx +61 -0
- package/src/components/ui/xy-pad/index.tsx +160 -0
- package/src/features/editor/components/editor-export-dialog.module.css +271 -0
- package/src/hooks/use-editor-renderer.ts +182 -0
- package/src/lib/app.ts +6 -0
- package/src/lib/cn.ts +7 -0
- package/src/lib/easings.ts +240 -0
- package/src/lib/editor/config/layer-registry.ts +2434 -0
- package/src/lib/editor/custom-shader/shared.ts +28 -0
- package/src/lib/editor/export.ts +420 -0
- package/src/lib/editor/history.ts +71 -0
- package/src/lib/editor/layers.ts +76 -0
- package/src/lib/editor/parameter-schema.ts +75 -0
- package/src/lib/editor/project-file.ts +145 -0
- package/src/lib/editor/shader-export-snippet.ts +37 -0
- package/src/lib/editor/shader-export.ts +315 -0
- package/src/lib/editor/timeline/evaluate.ts +252 -0
- package/src/lib/editor/view-transform.ts +58 -0
- package/src/lib/fonts.ts +28 -0
- package/src/renderer/ascii-atlas.ts +83 -0
- package/src/renderer/ascii-pass.ts +416 -0
- package/src/renderer/blend-modes.ts +229 -0
- package/src/renderer/contracts.ts +161 -0
- package/src/renderer/create-webgpu-renderer.ts +48 -0
- package/src/renderer/crt-pass.ts +1040 -0
- package/src/renderer/custom-shader-pass.ts +117 -0
- package/src/renderer/custom-shader-runtime.ts +309 -0
- package/src/renderer/dither-textures.ts +99 -0
- package/src/renderer/dithering-pass.ts +322 -0
- package/src/renderer/gradient-pass.ts +520 -0
- package/src/renderer/halftone-pass.ts +932 -0
- package/src/renderer/ink-pass.ts +683 -0
- package/src/renderer/live-pass.ts +194 -0
- package/src/renderer/media-pass.ts +187 -0
- package/src/renderer/media-texture.ts +66 -0
- package/src/renderer/particle-grid-pass.ts +389 -0
- package/src/renderer/pass-node-factory.ts +33 -0
- package/src/renderer/pass-node.ts +209 -0
- package/src/renderer/pattern-atlas.ts +97 -0
- package/src/renderer/pattern-pass.ts +552 -0
- package/src/renderer/pipeline-manager.ts +343 -0
- package/src/renderer/pixel-sorting-pass.ts +277 -0
- package/src/renderer/project-clock.ts +57 -0
- package/src/renderer/shaders/tsl/color/tonemapping.ts +86 -0
- package/src/renderer/shaders/tsl/cosine-palette.ts +8 -0
- package/src/renderer/shaders/tsl/noise/common.ts +30 -0
- package/src/renderer/shaders/tsl/noise/curl-noise-3d.ts +35 -0
- package/src/renderer/shaders/tsl/noise/curl-noise-4d.ts +35 -0
- package/src/renderer/shaders/tsl/noise/fbm.ts +12 -0
- package/src/renderer/shaders/tsl/noise/perlin-noise-3d.ts +97 -0
- package/src/renderer/shaders/tsl/noise/ridge-noise.ts +23 -0
- package/src/renderer/shaders/tsl/noise/simplex-noise-3d.ts +78 -0
- package/src/renderer/shaders/tsl/noise/simplex-noise-4d.ts +88 -0
- package/src/renderer/shaders/tsl/noise/turbulence.ts +55 -0
- package/src/renderer/shaders/tsl/noise/value-noise-3d.ts +31 -0
- package/src/renderer/shaders/tsl/noise/voronoi-noise-3d.ts +59 -0
- package/src/renderer/shaders/tsl/patterns/bloom-edge-pattern.ts +14 -0
- package/src/renderer/shaders/tsl/patterns/bloom.ts +10 -0
- package/src/renderer/shaders/tsl/patterns/canvas-weave-pattern.ts +23 -0
- package/src/renderer/shaders/tsl/patterns/grain-texture-pattern.ts +8 -0
- package/src/renderer/shaders/tsl/patterns/repeating-pattern.ts +10 -0
- package/src/renderer/shaders/tsl/utils/atan2.ts +8 -0
- package/src/renderer/shaders/tsl/utils/complex-conj.ts +8 -0
- package/src/renderer/shaders/tsl/utils/complex-cos.ts +9 -0
- package/src/renderer/shaders/tsl/utils/complex-div.ts +10 -0
- package/src/renderer/shaders/tsl/utils/complex-log.ts +6 -0
- package/src/renderer/shaders/tsl/utils/complex-mobius.ts +11 -0
- package/src/renderer/shaders/tsl/utils/complex-mul.ts +8 -0
- package/src/renderer/shaders/tsl/utils/complex-pow.ts +15 -0
- package/src/renderer/shaders/tsl/utils/complex-sin.ts +9 -0
- package/src/renderer/shaders/tsl/utils/complex-sqrt.ts +17 -0
- package/src/renderer/shaders/tsl/utils/complex-tan.ts +11 -0
- package/src/renderer/shaders/tsl/utils/complex-to-polar.ts +9 -0
- package/src/renderer/shaders/tsl/utils/hyperbolic.ts +19 -0
- package/src/renderer/shaders/tsl/utils/index.ts +47 -0
- package/src/renderer/shaders/tsl/utils/rotate.ts +14 -0
- package/src/renderer/shaders/tsl/utils/screen-aspect-uv.ts +14 -0
- package/src/renderer/shaders/tsl/utils/sd-box-2d.ts +5 -0
- package/src/renderer/shaders/tsl/utils/sd-diamond.ts +5 -0
- package/src/renderer/shaders/tsl/utils/sd-rhombus.ts +26 -0
- package/src/renderer/shaders/tsl/utils/sd-sphere.ts +5 -0
- package/src/renderer/shaders/tsl/utils/smax.ts +7 -0
- package/src/renderer/shaders/tsl/utils/smin.ts +6 -0
- package/src/renderer/text-pass.ts +176 -0
- package/src/store/asset-store.ts +193 -0
- package/src/store/editor-store.ts +223 -0
- package/src/store/history-store.ts +172 -0
- package/src/store/index.ts +31 -0
- package/src/store/layer-store.ts +675 -0
- package/src/store/timeline-store.ts +572 -0
- package/src/types/assets.d.ts +6 -0
- package/src/types/css.d.ts +21 -0
- package/src/types/editor.ts +357 -0
- package/src/types/react.d.ts +15 -0
- package/src/types/three-tsl.d.ts +146 -0
- package/src/types/three-webgpu.d.ts +51 -0
- package/tsconfig.json +49 -0
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
import * as THREE from "three/webgpu"
|
|
2
|
+
import { bloom } from "three/examples/jsm/tsl/display/BloomNode.js"
|
|
3
|
+
import {
|
|
4
|
+
clamp,
|
|
5
|
+
float,
|
|
6
|
+
max,
|
|
7
|
+
mix,
|
|
8
|
+
pow,
|
|
9
|
+
select,
|
|
10
|
+
sin,
|
|
11
|
+
smoothstep,
|
|
12
|
+
texture as tslTexture,
|
|
13
|
+
type TSLNode,
|
|
14
|
+
uniform,
|
|
15
|
+
uv,
|
|
16
|
+
vec2,
|
|
17
|
+
vec3,
|
|
18
|
+
vec4,
|
|
19
|
+
} from "three/tsl"
|
|
20
|
+
import { PassNode } from "@/renderer/pass-node"
|
|
21
|
+
import { loadImageTexture } from "@/renderer/media-texture"
|
|
22
|
+
import { grainTexturePattern } from "@/renderer/shaders/tsl/patterns/grain-texture-pattern"
|
|
23
|
+
import type { LayerParameterValues } from "@/types/editor"
|
|
24
|
+
|
|
25
|
+
type Node = TSLNode
|
|
26
|
+
|
|
27
|
+
const INTERNAL_TARGET_OPTIONS = {
|
|
28
|
+
depthBuffer: false,
|
|
29
|
+
format: THREE.RGBAFormat,
|
|
30
|
+
generateMipmaps: false,
|
|
31
|
+
magFilter: THREE.LinearFilter,
|
|
32
|
+
minFilter: THREE.LinearFilter,
|
|
33
|
+
stencilBuffer: false,
|
|
34
|
+
type: THREE.HalfFloatType,
|
|
35
|
+
} as const
|
|
36
|
+
|
|
37
|
+
function clamp01(value: number): number {
|
|
38
|
+
return Math.max(0, Math.min(1, value))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function hexToRgb(hex: string): [number, number, number] {
|
|
42
|
+
const normalized = hex.trim().replace("#", "")
|
|
43
|
+
const value =
|
|
44
|
+
normalized.length === 3
|
|
45
|
+
? normalized
|
|
46
|
+
.split("")
|
|
47
|
+
.map((entry) => `${entry}${entry}`)
|
|
48
|
+
.join("")
|
|
49
|
+
: normalized.padEnd(6, "0").slice(0, 6)
|
|
50
|
+
|
|
51
|
+
return [
|
|
52
|
+
Number.parseInt(value.slice(0, 2), 16) / 255,
|
|
53
|
+
Number.parseInt(value.slice(2, 4), 16) / 255,
|
|
54
|
+
Number.parseInt(value.slice(4, 6), 16) / 255,
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class InkPass extends PassNode {
|
|
59
|
+
private readonly blurScene: THREE.Scene
|
|
60
|
+
private readonly compositeScene: THREE.Scene
|
|
61
|
+
private readonly copyScene: THREE.Scene
|
|
62
|
+
private readonly orthoCamera: THREE.OrthographicCamera
|
|
63
|
+
private readonly blurMaterial: THREE.MeshBasicNodeMaterial
|
|
64
|
+
private readonly compositeMaterial: THREE.MeshBasicNodeMaterial
|
|
65
|
+
private readonly copyMaterial: THREE.MeshBasicNodeMaterial
|
|
66
|
+
private readonly blurInputNode: Node
|
|
67
|
+
private readonly crispInputNode: Node
|
|
68
|
+
private readonly finalInputNode: Node
|
|
69
|
+
private readonly copyInputNode: Node
|
|
70
|
+
private readonly noiseInputNode: Node
|
|
71
|
+
|
|
72
|
+
private blurSampleNodes: Node[] = []
|
|
73
|
+
private compositeBlurNodes: Node[] = []
|
|
74
|
+
private noiseSampleNodes: Node[] = []
|
|
75
|
+
|
|
76
|
+
private bloomEnabled = false
|
|
77
|
+
private bloomNode: ReturnType<typeof bloom> | null = null
|
|
78
|
+
private readonly bloomIntensityUniform: Node
|
|
79
|
+
private readonly bloomRadiusUniform: Node
|
|
80
|
+
private readonly bloomSoftnessUniform: Node
|
|
81
|
+
private readonly bloomThresholdUniform: Node
|
|
82
|
+
|
|
83
|
+
private readonly backgroundColorUniform: Node
|
|
84
|
+
private readonly coreColorUniform: Node
|
|
85
|
+
private readonly edgeColorUniform: Node
|
|
86
|
+
private readonly midColorUniform: Node
|
|
87
|
+
|
|
88
|
+
private readonly blurStrengthUniform: Node
|
|
89
|
+
private readonly crispBlendUniform: Node
|
|
90
|
+
private readonly directionXUniform: Node
|
|
91
|
+
private readonly directionYUniform: Node
|
|
92
|
+
private readonly dripLengthUniform: Node
|
|
93
|
+
private readonly dripWeightUniform: Node
|
|
94
|
+
private readonly fluidNoiseUniform: Node
|
|
95
|
+
private readonly grainEnabledUniform: Node
|
|
96
|
+
private readonly grainIntensityUniform: Node
|
|
97
|
+
private readonly grainScaleUniform: Node
|
|
98
|
+
private readonly blurSpreadUniform: Node
|
|
99
|
+
private readonly noiseScaleUniform: Node
|
|
100
|
+
private readonly passIndexUniform: Node
|
|
101
|
+
private readonly resolutionWidthUniform: Node
|
|
102
|
+
private readonly resolutionHeightUniform: Node
|
|
103
|
+
private readonly smokeSpeedUniform: Node
|
|
104
|
+
private readonly smokeTurbulenceUniform: Node
|
|
105
|
+
private readonly timeUniform: Node
|
|
106
|
+
|
|
107
|
+
private blurPassCount = 12
|
|
108
|
+
private crispPassCount = 3
|
|
109
|
+
private compositeTarget: THREE.WebGLRenderTarget
|
|
110
|
+
private crispTarget: THREE.WebGLRenderTarget
|
|
111
|
+
private readTarget: THREE.WebGLRenderTarget
|
|
112
|
+
private writeTarget: THREE.WebGLRenderTarget
|
|
113
|
+
private readonly placeholder: THREE.Texture
|
|
114
|
+
private noiseTexture: THREE.Texture | null = null
|
|
115
|
+
private noiseLoadStarted = false
|
|
116
|
+
private needsRefresh = true
|
|
117
|
+
private width = 1
|
|
118
|
+
private height = 1
|
|
119
|
+
private isAnimated = true
|
|
120
|
+
|
|
121
|
+
constructor(layerId: string) {
|
|
122
|
+
super(layerId)
|
|
123
|
+
|
|
124
|
+
this.orthoCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1)
|
|
125
|
+
this.blurScene = new THREE.Scene()
|
|
126
|
+
this.compositeScene = new THREE.Scene()
|
|
127
|
+
this.copyScene = new THREE.Scene()
|
|
128
|
+
|
|
129
|
+
this.placeholder = new THREE.Texture()
|
|
130
|
+
const flippedUv = vec2(uv().x, float(1).sub(uv().y))
|
|
131
|
+
this.blurInputNode = tslTexture(this.placeholder, flippedUv)
|
|
132
|
+
this.crispInputNode = tslTexture(this.placeholder, flippedUv)
|
|
133
|
+
this.finalInputNode = tslTexture(this.placeholder, flippedUv)
|
|
134
|
+
this.copyInputNode = tslTexture(this.placeholder, flippedUv)
|
|
135
|
+
this.noiseInputNode = tslTexture(this.placeholder, flippedUv)
|
|
136
|
+
|
|
137
|
+
this.bloomIntensityUniform = uniform(1.25)
|
|
138
|
+
this.bloomRadiusUniform = uniform(6)
|
|
139
|
+
this.bloomSoftnessUniform = uniform(0.35)
|
|
140
|
+
this.bloomThresholdUniform = uniform(0.6)
|
|
141
|
+
|
|
142
|
+
this.backgroundColorUniform = uniform(new THREE.Vector3(0.039, 0.043, 0.051))
|
|
143
|
+
this.coreColorUniform = uniform(new THREE.Vector3(1.0, 0.992, 0.91))
|
|
144
|
+
this.midColorUniform = uniform(new THREE.Vector3(0.784, 0.961, 0.259))
|
|
145
|
+
this.edgeColorUniform = uniform(new THREE.Vector3(0.0, 0.788, 0.655))
|
|
146
|
+
|
|
147
|
+
this.blurStrengthUniform = uniform(0.02)
|
|
148
|
+
this.crispBlendUniform = uniform(0.75)
|
|
149
|
+
this.directionXUniform = uniform(0.3746)
|
|
150
|
+
this.directionYUniform = uniform(0.9271)
|
|
151
|
+
this.dripLengthUniform = uniform(7.1)
|
|
152
|
+
this.dripWeightUniform = uniform(1.2)
|
|
153
|
+
this.fluidNoiseUniform = uniform(0.2)
|
|
154
|
+
this.grainEnabledUniform = uniform(1)
|
|
155
|
+
this.grainIntensityUniform = uniform(0.3)
|
|
156
|
+
this.grainScaleUniform = uniform(1.5)
|
|
157
|
+
this.blurSpreadUniform = uniform(1.7)
|
|
158
|
+
this.noiseScaleUniform = uniform(1)
|
|
159
|
+
this.passIndexUniform = uniform(0)
|
|
160
|
+
this.resolutionWidthUniform = uniform(1)
|
|
161
|
+
this.resolutionHeightUniform = uniform(1)
|
|
162
|
+
this.smokeSpeedUniform = uniform(0.2)
|
|
163
|
+
this.smokeTurbulenceUniform = uniform(0.25)
|
|
164
|
+
this.timeUniform = uniform(0)
|
|
165
|
+
|
|
166
|
+
this.readTarget = new THREE.WebGLRenderTarget(1, 1, INTERNAL_TARGET_OPTIONS)
|
|
167
|
+
this.writeTarget = new THREE.WebGLRenderTarget(1, 1, INTERNAL_TARGET_OPTIONS)
|
|
168
|
+
this.crispTarget = new THREE.WebGLRenderTarget(1, 1, INTERNAL_TARGET_OPTIONS)
|
|
169
|
+
this.compositeTarget = new THREE.WebGLRenderTarget(1, 1, INTERNAL_TARGET_OPTIONS)
|
|
170
|
+
|
|
171
|
+
this.blurMaterial = new THREE.MeshBasicNodeMaterial()
|
|
172
|
+
this.blurMaterial.colorNode = this.buildBlurNode()
|
|
173
|
+
const blurMesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), this.blurMaterial)
|
|
174
|
+
blurMesh.frustumCulled = false
|
|
175
|
+
this.blurScene.add(blurMesh)
|
|
176
|
+
|
|
177
|
+
this.copyMaterial = new THREE.MeshBasicNodeMaterial()
|
|
178
|
+
this.copyMaterial.colorNode = vec4(this.copyInputNode.rgb, float(1))
|
|
179
|
+
const copyMesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), this.copyMaterial)
|
|
180
|
+
copyMesh.frustumCulled = false
|
|
181
|
+
this.copyScene.add(copyMesh)
|
|
182
|
+
|
|
183
|
+
this.compositeMaterial = new THREE.MeshBasicNodeMaterial()
|
|
184
|
+
this.compositeMaterial.colorNode = this.buildCompositeNode()
|
|
185
|
+
const compositeMesh = new THREE.Mesh(
|
|
186
|
+
new THREE.PlaneGeometry(2, 2),
|
|
187
|
+
this.compositeMaterial,
|
|
188
|
+
)
|
|
189
|
+
compositeMesh.frustumCulled = false
|
|
190
|
+
this.compositeScene.add(compositeMesh)
|
|
191
|
+
|
|
192
|
+
this.rebuildEffectNode()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
override render(
|
|
196
|
+
renderer: THREE.WebGPURenderer,
|
|
197
|
+
inputTexture: THREE.Texture,
|
|
198
|
+
outputTarget: THREE.WebGLRenderTarget,
|
|
199
|
+
time: number,
|
|
200
|
+
delta: number,
|
|
201
|
+
): void {
|
|
202
|
+
this.ensureNoiseTexture()
|
|
203
|
+
|
|
204
|
+
this.blurInputNode.value = inputTexture
|
|
205
|
+
this.noiseInputNode.value = this.noiseTexture ?? this.placeholder
|
|
206
|
+
for (const node of this.blurSampleNodes) {
|
|
207
|
+
node.value = inputTexture
|
|
208
|
+
}
|
|
209
|
+
for (const node of this.noiseSampleNodes) {
|
|
210
|
+
node.value = this.noiseTexture ?? this.placeholder
|
|
211
|
+
}
|
|
212
|
+
this.passIndexUniform.value = 0
|
|
213
|
+
|
|
214
|
+
renderer.setRenderTarget(this.readTarget)
|
|
215
|
+
renderer.render(this.blurScene, this.orthoCamera)
|
|
216
|
+
|
|
217
|
+
if (this.crispPassCount <= 1) {
|
|
218
|
+
this.copyInputNode.value = this.readTarget.texture
|
|
219
|
+
renderer.setRenderTarget(this.crispTarget)
|
|
220
|
+
renderer.render(this.copyScene, this.orthoCamera)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const totalPasses = Math.max(this.blurPassCount, this.crispPassCount)
|
|
224
|
+
let readTarget = this.readTarget
|
|
225
|
+
let writeTarget = this.writeTarget
|
|
226
|
+
|
|
227
|
+
for (let passIndex = 1; passIndex < totalPasses; passIndex += 1) {
|
|
228
|
+
this.blurInputNode.value = readTarget.texture
|
|
229
|
+
for (const node of this.blurSampleNodes) {
|
|
230
|
+
node.value = readTarget.texture
|
|
231
|
+
}
|
|
232
|
+
this.passIndexUniform.value = passIndex
|
|
233
|
+
renderer.setRenderTarget(writeTarget)
|
|
234
|
+
renderer.render(this.blurScene, this.orthoCamera)
|
|
235
|
+
|
|
236
|
+
if (passIndex === this.crispPassCount - 1) {
|
|
237
|
+
this.copyInputNode.value = writeTarget.texture
|
|
238
|
+
renderer.setRenderTarget(this.crispTarget)
|
|
239
|
+
renderer.render(this.copyScene, this.orthoCamera)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const temp = readTarget
|
|
243
|
+
readTarget = writeTarget
|
|
244
|
+
writeTarget = temp
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
this.finalInputNode.value = readTarget.texture
|
|
248
|
+
this.crispInputNode.value = this.crispTarget.texture
|
|
249
|
+
for (const node of this.compositeBlurNodes) {
|
|
250
|
+
node.value = readTarget.texture
|
|
251
|
+
}
|
|
252
|
+
renderer.setRenderTarget(this.compositeTarget)
|
|
253
|
+
renderer.render(this.compositeScene, this.orthoCamera)
|
|
254
|
+
|
|
255
|
+
this.finalInputNode.value = this.compositeTarget.texture
|
|
256
|
+
super.render(renderer, inputTexture, outputTarget, time, delta)
|
|
257
|
+
this.needsRefresh = false
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
protected override beforeRender(time: number): void {
|
|
261
|
+
this.timeUniform.value = time
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
override needsContinuousRender(): boolean {
|
|
265
|
+
return this.isAnimated || this.needsRefresh
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
override updateParams(params: LayerParameterValues): void {
|
|
269
|
+
const angle = (((typeof params.blurDirection === "number" ? params.blurDirection : 68) *
|
|
270
|
+
Math.PI) /
|
|
271
|
+
180)
|
|
272
|
+
|
|
273
|
+
this.directionXUniform.value = Math.cos(angle)
|
|
274
|
+
this.directionYUniform.value = Math.sin(angle)
|
|
275
|
+
this.blurPassCount =
|
|
276
|
+
typeof params.blurPasses === "number" ? Math.max(1, Math.round(params.blurPasses)) : 12
|
|
277
|
+
this.crispPassCount =
|
|
278
|
+
typeof params.crispPasses === "number" ? Math.max(1, Math.round(params.crispPasses)) : 3
|
|
279
|
+
this.blurStrengthUniform.value =
|
|
280
|
+
typeof params.blurStrength === "number" ? Math.max(0.001, params.blurStrength) : 0.02
|
|
281
|
+
this.crispBlendUniform.value =
|
|
282
|
+
typeof params.crispBlend === "number" ? clamp01(params.crispBlend) : 0.75
|
|
283
|
+
this.dripLengthUniform.value =
|
|
284
|
+
typeof params.dripLength === "number" ? Math.max(1, params.dripLength) : 7.1
|
|
285
|
+
this.dripWeightUniform.value =
|
|
286
|
+
typeof params.dripWeight === "number" ? Math.max(0.2, params.dripWeight) : 1.2
|
|
287
|
+
this.fluidNoiseUniform.value =
|
|
288
|
+
typeof params.fluidNoise === "number" ? Math.max(0, params.fluidNoise) : 0.2
|
|
289
|
+
this.noiseScaleUniform.value =
|
|
290
|
+
typeof params.noiseScale === "number" ? Math.max(0.5, params.noiseScale) : 1
|
|
291
|
+
this.smokeSpeedUniform.value =
|
|
292
|
+
typeof params.smokeSpeed === "number" ? Math.max(0, params.smokeSpeed) : 0.2
|
|
293
|
+
this.smokeTurbulenceUniform.value =
|
|
294
|
+
typeof params.smokeTurbulence === "number" ? Math.max(0, params.smokeTurbulence) : 0.25
|
|
295
|
+
this.blurSpreadUniform.value =
|
|
296
|
+
typeof params.blurSpread === "number" ? Math.max(0.5, params.blurSpread) : 1.7
|
|
297
|
+
this.grainEnabledUniform.value = params.grainEnabled === false ? 0 : 1
|
|
298
|
+
this.grainIntensityUniform.value =
|
|
299
|
+
typeof params.grainIntensity === "number" ? clamp01(params.grainIntensity) : 0.3
|
|
300
|
+
this.grainScaleUniform.value =
|
|
301
|
+
typeof params.grainScale === "number" ? Math.max(0.5, params.grainScale) : 1.5
|
|
302
|
+
|
|
303
|
+
this.setColorUniform(
|
|
304
|
+
this.backgroundColorUniform,
|
|
305
|
+
typeof params.backgroundColor === "string" ? params.backgroundColor : "#0a0b0d",
|
|
306
|
+
)
|
|
307
|
+
this.setColorUniform(
|
|
308
|
+
this.coreColorUniform,
|
|
309
|
+
typeof params.coreColor === "string" ? params.coreColor : "#fffde8",
|
|
310
|
+
)
|
|
311
|
+
this.setColorUniform(
|
|
312
|
+
this.midColorUniform,
|
|
313
|
+
typeof params.midColor === "string" ? params.midColor : "#c8f542",
|
|
314
|
+
)
|
|
315
|
+
this.setColorUniform(
|
|
316
|
+
this.edgeColorUniform,
|
|
317
|
+
typeof params.edgeColor === "string" ? params.edgeColor : "#00c9a7",
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
const nextBloomEnabled = params.bloomEnabled === true
|
|
321
|
+
const nextBloomIntensity =
|
|
322
|
+
typeof params.bloomIntensity === "number" ? Math.max(0, params.bloomIntensity) : 1.25
|
|
323
|
+
const nextBloomThreshold =
|
|
324
|
+
typeof params.bloomThreshold === "number" ? clamp01(params.bloomThreshold) : 0.6
|
|
325
|
+
const nextBloomRadius =
|
|
326
|
+
typeof params.bloomRadius === "number" ? Math.max(0, params.bloomRadius) : 6
|
|
327
|
+
const nextBloomSoftness =
|
|
328
|
+
typeof params.bloomSoftness === "number" ? clamp01(params.bloomSoftness) : 0.35
|
|
329
|
+
|
|
330
|
+
this.bloomIntensityUniform.value = nextBloomIntensity
|
|
331
|
+
this.bloomRadiusUniform.value = nextBloomRadius
|
|
332
|
+
this.bloomSoftnessUniform.value = nextBloomSoftness
|
|
333
|
+
this.bloomThresholdUniform.value = nextBloomThreshold
|
|
334
|
+
|
|
335
|
+
if (nextBloomEnabled !== this.bloomEnabled) {
|
|
336
|
+
this.bloomEnabled = nextBloomEnabled
|
|
337
|
+
this.rebuildEffectNode()
|
|
338
|
+
} else if (this.bloomNode) {
|
|
339
|
+
this.bloomNode.strength.value = nextBloomIntensity
|
|
340
|
+
this.bloomNode.radius.value = this.normalizeBloomRadius(nextBloomRadius)
|
|
341
|
+
this.bloomNode.threshold.value = nextBloomThreshold
|
|
342
|
+
this.bloomNode.smoothWidth.value = this.normalizeBloomSoftness(nextBloomSoftness)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
this.isAnimated = (this.smokeSpeedUniform.value as number) > 0
|
|
346
|
+
this.needsRefresh = true
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
override resize(width: number, height: number): void {
|
|
350
|
+
this.width = Math.max(1, width)
|
|
351
|
+
this.height = Math.max(1, height)
|
|
352
|
+
this.resolutionWidthUniform.value = this.width
|
|
353
|
+
this.resolutionHeightUniform.value = this.height
|
|
354
|
+
this.readTarget.setSize(this.width, this.height)
|
|
355
|
+
this.writeTarget.setSize(this.width, this.height)
|
|
356
|
+
this.crispTarget.setSize(this.width, this.height)
|
|
357
|
+
this.compositeTarget.setSize(this.width, this.height)
|
|
358
|
+
this.needsRefresh = true
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
override dispose(): void {
|
|
362
|
+
this.disposeBloomNode()
|
|
363
|
+
this.noiseTexture?.dispose()
|
|
364
|
+
this.readTarget?.dispose()
|
|
365
|
+
this.writeTarget?.dispose()
|
|
366
|
+
this.crispTarget?.dispose()
|
|
367
|
+
this.compositeTarget?.dispose()
|
|
368
|
+
this.blurMaterial?.dispose()
|
|
369
|
+
this.copyMaterial?.dispose()
|
|
370
|
+
this.compositeMaterial?.dispose()
|
|
371
|
+
this.placeholder.dispose()
|
|
372
|
+
super.dispose()
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
protected override buildEffectNode(): Node {
|
|
376
|
+
if (!this.finalInputNode) {
|
|
377
|
+
return vec4(float(0), float(0), float(0), float(1))
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
this.disposeBloomNode()
|
|
381
|
+
this.bloomNode = null
|
|
382
|
+
|
|
383
|
+
const baseColor = vec3(this.finalInputNode.r, this.finalInputNode.g, this.finalInputNode.b)
|
|
384
|
+
|
|
385
|
+
if (!this.bloomEnabled) {
|
|
386
|
+
return vec4(baseColor, float(1))
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
this.bloomNode = bloom(
|
|
390
|
+
vec4(baseColor, float(1)),
|
|
391
|
+
this.bloomIntensityUniform.value as number,
|
|
392
|
+
this.normalizeBloomRadius(this.bloomRadiusUniform.value as number),
|
|
393
|
+
this.bloomThresholdUniform.value as number,
|
|
394
|
+
)
|
|
395
|
+
this.bloomNode.smoothWidth.value = this.normalizeBloomSoftness(
|
|
396
|
+
this.bloomSoftnessUniform.value as number,
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
return vec4(
|
|
400
|
+
clamp(
|
|
401
|
+
baseColor.add(this.getBloomTextureNode().rgb),
|
|
402
|
+
vec3(float(0), float(0), float(0)),
|
|
403
|
+
vec3(float(1), float(1), float(1)),
|
|
404
|
+
),
|
|
405
|
+
float(1),
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private buildBlurNode(): Node {
|
|
410
|
+
this.blurSampleNodes = []
|
|
411
|
+
this.noiseSampleNodes = []
|
|
412
|
+
|
|
413
|
+
const texUv = vec2(uv().x, float(1).sub(uv().y))
|
|
414
|
+
const texelSize = vec2(
|
|
415
|
+
float(1).div(this.resolutionWidthUniform),
|
|
416
|
+
float(1).div(this.resolutionHeightUniform),
|
|
417
|
+
)
|
|
418
|
+
const original = this.blurInputNode
|
|
419
|
+
const originalIntensity = max(max(original.r, original.g), original.b)
|
|
420
|
+
const rotatedUv = vec2(
|
|
421
|
+
texUv.x.mul(float(Math.cos(0.7854))).sub(texUv.y.mul(float(Math.sin(0.7854)))),
|
|
422
|
+
texUv.x.mul(float(Math.sin(0.7854))).add(texUv.y.mul(float(Math.cos(0.7854)))),
|
|
423
|
+
)
|
|
424
|
+
const timeOffset1 = vec2(
|
|
425
|
+
this.timeUniform.mul(this.smokeSpeedUniform).mul(0.3),
|
|
426
|
+
this.timeUniform.mul(this.smokeSpeedUniform).mul(0.15),
|
|
427
|
+
)
|
|
428
|
+
const timeOffset2 = vec2(
|
|
429
|
+
sin(this.timeUniform.mul(this.smokeSpeedUniform).mul(0.7)).mul(0.1),
|
|
430
|
+
sin(this.timeUniform.mul(this.smokeSpeedUniform).mul(0.5).add(float(1.5707963))).mul(0.08),
|
|
431
|
+
)
|
|
432
|
+
const noiseUv1 = texUv
|
|
433
|
+
.mul(this.noiseScaleUniform)
|
|
434
|
+
.add(vec2(this.passIndexUniform.mul(0.1), this.passIndexUniform.mul(0.1)))
|
|
435
|
+
.add(timeOffset1)
|
|
436
|
+
const noiseUv2 = rotatedUv
|
|
437
|
+
.mul(this.noiseScaleUniform)
|
|
438
|
+
.mul(0.8)
|
|
439
|
+
.add(vec2(this.passIndexUniform.mul(0.15), this.passIndexUniform.mul(0.15)))
|
|
440
|
+
.add(timeOffset1.mul(1.2))
|
|
441
|
+
const turbulenceUv = rotatedUv
|
|
442
|
+
.mul(this.noiseScaleUniform)
|
|
443
|
+
.mul(0.6)
|
|
444
|
+
.add(timeOffset2.mul(2))
|
|
445
|
+
|
|
446
|
+
const noiseSample1 = this.trackNoiseSampleNode(noiseUv1)
|
|
447
|
+
const noiseSample2 = this.trackNoiseSampleNode(noiseUv2)
|
|
448
|
+
const turbulenceSample = this.trackNoiseSampleNode(turbulenceUv)
|
|
449
|
+
const noiseSample = mix(noiseSample1.rgb, noiseSample2.rgb, float(0.5))
|
|
450
|
+
|
|
451
|
+
const noiseX = noiseSample.r
|
|
452
|
+
.sub(0.5)
|
|
453
|
+
.mul(2)
|
|
454
|
+
.mul(this.fluidNoiseUniform)
|
|
455
|
+
.add(turbulenceSample.r.sub(0.5).mul(this.smokeTurbulenceUniform))
|
|
456
|
+
const noiseY = noiseSample.g
|
|
457
|
+
.sub(0.5)
|
|
458
|
+
.mul(2)
|
|
459
|
+
.mul(this.fluidNoiseUniform)
|
|
460
|
+
.mul(0.4)
|
|
461
|
+
.add(turbulenceSample.g.sub(0.5).mul(this.smokeTurbulenceUniform).mul(0.5))
|
|
462
|
+
|
|
463
|
+
const flowDir = vec2(
|
|
464
|
+
this.directionXUniform.add(noiseX),
|
|
465
|
+
this.directionYUniform.add(noiseY),
|
|
466
|
+
).normalize()
|
|
467
|
+
const baseNoise = noiseSample.b.sub(0.5).mul(0.03).mul(this.fluidNoiseUniform)
|
|
468
|
+
|
|
469
|
+
let result: Node = vec4(float(0), float(0), float(0), float(0))
|
|
470
|
+
let totalWeight: Node = float(0)
|
|
471
|
+
|
|
472
|
+
for (let sampleIndex = 0; sampleIndex < 10; sampleIndex += 1) {
|
|
473
|
+
const t = float(sampleIndex / 9)
|
|
474
|
+
const asymmetry = smoothstep(float(0), float(1), t).mul(t).add(t.mul(0.5))
|
|
475
|
+
const sampleDist = asymmetry
|
|
476
|
+
.mul(this.dripLengthUniform)
|
|
477
|
+
.mul(this.blurStrengthUniform)
|
|
478
|
+
.mul(float(1).add(this.passIndexUniform.mul(this.blurSpreadUniform).mul(0.15)))
|
|
479
|
+
const sampleNoise = baseNoise.mul(float(1 + sampleIndex * 0.1))
|
|
480
|
+
const disperseTurbulence = sampleNoise.mul(t).mul(this.smokeTurbulenceUniform)
|
|
481
|
+
const samplePos = texUv
|
|
482
|
+
.add(flowDir.mul(sampleDist).mul(texelSize).mul(100))
|
|
483
|
+
.add(
|
|
484
|
+
vec2(
|
|
485
|
+
sampleNoise.add(disperseTurbulence),
|
|
486
|
+
sampleNoise.add(disperseTurbulence).mul(0.3),
|
|
487
|
+
),
|
|
488
|
+
)
|
|
489
|
+
const weight = mix(float(1).sub(t), float(1), smoothstep(float(0.35), float(0), t))
|
|
490
|
+
const sample = this.trackBlurSampleNode(samplePos)
|
|
491
|
+
result = result.add(sample.mul(weight))
|
|
492
|
+
totalWeight = totalWeight.add(weight)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
for (let sampleIndex = 1; sampleIndex <= 2; sampleIndex += 1) {
|
|
496
|
+
const t = float(sampleIndex / 3)
|
|
497
|
+
const sampleDist = t
|
|
498
|
+
.mul(this.dripLengthUniform)
|
|
499
|
+
.mul(this.blurStrengthUniform)
|
|
500
|
+
.mul(0.2)
|
|
501
|
+
const samplePos = texUv.sub(flowDir.mul(sampleDist).mul(texelSize).mul(100))
|
|
502
|
+
const weight = float(1).sub(t).mul(0.4)
|
|
503
|
+
const sample = this.trackBlurSampleNode(samplePos)
|
|
504
|
+
result = result.add(sample.mul(weight))
|
|
505
|
+
totalWeight = totalWeight.add(weight)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const blurred = result.div(max(totalWeight, float(0.0001)))
|
|
509
|
+
const lifted = max(blurred, original)
|
|
510
|
+
return vec4(mix(blurred.rgb, lifted.rgb, originalIntensity.mul(0.5)), float(1))
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private buildCompositeNode(): Node {
|
|
514
|
+
this.compositeBlurNodes = []
|
|
515
|
+
|
|
516
|
+
const texUv = vec2(uv().x, float(1).sub(uv().y))
|
|
517
|
+
const blurSample = this.finalInputNode
|
|
518
|
+
const crispSample = this.crispInputNode
|
|
519
|
+
const blurIntensity = max(max(blurSample.r, blurSample.g), blurSample.b)
|
|
520
|
+
const crispIntensity = max(max(crispSample.r, crispSample.g), crispSample.b)
|
|
521
|
+
const texelSize = vec2(
|
|
522
|
+
float(1).div(this.resolutionWidthUniform),
|
|
523
|
+
float(1).div(this.resolutionHeightUniform),
|
|
524
|
+
)
|
|
525
|
+
const blurR = this.trackCompositeBlurNode(
|
|
526
|
+
texUv.add(vec2(texelSize.x.mul(2), texelSize.y)),
|
|
527
|
+
).r
|
|
528
|
+
const blurB = this.trackCompositeBlurNode(
|
|
529
|
+
texUv.sub(vec2(texelSize.x.mul(2), texelSize.y)),
|
|
530
|
+
).b
|
|
531
|
+
|
|
532
|
+
const backgroundColor = vec3(
|
|
533
|
+
float(this.backgroundColorUniform.x),
|
|
534
|
+
float(this.backgroundColorUniform.y),
|
|
535
|
+
float(this.backgroundColorUniform.z),
|
|
536
|
+
)
|
|
537
|
+
const edgeColor = vec3(
|
|
538
|
+
float(this.edgeColorUniform.x),
|
|
539
|
+
float(this.edgeColorUniform.y),
|
|
540
|
+
float(this.edgeColorUniform.z),
|
|
541
|
+
)
|
|
542
|
+
const midColor = vec3(
|
|
543
|
+
float(this.midColorUniform.x),
|
|
544
|
+
float(this.midColorUniform.y),
|
|
545
|
+
float(this.midColorUniform.z),
|
|
546
|
+
)
|
|
547
|
+
const coreColor = vec3(
|
|
548
|
+
float(this.coreColorUniform.x),
|
|
549
|
+
float(this.coreColorUniform.y),
|
|
550
|
+
float(this.coreColorUniform.z),
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
const fluidColor = this.applyColorGradient(blurIntensity, backgroundColor, edgeColor, midColor, coreColor)
|
|
554
|
+
const crispColor = this.applyColorGradient(
|
|
555
|
+
crispIntensity,
|
|
556
|
+
backgroundColor,
|
|
557
|
+
edgeColor,
|
|
558
|
+
midColor,
|
|
559
|
+
coreColor,
|
|
560
|
+
)
|
|
561
|
+
const fluidColorChroma = vec3(
|
|
562
|
+
mix(fluidColor.x, fluidColor.x.mul(1.1), blurR.mul(0.3)),
|
|
563
|
+
fluidColor.y,
|
|
564
|
+
mix(fluidColor.z, fluidColor.z.mul(1.15), blurB.mul(0.3)),
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
const fluidMask = pow(clamp(blurIntensity, float(0), float(1)), float(1.2))
|
|
568
|
+
const crispMask = pow(clamp(crispIntensity, float(0), float(1)), float(0.96))
|
|
569
|
+
const fluidGlow = fluidColorChroma.mul(fluidMask).mul(1.8)
|
|
570
|
+
const crispGlow = crispColor.mul(crispMask).mul(1.95)
|
|
571
|
+
const crispWeight = crispMask.mul(this.crispBlendUniform)
|
|
572
|
+
let combined = mix(fluidGlow, crispGlow, crispWeight).add(fluidGlow.mul(0.15))
|
|
573
|
+
|
|
574
|
+
const grain = grainTexturePattern(
|
|
575
|
+
texUv.mul(vec2(this.grainScaleUniform, this.grainScaleUniform)),
|
|
576
|
+
)
|
|
577
|
+
.sub(0.5)
|
|
578
|
+
.mul(this.grainIntensityUniform)
|
|
579
|
+
combined = select(
|
|
580
|
+
this.grainEnabledUniform.greaterThan(float(0.5)),
|
|
581
|
+
combined.add(vec3(grain, grain, grain)),
|
|
582
|
+
combined,
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
const alpha = max(fluidMask, crispMask)
|
|
586
|
+
let finalColor = mix(backgroundColor, combined, smoothstep(float(0.01), float(0.85), alpha))
|
|
587
|
+
const vignetteUv = texUv.mul(2).sub(vec2(1, 1))
|
|
588
|
+
finalColor = finalColor.mul(float(1).sub(vignetteUv.dot(vignetteUv).mul(0.15)))
|
|
589
|
+
|
|
590
|
+
return vec4(clamp(finalColor, vec3(0, 0, 0), vec3(1, 1, 1)), float(1))
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private applyColorGradient(
|
|
594
|
+
intensity: Node,
|
|
595
|
+
bgColor: Node,
|
|
596
|
+
edgeColor: Node,
|
|
597
|
+
midColor: Node,
|
|
598
|
+
coreColor: Node,
|
|
599
|
+
): Node {
|
|
600
|
+
const t1 = smoothstep(float(0.6), float(0.95), intensity)
|
|
601
|
+
const t2 = smoothstep(float(0.2), float(0.65), intensity)
|
|
602
|
+
const t3 = smoothstep(float(0), float(0.25), intensity)
|
|
603
|
+
const edgeMixed = mix(bgColor, edgeColor, t3)
|
|
604
|
+
const midMixed = mix(edgeMixed, midColor, t2)
|
|
605
|
+
return mix(midMixed, coreColor, t1)
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
private ensureNoiseTexture(): void {
|
|
609
|
+
if (this.noiseTexture || this.noiseLoadStarted) {
|
|
610
|
+
return
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
this.noiseLoadStarted = true
|
|
614
|
+
void loadImageTexture("/textures/blue-noise.png")
|
|
615
|
+
.then((texture) => {
|
|
616
|
+
texture.wrapS = THREE.RepeatWrapping
|
|
617
|
+
texture.wrapT = THREE.RepeatWrapping
|
|
618
|
+
this.noiseTexture = texture
|
|
619
|
+
this.needsRefresh = true
|
|
620
|
+
})
|
|
621
|
+
.catch(() => {
|
|
622
|
+
this.needsRefresh = true
|
|
623
|
+
})
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
private trackBlurSampleNode(sampleUv: Node): Node {
|
|
627
|
+
const sampleNode = tslTexture(this.placeholder, sampleUv)
|
|
628
|
+
this.blurSampleNodes.push(sampleNode)
|
|
629
|
+
return sampleNode
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
private trackCompositeBlurNode(sampleUv: Node): Node {
|
|
633
|
+
const sampleNode = tslTexture(this.placeholder, sampleUv)
|
|
634
|
+
this.compositeBlurNodes.push(sampleNode)
|
|
635
|
+
return sampleNode
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
private trackNoiseSampleNode(sampleUv: Node): Node {
|
|
639
|
+
const sampleNode = tslTexture(this.placeholder, sampleUv)
|
|
640
|
+
this.noiseSampleNodes.push(sampleNode)
|
|
641
|
+
return sampleNode
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
private setColorUniform(target: Node, value: string): void {
|
|
645
|
+
const [r, g, b] = hexToRgb(value)
|
|
646
|
+
;(target.value as THREE.Vector3).set(r, g, b)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
private normalizeBloomRadius(value: number): number {
|
|
650
|
+
return clamp01(value / 24)
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
private normalizeBloomSoftness(value: number): number {
|
|
654
|
+
return Math.max(0.001, value * 0.25)
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
private disposeBloomNode(): void {
|
|
658
|
+
;(this.bloomNode as { dispose?: () => void } | null)?.dispose?.()
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
private getBloomTextureNode(): Node {
|
|
662
|
+
const bloomNode = this.bloomNode as
|
|
663
|
+
| ({
|
|
664
|
+
getTexture?: () => Node
|
|
665
|
+
getTextureNode?: () => Node
|
|
666
|
+
} & object)
|
|
667
|
+
| null
|
|
668
|
+
|
|
669
|
+
if (!bloomNode) {
|
|
670
|
+
throw new Error("Bloom node is not initialized")
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if ("getTextureNode" in bloomNode && typeof bloomNode.getTextureNode === "function") {
|
|
674
|
+
return bloomNode.getTextureNode()
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if ("getTexture" in bloomNode && typeof bloomNode.getTexture === "function") {
|
|
678
|
+
return bloomNode.getTexture()
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
throw new Error("Bloom node does not expose a texture getter")
|
|
682
|
+
}
|
|
683
|
+
}
|