claude-code-arcane 2.9.8 → 2.10.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/CHANGELOG.md +26 -0
- package/README.md +40 -38
- package/agents/business/business-ops-lead.md +1 -14
- package/agents/game/gameplay-programmer.md +3 -4
- package/agents/game/technical-artist.md +3 -4
- package/dist/cli.js +105 -39
- package/docs/USER-GUIDE.md +24 -22
- package/docs/coordination-rules.md +1 -1
- package/docs/directory-structure.md +93 -122
- package/package.json +1 -1
- package/profiles/ai.yaml +3 -0
- package/profiles/android-native.yaml +2 -0
- package/profiles/backend-go.yaml +1 -8
- package/profiles/backend-ts.yaml +2 -8
- package/profiles/backend-typescript.yaml +51 -0
- package/profiles/flutter.yaml +2 -0
- package/profiles/frontend-react-vite.yaml +39 -0
- package/profiles/frontend.yaml +2 -0
- package/profiles/integrations.yaml +6 -1
- package/profiles/ios-native.yaml +2 -0
- package/profiles/mobile-android-kotlin.yaml +36 -0
- package/profiles/mobile-flutter.yaml +37 -0
- package/profiles/mobile-ios-swift.yaml +37 -0
- package/profiles/mobile-react-native.yaml +39 -0
- package/profiles/mobile.yaml +2 -0
- package/rules/gamedev/engine-code.md +1 -1
- package/rules/gamedev/shader-code.md +1 -2
- package/rules/gamedev/vn-code.md +3 -2
- package/rules/implementation-workflow.md +1 -1
- package/rules/{ai-code.md → llm-code.md} +5 -5
- package/skills/agent-designer/README.md +2 -1
- package/skills/agent-designer/agent_evaluator.py +0 -5
- package/skills/ai-sdk-setup/SKILL.md +4 -4
- package/skills/arcane-add/SKILL.md +1 -1
- package/skills/arcane-list/SKILL.md +3 -3
- package/skills/arcane-status/SKILL.md +1 -1
- package/skills/architecture-decision/SKILL.md +3 -4
- package/skills/architecture-review/SKILL.md +1 -1
- package/skills/asset-audit/SKILL.md +0 -1
- package/skills/balance-check/SKILL.md +1 -1
- package/skills/brainstorm/SKILL.md +3 -3
- package/skills/brainstorm/references/next-steps-pipeline.md +8 -9
- package/skills/consistency-check/SKILL.md +2 -2
- package/skills/design-system/SKILL.md +1 -1
- package/skills/design-system/references/post-design-validation.md +2 -3
- package/skills/freelance-kickoff/SKILL.md +1 -1
- package/skills/frontend-dev/SKILL.md +27 -28
- package/skills/frontend-dev/references/asset-prompt-guide.md +1 -1
- package/skills/frontend-dev/references/motion-recipes.md +1 -1
- package/skills/help/SKILL.md +1 -2
- package/skills/hotfix/SKILL.md +2 -2
- package/skills/milestone-review/SKILL.md +1 -1
- package/skills/patch-notes/SKILL.md +1 -1
- package/skills/playtest-report/SKILL.md +2 -3
- package/skills/playwright-pro/CLAUDE.md +2 -2
- package/skills/playwright-pro/SKILL.md +13 -9
- package/skills/playwright-pro/agents/migration-planner.md +2 -2
- package/skills/playwright-pro/agents/test-architect.md +1 -2
- package/skills/playwright-pro/agents/test-debugger.md +1 -1
- package/skills/playwright-pro/hooks/detect-playwright.sh +1 -1
- package/skills/playwright-pro/skills/browserstack/SKILL.md +5 -5
- package/skills/playwright-pro/skills/migrate/SKILL.md +1 -1
- package/skills/playwright-pro/skills/report/SKILL.md +2 -2
- package/skills/playwright-pro/skills/testrail/SKILL.md +5 -5
- package/skills/playwright-pro/templates/README.md +2 -2
- package/skills/prototype/SKILL.md +1 -1
- package/skills/quick-design/SKILL.md +1 -1
- package/skills/release-checklist/SKILL.md +0 -1
- package/skills/security-audit/SKILL.md +1 -1
- package/skills/self-improving-agent/CLAUDE.md +12 -12
- package/skills/self-improving-agent/SKILL.md +16 -12
- package/skills/self-improving-agent/agents/memory-analyst.md +1 -1
- package/skills/self-improving-agent/hooks/error-capture.sh +2 -2
- package/skills/self-improving-agent/reference/memory-architecture.md +2 -2
- package/skills/self-improving-agent/reference/promotion-rules.md +3 -3
- package/skills/self-improving-agent/reference/rules-directory-patterns.md +1 -1
- package/skills/self-improving-agent/settings.json +5 -5
- package/skills/self-improving-agent/skills/extract/SKILL.md +7 -7
- package/skills/self-improving-agent/skills/promote/SKILL.md +5 -5
- package/skills/self-improving-agent/skills/remember/SKILL.md +9 -9
- package/skills/self-improving-agent/skills/review/SKILL.md +7 -7
- package/skills/self-improving-agent/skills/status/SKILL.md +8 -8
- package/skills/senior-ml-engineer/references/llm_integration_guide.md +14 -31
- package/skills/senior-prompt-engineer/SKILL.md +10 -10
- package/skills/senior-prompt-engineer/references/prompt_engineering_patterns.md +1 -1
- package/skills/senior-prompt-engineer/scripts/agent_orchestrator.py +43 -40
- package/skills/senior-prompt-engineer/scripts/prompt_optimizer.py +23 -66
- package/skills/shader-dev/reference/fluid-simulation.md +4 -4
- package/skills/shader-dev/reference/simulation-physics.md +6 -6
- package/skills/shader-dev/reference/voxel-rendering.md +1 -1
- package/skills/shader-dev/techniques/ambient-occlusion.md +1 -1
- package/skills/shader-dev/techniques/analytic-ray-tracing.md +2 -2
- package/skills/shader-dev/techniques/domain-warping.md +2 -2
- package/skills/shader-dev/techniques/fluid-simulation.md +45 -45
- package/skills/shader-dev/techniques/multipass-buffer.md +24 -24
- package/skills/shader-dev/techniques/normal-estimation.md +1 -1
- package/skills/shader-dev/techniques/particle-system.md +16 -16
- package/skills/shader-dev/techniques/polar-uv-manipulation.md +1 -1
- package/skills/shader-dev/techniques/post-processing.md +7 -7
- package/skills/shader-dev/techniques/sdf-3d.md +1 -1
- package/skills/shader-dev/techniques/simulation-physics.md +24 -24
- package/skills/shader-dev/techniques/sound-synthesis.md +2 -2
- package/skills/shader-dev/techniques/voronoi-cellular-noise.md +1 -1
- package/skills/shader-dev/techniques/voxel-rendering.md +60 -60
- package/skills/smoke-check/SKILL.md +1 -1
- package/skills/sprint-plan/SKILL.md +1 -2
- package/skills/start/SKILL.md +7 -7
- package/skills/story-done/SKILL.md +1 -1
- package/skills/test-helpers/SKILL.md +7 -2
- package/skills/test-setup/SKILL.md +1 -1
- package/skills/ue-actor-component-architecture/SKILL.md +0 -1
- package/skills/ue-ai-navigation/SKILL.md +0 -1
- package/skills/ue-animation-system/SKILL.md +0 -1
- package/skills/ue-async-threading/SKILL.md +0 -1
- package/skills/ue-audio-system/SKILL.md +0 -1
- package/skills/ue-blueprints/SKILL.md +0 -1
- package/skills/ue-character-movement/SKILL.md +0 -1
- package/skills/ue-cpp-foundations/SKILL.md +0 -1
- package/skills/ue-data-assets-tables/SKILL.md +0 -1
- package/skills/ue-editor-tools/SKILL.md +0 -1
- package/skills/ue-game-features/SKILL.md +0 -1
- package/skills/ue-gameplay-abilities/SKILL.md +0 -1
- package/skills/ue-gameplay-framework/SKILL.md +0 -1
- package/skills/ue-input-system/SKILL.md +0 -1
- package/skills/ue-mass-entity/SKILL.md +0 -1
- package/skills/ue-materials-rendering/SKILL.md +0 -1
- package/skills/ue-module-build-system/SKILL.md +0 -1
- package/skills/ue-naming-conventions/SKILL.md +0 -1
- package/skills/ue-networking-replication/SKILL.md +0 -1
- package/skills/ue-niagara-effects/SKILL.md +0 -1
- package/skills/ue-packaging/SKILL.md +0 -1
- package/skills/ue-physics-collision/SKILL.md +0 -1
- package/skills/ue-procedural-generation/SKILL.md +0 -1
- package/skills/ue-project-context/SKILL.md +0 -1
- package/skills/ue-sequencer-cinematics/SKILL.md +0 -1
- package/skills/ue-serialization-savegames/SKILL.md +0 -1
- package/skills/ue-state-trees/SKILL.md +0 -1
- package/skills/ue-testing-debugging/SKILL.md +0 -1
- package/skills/ue-ui-umg-slate/SKILL.md +0 -1
- package/skills/ue-world-level-streaming/SKILL.md +0 -1
- package/skills/ux-review/SKILL.md +1 -1
- package/docs/RELEASE-SETUP.md +0 -99
- package/docs/presentations/arcane-overview-12.pptx +0 -0
- package/docs/presentations/arcane-overview.pptx +0 -0
- package/docs/presentations/build_arcane_deck.py +0 -310
- package/docs/presentations/build_arcane_deck_12.py +0 -399
- package/docs/trusted-publishing-setup.md +0 -163
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# Particle System
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**GLSL ES 3.0 return values**: All code paths in non-void functions must return a value. Every branch of an if statement must have a return.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Particle system time cycling**: Must use `mod(time - offset, period)` for cycle computation. **Never use `floor(time / period) * period`**! The latter causes all particles to have negative time initially, resulting in startup delay or blank rendering.
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**Brightness budget (most common failure cause!)**: Each particle's `numerator / (dist² + epsilon)` peak = `numerator / epsilon`. **Total peak = N_particles x (numerator / epsilon) must be < 5.0** (single pass). Exceeding this budget causes washout even with Reinhard. See specific reference values in the comments of each template below. Multi-pass ping-pong systems have a stricter budget, see below.
|
|
8
8
|
|
|
9
|
-
**
|
|
9
|
+
**Particle color vs background contrast**: When particle color is close to the background (sand dust/snow/fog), visibility must be enhanced through at least one method: (1) brightness significantly higher than background (2) different hue (3) visible motion trail.
|
|
10
10
|
|
|
11
|
-
**
|
|
11
|
+
**Elongated glow (meteor/trail lines)**: Do not use `1/(distPerp² + tiny_epsilon)` for line glow — too-small epsilon makes the line center extremely bright. Correct approach: use `smoothstep` or `exp(-dist)` for lines, see meteor template below.
|
|
12
12
|
|
|
13
13
|
### Correct Pattern
|
|
14
14
|
```glsl
|
|
@@ -133,7 +133,7 @@ void main() {
|
|
|
133
133
|
float fade = smoothstep(1.0, 0.0, burstPhase);
|
|
134
134
|
fade *= fade;
|
|
135
135
|
|
|
136
|
-
//
|
|
136
|
+
// Brightness budget: peak = 0.015/0.03 = 0.5, x 40 particles x fade ≈ total peak < 5.0
|
|
137
137
|
float intensity = fade * 0.015 / (dist * dist + 0.03);
|
|
138
138
|
|
|
139
139
|
float hueOffset = (hash11(pId * 3.7 + fwId * 11.0) - 0.5) * 0.15;
|
|
@@ -330,7 +330,7 @@ requestAnimationFrame(render);
|
|
|
330
330
|
|
|
331
331
|
### Star Field Function Template (For Meteors, Fireworks, and Other Night Sky Scenes)
|
|
332
332
|
|
|
333
|
-
**
|
|
333
|
+
**Star visibility**: Stars must be clearly visible in screenshots as individual light points. Use `exp(-dist*dist*k)` for sharp Gaussian dots rather than `1/(dist²+eps)` broad glow. Each star's peak brightness should be at least 0.3 to be visible against a dark background.
|
|
334
334
|
|
|
335
335
|
```glsl
|
|
336
336
|
#define NUM_STARS 200
|
|
@@ -484,7 +484,7 @@ vec2 particlePosition(int id, float time) {
|
|
|
484
484
|
### Step 4: Buffer-Stored Particle State (Stateful System)
|
|
485
485
|
```glsl
|
|
486
486
|
// === Buffer A: Particle Physics Update ===
|
|
487
|
-
//
|
|
487
|
+
// Multi-pass system warning: each fragment shader is compiled independently, helper functions must be redefined in each shader!
|
|
488
488
|
#define NUM_PARTICLES 40
|
|
489
489
|
#define MAX_VEL 0.5
|
|
490
490
|
#define MAX_ACC 3.0
|
|
@@ -547,7 +547,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
|
547
547
|
```
|
|
548
548
|
|
|
549
549
|
### Step 5: Particle Rendering — Metaball Style
|
|
550
|
-
//
|
|
550
|
+
// Multi-pass system warning: Image shader must define all the following helper functions (compiled independently)!
|
|
551
551
|
```glsl
|
|
552
552
|
#define BRIGHTNESS 0.002
|
|
553
553
|
#define COLOR_START vec3(0.0, 0.64, 0.2)
|
|
@@ -595,7 +595,7 @@ vec3 renderParticles(vec2 uv) {
|
|
|
595
595
|
|
|
596
596
|
### Step 6: Frame Feedback Motion Blur
|
|
597
597
|
```glsl
|
|
598
|
-
//
|
|
598
|
+
// Ping-pong brightness budget (most common washout cause!):
|
|
599
599
|
// Steady-state brightness = singleFrameContribution / (1 - TRAIL_DECAY)
|
|
600
600
|
// decay=0.88 → 8.3x amplification, decay=0.95 → 20x amplification
|
|
601
601
|
// Budget formula: N_particles x (numerator/epsilon) x 1/(1-decay) < 10.0
|
|
@@ -834,8 +834,8 @@ if (particle.z > particle.a) {
|
|
|
834
834
|
Uses stateless single pass. Key: spiral trajectory + high-visibility particles + vortex eye dark zone + separated background fog layer.
|
|
835
835
|
|
|
836
836
|
```glsl
|
|
837
|
-
//
|
|
838
|
-
//
|
|
837
|
+
// Particle color must be 2-3x brighter than background to be visible (sand-colored particles on sand-colored background easily disappear)
|
|
838
|
+
// Brightness budget: 150 particles x peak(0.005/0.003=1.67) x fade(avg~0.3) ≈ 75, overexposed!
|
|
839
839
|
// Must increase epsilon or decrease numerator. Safe values: numerator=0.002, epsilon=0.008 → peak=0.25, total=11 → OK after Reinhard
|
|
840
840
|
#define NUM_DUST 150
|
|
841
841
|
#define VORTEX_CENTER vec2(0.0)
|
|
@@ -886,7 +886,7 @@ void main() {
|
|
|
886
886
|
|
|
887
887
|
Meteors, magic projectiles, etc. need elongated glow (stretched luminous lines). **Do not use `1/(distPerp² + tiny_epsilon)` for lines** — too-small epsilon makes line centers extremely bright and washed out. Use `exp(-dist)` or `smoothstep` for safe line glow.
|
|
888
888
|
|
|
889
|
-
**
|
|
889
|
+
**Common meteor failures**: (1) Star background too dark to see — must call `starField()` above and ensure stars use Gaussian dots `exp(-dist²*k)` for rendering (2) Meteor trail too faint — `core` multiplier should be at least 0.15, each step after dividing by `NUM_TRAIL_STEPS` still needs >= 0.005 contribution
|
|
890
890
|
|
|
891
891
|
```glsl
|
|
892
892
|
#define NUM_METEORS 6
|
|
@@ -959,7 +959,7 @@ void main() {
|
|
|
959
959
|
|
|
960
960
|
Water/sparks jetting upward from a point, parabolic descent. Key: **Particles must be sharp, individually visible points** (small epsilon), not just a diffuse glow blob. Must include: (1) main water column particles (upward jet + parabola) (2) splash particles (spread sideways after hitting water) (3) water surface/pool visuals.
|
|
961
961
|
|
|
962
|
-
**
|
|
962
|
+
**Most common fountain failure**: Only produces blurry glow without visible individual water droplet trajectories! Must use small epsilon (<=0.002) so each particle is clearly visible as an individual light point. Numerator must also be proportionally reduced to control total brightness.
|
|
963
963
|
|
|
964
964
|
```glsl
|
|
965
965
|
#define NUM_WATER 60
|
|
@@ -1042,7 +1042,7 @@ void main() {
|
|
|
1042
1042
|
|
|
1043
1043
|
Flame effects must include **two layers**: (1) smooth flame body at the base (noise-driven cone gradient) (2) many **discrete ember/spark particles** above, drifting upward and gradually extinguishing. Using only a smooth gradient will be judged as "no particle system."
|
|
1044
1044
|
|
|
1045
|
-
**
|
|
1045
|
+
**Most common flame failure**: Only draws a smooth gradient without discrete particles! Must have NUM_SPARKS individual point-like particles drifting out from the flame top.
|
|
1046
1046
|
|
|
1047
1047
|
```glsl
|
|
1048
1048
|
#define NUM_SPARKS 60
|
|
@@ -1112,7 +1112,7 @@ void main() {
|
|
|
1112
1112
|
|
|
1113
1113
|
Magic effects, spiral ascent, magic circles, etc. require particles arranged in **geometric arrays** with **iridescent shimmer**. Key: particles must be individually visible glowing points (not a blurry glow blob), and the spiral structure must be clearly discernible.
|
|
1114
1114
|
|
|
1115
|
-
**
|
|
1115
|
+
**Most common magic failure**: Only produces a blob of blurry light (diffuse glow blob) without visible individual particles or geometric structure! Ensure each particle is an independently visible small light point, and the overall arrangement forms spiral/ring/other geometric shapes. Reduce epsilon to make each particle sharper (small light dot) rather than a large blurry halo.
|
|
1116
1116
|
|
|
1117
1117
|
```glsl
|
|
1118
1118
|
#define NUM_SPIRAL 80
|
|
@@ -9,7 +9,7 @@ Code templates in this document use ShaderToy GLSL style. When generating standa
|
|
|
9
9
|
- Fragment shader: `varying` → `in`, `gl_FragColor` → custom `out vec4 fragColor`, `texture2D()` → `texture()`
|
|
10
10
|
- ShaderToy's `void mainImage(out vec4 fragColor, in vec2 fragCoord)` must be adapted to standard `void main()` entry
|
|
11
11
|
|
|
12
|
-
**
|
|
12
|
+
**GLSL Type Strictness Warning**:
|
|
13
13
|
- `vec2 = float` is illegal: types must match exactly, e.g., `float r = length(uv)` not `vec2 r = length(uv)`
|
|
14
14
|
- Function return types must match: commonly used `fbm()` / `noise()` return `float`, cannot be assigned to `vec2`
|
|
15
15
|
- If you need a vec2 type, use `vec2(fbm(...), fbm(...))` or `vec2(value)` constructor
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
## WebGL2 Adaptation Requirements
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Critical Warning for Standalone HTML Deployment**: Post-processing effects require an input texture to work. When generating standalone HTML, you must:
|
|
4
4
|
1. Set `#define USE_DEMO_SCENE 1` to use the built-in demo scene (recommended), or
|
|
5
5
|
2. Pass a valid input texture to the `iChannel0` channel, otherwise the screen will be completely black
|
|
6
6
|
3. **Critical**: When USE_DEMO_SCENE=1, ensure the #else branch code does not reference non-existent uniforms (e.g., iChannel0)
|
|
7
7
|
|
|
8
|
-
**
|
|
8
|
+
**GLSL Type Strictness Rules**:
|
|
9
9
|
- `vec2 = float` is illegal — must use `vec2(x, x)` or `vec2(x)`
|
|
10
10
|
- Function parameters must be defined before use; using a variable name in its own initializer is forbidden (e.g., `float w = filmicCurve(w, w)` is an error)
|
|
11
11
|
- Variables must be declared before use
|
|
@@ -203,7 +203,7 @@ const progPost = createProgram(vsSource, fsPost);
|
|
|
203
203
|
function createFBO(w, h) {
|
|
204
204
|
const tex = gl.createTexture();
|
|
205
205
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
206
|
-
//
|
|
206
|
+
// Check for float texture extension, fall back to RGBA8 if unsupported
|
|
207
207
|
const fmt = ext ? gl.RGBA16F : gl.RGBA;
|
|
208
208
|
const typ = ext ? gl.FLOAT : gl.UNSIGNED_BYTE;
|
|
209
209
|
gl.texImage2D(gl.TEXTURE_2D, 0, fmt, w, h, 0, gl.RGBA, typ, null);
|
|
@@ -293,7 +293,7 @@ The essence of post-processing is **per-pixel transformation of an already-rende
|
|
|
293
293
|
vec3 reinhard(vec3 color) { return color / (1.0 + color); }
|
|
294
294
|
|
|
295
295
|
// Filmic Reinhard (W=white point, T2=shoulder parameter)
|
|
296
|
-
//
|
|
296
|
+
// GLSL critical rule: function parameters must be defined before use; using a variable name in its own initializer is forbidden
|
|
297
297
|
const float W = 1.2, T2 = 7.5; // adjustable
|
|
298
298
|
float filmic_reinhard_curve(float x) {
|
|
299
299
|
float q = (T2 * T2 + 1.0) * x * x;
|
|
@@ -440,7 +440,7 @@ color += (orderedDither(fragCoord) - 0.5) * 4.0 / 255.0;
|
|
|
440
440
|
|
|
441
441
|
### Step 10: Demo Scene (Required for Standalone HTML!)
|
|
442
442
|
|
|
443
|
-
**
|
|
443
|
+
**Critical Warning**: Standalone HTML deployment must provide an input texture, otherwise post-processing effects will output solid black.
|
|
444
444
|
|
|
445
445
|
```glsl
|
|
446
446
|
// Demo scene fallback: used when no valid input texture is available
|
|
@@ -541,7 +541,7 @@ vec3 fxaa(sampler2D tex, vec2 fragCoord, vec2 resolution) {
|
|
|
541
541
|
|
|
542
542
|
Can be run directly in ShaderToy. `iChannel0` is the scene texture.
|
|
543
543
|
|
|
544
|
-
**
|
|
544
|
+
**Important Warning**: For standalone HTML deployment, you must:
|
|
545
545
|
1. Pass a valid input texture to iChannel0 (or uChannel0)
|
|
546
546
|
2. Or set `#define USE_DEMO_SCENE 1` to use the built-in demo scene
|
|
547
547
|
|
|
@@ -589,7 +589,7 @@ vec3 demoScene(vec2 uv, float time) {
|
|
|
589
589
|
}
|
|
590
590
|
|
|
591
591
|
vec3 tonemapReinhard(vec3 c) { return c / (1.0 + c); }
|
|
592
|
-
//
|
|
592
|
+
// filmicCurve takes only one parameter x; w is computed externally via WHITE_POINT
|
|
593
593
|
float filmicCurve(float x) {
|
|
594
594
|
float q = (SHOULDER*SHOULDER+1.0)*x*x; return q/(q+x+SHOULDER*SHOULDER);
|
|
595
595
|
}
|
|
@@ -36,7 +36,7 @@ Key math:
|
|
|
36
36
|
|
|
37
37
|
Runs directly in ShaderToy. Includes multi-primitive scene, smooth blending, soft shadows, AO, and material system.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
**** When using the `vec2(distance, materialID)` material system, `smin` needs to handle `vec2` types. The template includes a `vec2 smin(vec2 a, vec2 b, float k)` overload that ensures the material ID is correctly passed through during smooth blending (taking the material of the closer distance).
|
|
40
40
|
|
|
41
41
|
```glsl
|
|
42
42
|
// 3D SDF Full Rendering Pipeline Template - Runs in ShaderToy
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Below is a complete standalone HTML template demonstrating how to set up WebGL2 double buffering (ping-pong) for physics simulation:
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**WebGL2 ping-pong core rule: The texture bound to the write-target framebuffer must never simultaneously serve as input for any iChannel.** Violating this rule causes undefined behavior (typically all-black/all-zero output).
|
|
8
8
|
|
|
9
9
|
For simulations requiring "current frame" and "previous frame" two time steps (such as the wave equation), use **dual-channel encoding**: R channel stores current height, G channel stores previous frame height. This way only one buffer is read from (iChannel0 = currentBuf), writing to another buffer (nextBuf), avoiding read-write conflicts.
|
|
10
10
|
|
|
@@ -62,9 +62,9 @@ void main() {
|
|
|
62
62
|
}`;
|
|
63
63
|
|
|
64
64
|
// === Buffer Pass Shader (physics simulation) ===
|
|
65
|
-
//
|
|
65
|
+
// Only uses iChannel0 (reads currentBuf), writes to nextBuf
|
|
66
66
|
// R channel = current height, G channel = previous frame height (dual-channel encoding avoids read-write conflict)
|
|
67
|
-
//
|
|
67
|
+
// RGBA8 compatible: use encode/decode to map signed values to [0,1], ensuring SwiftShader/no-float-texture environments work correctly
|
|
68
68
|
const fsBuffer = `#version 300 es
|
|
69
69
|
precision highp float;
|
|
70
70
|
uniform sampler2D iChannel0;
|
|
@@ -161,7 +161,7 @@ gl.enableVertexAttribArray(posLoc);
|
|
|
161
161
|
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
162
162
|
|
|
163
163
|
// === Render loop ===
|
|
164
|
-
//
|
|
164
|
+
// currentBuf (read-only) and nextBuf (write-only) must be different buffers
|
|
165
165
|
let frame = 0;
|
|
166
166
|
function render(time) {
|
|
167
167
|
time *= 0.001;
|
|
@@ -207,7 +207,7 @@ requestAnimationFrame(render);
|
|
|
207
207
|
</script></body></html>
|
|
208
208
|
```
|
|
209
209
|
|
|
210
|
-
**
|
|
210
|
+
**Common errors**:
|
|
211
211
|
1. **RGBA8 signed value truncation (fatal)**: Environments like SwiftShader that don't support `EXT_color_buffer_float` fall back to RGBA8 where values are clamped to [0,1]. Simulations requiring negative values (like the wave equation) will all zero out and produce a static image. **Must** use encode/decode functions: store as `v * 0.5 + 0.5`, read as `v * 2.0 - 1.0`, switching at runtime via `uniform int useFloatTex`. See the `encode()`/`decode()` functions in the template above
|
|
212
212
|
2. **Ping-pong read-write conflict (fatal)**: When rendering to a framebuffer, the texture bound to that framebuffer cannot simultaneously serve as input. The wave equation uses dual-channel encoding (R=current, G=previous) requiring only one input buffer; cloth/particle systems read getpos/getvel both from iChannel0
|
|
213
213
|
3. **Cloth rendering must use world coordinate projection (fatal)**: The cloth Image Pass cannot use `uv * vec2(SIZX, SIZY)` to map screen UV directly to grid ID. It must iterate over mesh faces, project vertex world coordinates to screen space via `worldToScreen()`, and perform triangle rasterization
|
|
@@ -253,14 +253,14 @@ Vorticity confinement: curl = ∂v_x/∂y - ∂v_y/∂x
|
|
|
253
253
|
|
|
254
254
|
### Step 1: Ping-Pong Double Buffering (Correct WebGL2 Implementation)
|
|
255
255
|
|
|
256
|
-
**
|
|
256
|
+
**Key difference between ShaderToy and WebGL2**: In ShaderToy, Buffer A/B are two independent passes with separate write targets, so `iChannel0=self, iChannel1=other` doesn't conflict. But in WebGL2 with a single shader program doing ping-pong, the write-target texture cannot be read simultaneously.
|
|
257
257
|
|
|
258
258
|
**Solution: Dual-channel encoding** — R channel stores current height, G channel stores previous frame height, requiring only one input buffer:
|
|
259
259
|
|
|
260
260
|
```glsl
|
|
261
261
|
// WebGL2 Wave Equation Buffer Pass
|
|
262
|
-
//
|
|
263
|
-
//
|
|
262
|
+
// Only iChannel0 (reads currentBuf), writes to nextBuf (must be different!)
|
|
263
|
+
// encode/decode ensures signed values aren't truncated under RGBA8 (no float textures)
|
|
264
264
|
|
|
265
265
|
uniform int useFloatTex;
|
|
266
266
|
float decode(float v) { return useFloatTex == 1 ? v : v * 2.0 - 1.0; }
|
|
@@ -298,7 +298,7 @@ gl.activeTexture(gl.TEXTURE0);
|
|
|
298
298
|
gl.bindTexture(gl.TEXTURE_2D, currentBuf.tex); // read from currentBuf
|
|
299
299
|
gl.uniform1i(gl.getUniformLocation(progBuffer, 'iChannel0'), 0);
|
|
300
300
|
gl.uniform1i(gl.getUniformLocation(progBuffer, 'useFloatTex'), useFloat ? 1 : 0);
|
|
301
|
-
//
|
|
301
|
+
// Do NOT bind iChannel1 to nextBuf/otherBuf!
|
|
302
302
|
```
|
|
303
303
|
|
|
304
304
|
### Step 2: Interaction-Driven (External Force Injection)
|
|
@@ -328,7 +328,7 @@ next += force;
|
|
|
328
328
|
### Step 3: Height Field Rendering (Image Pass)
|
|
329
329
|
|
|
330
330
|
```glsl
|
|
331
|
-
//
|
|
331
|
+
// Image Pass also needs decode
|
|
332
332
|
uniform int useFloatTex;
|
|
333
333
|
float decode(float v) { return useFloatTex == 1 ? v : v * 2.0 - 1.0; }
|
|
334
334
|
|
|
@@ -429,9 +429,9 @@ Texture regions are partitioned to store different attributes:
|
|
|
429
429
|
#define SIZX 128.0
|
|
430
430
|
#define SIZY 64.0
|
|
431
431
|
|
|
432
|
-
//
|
|
432
|
+
// Cloth/particle systems: getpos/getvel both read from iChannel0 (not iChannel1)
|
|
433
433
|
// Because iChannel0 = currentBuf (read-only), write target is nextBuf (separate buffer)
|
|
434
|
-
//
|
|
434
|
+
// Use +0.5 to sample texel centers (not +0.01)
|
|
435
435
|
vec3 getpos(vec2 id) {
|
|
436
436
|
return texture(iChannel0, (id + 0.5) / iResolution.xy).xyz;
|
|
437
437
|
}
|
|
@@ -566,8 +566,8 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord)
|
|
|
566
566
|
|
|
567
567
|
```glsl
|
|
568
568
|
// === Buffer Pass (Wave Equation) ===
|
|
569
|
-
//
|
|
570
|
-
//
|
|
569
|
+
// Only uses iChannel0 = currentBuf (read-only), writes to nextBuf
|
|
570
|
+
// encode/decode ensures signed values aren't truncated under RGBA8 (SwiftShader compatible)
|
|
571
571
|
|
|
572
572
|
uniform int useFloatTex;
|
|
573
573
|
float decode(float v) { return useFloatTex == 1 ? v : v * 2.0 - 1.0; }
|
|
@@ -611,7 +611,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord)
|
|
|
611
611
|
|
|
612
612
|
```glsl
|
|
613
613
|
// === Image Pass ===
|
|
614
|
-
//
|
|
614
|
+
// Also needs decode to correctly read signed height values
|
|
615
615
|
|
|
616
616
|
uniform int useFloatTex;
|
|
617
617
|
float decode(float v) { return useFloatTex == 1 ? v : v * 2.0 - 1.0; }
|
|
@@ -744,7 +744,7 @@ void main() {
|
|
|
744
744
|
```glsl
|
|
745
745
|
// === Image Pass (Smoke Rendering) ===
|
|
746
746
|
// Reads density (z channel) and velocity (xy channels) from buffer, renders dense layered smoke + light scattering
|
|
747
|
-
//
|
|
747
|
+
// Smoke brightness key: absorption coefficient must be large enough (>=3.0), background not too dark, lightTransmit must not over-attenuate
|
|
748
748
|
|
|
749
749
|
void main() {
|
|
750
750
|
vec2 uv = vUv;
|
|
@@ -812,7 +812,7 @@ Cloth simulation requires 2 buffers for ping-pong alternating read/write, with a
|
|
|
812
812
|
|
|
813
813
|
```glsl
|
|
814
814
|
// WebGL2-adapted cloth simulation Buffer Pass
|
|
815
|
-
//
|
|
815
|
+
// getpos/getvel both read from iChannel0! iChannel0 = currentBuf (read-only), writes to nextBuf
|
|
816
816
|
|
|
817
817
|
#define SIZX 128.0
|
|
818
818
|
#define SIZY 64.0
|
|
@@ -889,12 +889,12 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
|
889
889
|
|
|
890
890
|
#### Cloth Rendering Pass (Image Pass) Complete Template
|
|
891
891
|
|
|
892
|
-
**
|
|
892
|
+
**Cloth rendering core principle**: After physics simulation, cloth particle world positions (pos.xy) will deviate from their initial grid positions (due to gravity, collisions, etc.). The Image Pass must render based on particles' **actual world positions** projected to the screen — you cannot use `uv * vec2(SIZX, SIZY)` to directly map screen UV to grid ID (that would produce scattered dots/fragments rather than a continuous cloth surface).
|
|
893
893
|
|
|
894
894
|
Correct approach: iterate over all cloth mesh cells, project each cell's 4 vertex world coordinates to screen space, determine if the current pixel falls within that quad, then interpolate shading.
|
|
895
895
|
|
|
896
896
|
```glsl
|
|
897
|
-
//
|
|
897
|
+
// must pass additional uniform vec2 iSimResolution (simulation resolution)
|
|
898
898
|
// getpos/getvel use iSimResolution, not iResolution
|
|
899
899
|
// Rendering method: iterate cloth mesh, project world coordinates to screen coordinates
|
|
900
900
|
|
|
@@ -1035,11 +1035,11 @@ void main() {
|
|
|
1035
1035
|
}
|
|
1036
1036
|
```
|
|
1037
1037
|
|
|
1038
|
-
**
|
|
1038
|
+
**Cloth rendering performance note**: The above template uses a double loop to iterate all mesh faces for triangle rasterization. For a 128x64 mesh this is about 8000 quads per frame. If GPU performance is insufficient, reduce mesh resolution (e.g., SIZX=64, SIZY=32) or use `texelFetch` instead of `texture` for speed. Another approach is to partition the cloth into blocks (e.g., 4x4), each with an independent bounding box for early culling.
|
|
1039
1039
|
|
|
1040
1040
|
#### Cloth Simulation Complete HTML Template (Multi-Substep Iteration)
|
|
1041
1041
|
|
|
1042
|
-
**
|
|
1042
|
+
**Key notes (must-read for cloth template)**:
|
|
1043
1043
|
1. **No read-write conflict**: In the Buffer Pass, iChannel0 is bound to currentBuf (read-only), the write target is nextBuf (separate buffer). getpos/getvel both read from iChannel0
|
|
1044
1044
|
2. **iSimResolution uniform**: Image Pass must have `uniform vec2 iSimResolution` passing `(SIM_W, SIM_H)`, and `getpos`/`getvel` internally use `iSimResolution` for UV calculation
|
|
1045
1045
|
3. **iFrame value passing**: In substep loops, iFrame should pass `frame * SUBSTEPS + substep`, ensuring the initialization condition `iFrame < SUBSTEPS` only triggers on the first frame
|
|
@@ -1130,7 +1130,7 @@ float getBallRadius() { return 0.12; }
|
|
|
1130
1130
|
vec3 pos, vel, ovel;
|
|
1131
1131
|
vec2 c;
|
|
1132
1132
|
|
|
1133
|
-
//
|
|
1133
|
+
// Both read from iChannel0 (currentBuf), not iChannel1
|
|
1134
1134
|
vec3 getpos(vec2 id) {
|
|
1135
1135
|
return texture(iChannel0, (id + 0.5) / iResolution.xy).xyz;
|
|
1136
1136
|
}
|
|
@@ -1414,7 +1414,7 @@ function render(time) {
|
|
|
1414
1414
|
time *= 0.001;
|
|
1415
1415
|
|
|
1416
1416
|
for (let substep = 0; substep < SUBSTEPS; substep++) {
|
|
1417
|
-
//
|
|
1417
|
+
// ping-pong: read from currentBuf (iChannel0), write to nextBuf
|
|
1418
1418
|
let currentBuf = (globalStep % 2 === 0) ? bufA : bufB;
|
|
1419
1419
|
let nextBuf = (globalStep % 2 === 0) ? bufB : bufA;
|
|
1420
1420
|
|
|
@@ -1422,7 +1422,7 @@ function render(time) {
|
|
|
1422
1422
|
gl.viewport(0, 0, SIM_W, SIM_H);
|
|
1423
1423
|
gl.useProgram(progBuffer);
|
|
1424
1424
|
|
|
1425
|
-
//
|
|
1425
|
+
// Only bind iChannel0 = currentBuf (read-only)
|
|
1426
1426
|
gl.activeTexture(gl.TEXTURE0);
|
|
1427
1427
|
gl.bindTexture(gl.TEXTURE_2D, currentBuf.tex);
|
|
1428
1428
|
gl.uniform1i(gl.getUniformLocation(progBuffer, 'iChannel0'), 0);
|
|
@@ -157,7 +157,7 @@ const float NOTES[16] = float[16](
|
|
|
157
157
|
|
|
158
158
|
float melody_array(float time, float bpm) {
|
|
159
159
|
float beat = time * bpm / 60.0;
|
|
160
|
-
int idx = int(mod(beat, 16.0)); //
|
|
160
|
+
int idx = int(mod(beat, 16.0)); // Must use int() conversion
|
|
161
161
|
float noteTime = fract(beat);
|
|
162
162
|
float freq = noteFreq(NOTES[idx]);
|
|
163
163
|
return instrument_additive(freq, noteTime * 60.0 / bpm);
|
|
@@ -241,7 +241,7 @@ vec2 mainSound(int samp, float time) {
|
|
|
241
241
|
// Arrangement: smoothstep controls intro/outro
|
|
242
242
|
audio *= smoothstep(0.0, 4.0, bar); // Fade in over first 4 bars
|
|
243
243
|
audio *= 0.35 * smoothstep(0.0, 0.5, time);
|
|
244
|
-
//
|
|
244
|
+
// Array indices must be int type
|
|
245
245
|
// float idx = mod(beat, 16.0); // WRONG: float cannot be used as index
|
|
246
246
|
int idx = int(mod(beat, 16.0)); // CORRECT: int(mod(...)) conversion
|
|
247
247
|
return clamp(audio, -1.0, 1.0);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
-
|
|
1
|
+
- **** All declared `uniform` variables must be used in the shader code, otherwise the compiler will optimize them away. After optimization, `gl.getUniformLocation()` returns `null`, and setting that uniform triggers a WebGL `INVALID_OPERATION` error, which may cause rendering failure. Ensure uniforms like `iTime` are actually used in `main()` (e.g., `float t = iTime * 1.0;`)
|
|
2
2
|
|
|
3
3
|
# Voronoi & Cellular Noise
|
|
4
4
|
|