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.
Files changed (147) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +40 -38
  3. package/agents/business/business-ops-lead.md +1 -14
  4. package/agents/game/gameplay-programmer.md +3 -4
  5. package/agents/game/technical-artist.md +3 -4
  6. package/dist/cli.js +105 -39
  7. package/docs/USER-GUIDE.md +24 -22
  8. package/docs/coordination-rules.md +1 -1
  9. package/docs/directory-structure.md +93 -122
  10. package/package.json +1 -1
  11. package/profiles/ai.yaml +3 -0
  12. package/profiles/android-native.yaml +2 -0
  13. package/profiles/backend-go.yaml +1 -8
  14. package/profiles/backend-ts.yaml +2 -8
  15. package/profiles/backend-typescript.yaml +51 -0
  16. package/profiles/flutter.yaml +2 -0
  17. package/profiles/frontend-react-vite.yaml +39 -0
  18. package/profiles/frontend.yaml +2 -0
  19. package/profiles/integrations.yaml +6 -1
  20. package/profiles/ios-native.yaml +2 -0
  21. package/profiles/mobile-android-kotlin.yaml +36 -0
  22. package/profiles/mobile-flutter.yaml +37 -0
  23. package/profiles/mobile-ios-swift.yaml +37 -0
  24. package/profiles/mobile-react-native.yaml +39 -0
  25. package/profiles/mobile.yaml +2 -0
  26. package/rules/gamedev/engine-code.md +1 -1
  27. package/rules/gamedev/shader-code.md +1 -2
  28. package/rules/gamedev/vn-code.md +3 -2
  29. package/rules/implementation-workflow.md +1 -1
  30. package/rules/{ai-code.md → llm-code.md} +5 -5
  31. package/skills/agent-designer/README.md +2 -1
  32. package/skills/agent-designer/agent_evaluator.py +0 -5
  33. package/skills/ai-sdk-setup/SKILL.md +4 -4
  34. package/skills/arcane-add/SKILL.md +1 -1
  35. package/skills/arcane-list/SKILL.md +3 -3
  36. package/skills/arcane-status/SKILL.md +1 -1
  37. package/skills/architecture-decision/SKILL.md +3 -4
  38. package/skills/architecture-review/SKILL.md +1 -1
  39. package/skills/asset-audit/SKILL.md +0 -1
  40. package/skills/balance-check/SKILL.md +1 -1
  41. package/skills/brainstorm/SKILL.md +3 -3
  42. package/skills/brainstorm/references/next-steps-pipeline.md +8 -9
  43. package/skills/consistency-check/SKILL.md +2 -2
  44. package/skills/design-system/SKILL.md +1 -1
  45. package/skills/design-system/references/post-design-validation.md +2 -3
  46. package/skills/freelance-kickoff/SKILL.md +1 -1
  47. package/skills/frontend-dev/SKILL.md +27 -28
  48. package/skills/frontend-dev/references/asset-prompt-guide.md +1 -1
  49. package/skills/frontend-dev/references/motion-recipes.md +1 -1
  50. package/skills/help/SKILL.md +1 -2
  51. package/skills/hotfix/SKILL.md +2 -2
  52. package/skills/milestone-review/SKILL.md +1 -1
  53. package/skills/patch-notes/SKILL.md +1 -1
  54. package/skills/playtest-report/SKILL.md +2 -3
  55. package/skills/playwright-pro/CLAUDE.md +2 -2
  56. package/skills/playwright-pro/SKILL.md +13 -9
  57. package/skills/playwright-pro/agents/migration-planner.md +2 -2
  58. package/skills/playwright-pro/agents/test-architect.md +1 -2
  59. package/skills/playwright-pro/agents/test-debugger.md +1 -1
  60. package/skills/playwright-pro/hooks/detect-playwright.sh +1 -1
  61. package/skills/playwright-pro/skills/browserstack/SKILL.md +5 -5
  62. package/skills/playwright-pro/skills/migrate/SKILL.md +1 -1
  63. package/skills/playwright-pro/skills/report/SKILL.md +2 -2
  64. package/skills/playwright-pro/skills/testrail/SKILL.md +5 -5
  65. package/skills/playwright-pro/templates/README.md +2 -2
  66. package/skills/prototype/SKILL.md +1 -1
  67. package/skills/quick-design/SKILL.md +1 -1
  68. package/skills/release-checklist/SKILL.md +0 -1
  69. package/skills/security-audit/SKILL.md +1 -1
  70. package/skills/self-improving-agent/CLAUDE.md +12 -12
  71. package/skills/self-improving-agent/SKILL.md +16 -12
  72. package/skills/self-improving-agent/agents/memory-analyst.md +1 -1
  73. package/skills/self-improving-agent/hooks/error-capture.sh +2 -2
  74. package/skills/self-improving-agent/reference/memory-architecture.md +2 -2
  75. package/skills/self-improving-agent/reference/promotion-rules.md +3 -3
  76. package/skills/self-improving-agent/reference/rules-directory-patterns.md +1 -1
  77. package/skills/self-improving-agent/settings.json +5 -5
  78. package/skills/self-improving-agent/skills/extract/SKILL.md +7 -7
  79. package/skills/self-improving-agent/skills/promote/SKILL.md +5 -5
  80. package/skills/self-improving-agent/skills/remember/SKILL.md +9 -9
  81. package/skills/self-improving-agent/skills/review/SKILL.md +7 -7
  82. package/skills/self-improving-agent/skills/status/SKILL.md +8 -8
  83. package/skills/senior-ml-engineer/references/llm_integration_guide.md +14 -31
  84. package/skills/senior-prompt-engineer/SKILL.md +10 -10
  85. package/skills/senior-prompt-engineer/references/prompt_engineering_patterns.md +1 -1
  86. package/skills/senior-prompt-engineer/scripts/agent_orchestrator.py +43 -40
  87. package/skills/senior-prompt-engineer/scripts/prompt_optimizer.py +23 -66
  88. package/skills/shader-dev/reference/fluid-simulation.md +4 -4
  89. package/skills/shader-dev/reference/simulation-physics.md +6 -6
  90. package/skills/shader-dev/reference/voxel-rendering.md +1 -1
  91. package/skills/shader-dev/techniques/ambient-occlusion.md +1 -1
  92. package/skills/shader-dev/techniques/analytic-ray-tracing.md +2 -2
  93. package/skills/shader-dev/techniques/domain-warping.md +2 -2
  94. package/skills/shader-dev/techniques/fluid-simulation.md +45 -45
  95. package/skills/shader-dev/techniques/multipass-buffer.md +24 -24
  96. package/skills/shader-dev/techniques/normal-estimation.md +1 -1
  97. package/skills/shader-dev/techniques/particle-system.md +16 -16
  98. package/skills/shader-dev/techniques/polar-uv-manipulation.md +1 -1
  99. package/skills/shader-dev/techniques/post-processing.md +7 -7
  100. package/skills/shader-dev/techniques/sdf-3d.md +1 -1
  101. package/skills/shader-dev/techniques/simulation-physics.md +24 -24
  102. package/skills/shader-dev/techniques/sound-synthesis.md +2 -2
  103. package/skills/shader-dev/techniques/voronoi-cellular-noise.md +1 -1
  104. package/skills/shader-dev/techniques/voxel-rendering.md +60 -60
  105. package/skills/smoke-check/SKILL.md +1 -1
  106. package/skills/sprint-plan/SKILL.md +1 -2
  107. package/skills/start/SKILL.md +7 -7
  108. package/skills/story-done/SKILL.md +1 -1
  109. package/skills/test-helpers/SKILL.md +7 -2
  110. package/skills/test-setup/SKILL.md +1 -1
  111. package/skills/ue-actor-component-architecture/SKILL.md +0 -1
  112. package/skills/ue-ai-navigation/SKILL.md +0 -1
  113. package/skills/ue-animation-system/SKILL.md +0 -1
  114. package/skills/ue-async-threading/SKILL.md +0 -1
  115. package/skills/ue-audio-system/SKILL.md +0 -1
  116. package/skills/ue-blueprints/SKILL.md +0 -1
  117. package/skills/ue-character-movement/SKILL.md +0 -1
  118. package/skills/ue-cpp-foundations/SKILL.md +0 -1
  119. package/skills/ue-data-assets-tables/SKILL.md +0 -1
  120. package/skills/ue-editor-tools/SKILL.md +0 -1
  121. package/skills/ue-game-features/SKILL.md +0 -1
  122. package/skills/ue-gameplay-abilities/SKILL.md +0 -1
  123. package/skills/ue-gameplay-framework/SKILL.md +0 -1
  124. package/skills/ue-input-system/SKILL.md +0 -1
  125. package/skills/ue-mass-entity/SKILL.md +0 -1
  126. package/skills/ue-materials-rendering/SKILL.md +0 -1
  127. package/skills/ue-module-build-system/SKILL.md +0 -1
  128. package/skills/ue-naming-conventions/SKILL.md +0 -1
  129. package/skills/ue-networking-replication/SKILL.md +0 -1
  130. package/skills/ue-niagara-effects/SKILL.md +0 -1
  131. package/skills/ue-packaging/SKILL.md +0 -1
  132. package/skills/ue-physics-collision/SKILL.md +0 -1
  133. package/skills/ue-procedural-generation/SKILL.md +0 -1
  134. package/skills/ue-project-context/SKILL.md +0 -1
  135. package/skills/ue-sequencer-cinematics/SKILL.md +0 -1
  136. package/skills/ue-serialization-savegames/SKILL.md +0 -1
  137. package/skills/ue-state-trees/SKILL.md +0 -1
  138. package/skills/ue-testing-debugging/SKILL.md +0 -1
  139. package/skills/ue-ui-umg-slate/SKILL.md +0 -1
  140. package/skills/ue-world-level-streaming/SKILL.md +0 -1
  141. package/skills/ux-review/SKILL.md +1 -1
  142. package/docs/RELEASE-SETUP.md +0 -99
  143. package/docs/presentations/arcane-overview-12.pptx +0 -0
  144. package/docs/presentations/arcane-overview.pptx +0 -0
  145. package/docs/presentations/build_arcane_deck.py +0 -310
  146. package/docs/presentations/build_arcane_deck_12.py +0 -399
  147. package/docs/trusted-publishing-setup.md +0 -163
@@ -50,12 +50,12 @@ Numerical viscosity over-smooths small-scale vortices. Vorticity confinement com
50
50
 
51
51
  **Why**: GPU shaders cannot simultaneously read and write the same buffer. The ping-pong strategy reads from one buffer (previous frame's data) and writes to the other each frame, then swaps on the next frame.
52
52
 
53
- **IMPORTANT: 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. However, in WebGL2 there's only one shader program doing ping-pong, and the write target texture cannot be simultaneously read. The solution is **dual-channel encoding** (R=current height, G=previous frame height).
53
+ **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. However, in WebGL2 there's only one shader program doing ping-pong, and the write target texture cannot be simultaneously read. The solution is **dual-channel encoding** (R=current height, G=previous frame height).
54
54
 
55
55
  **Code** (WebGL2-safe version, reads only from iChannel0, with RGBA8-compatible encoding):
56
56
  ```glsl
57
- // IMPORTANT: Only use iChannel0 (read currentBuf), write to nextBuf (must be different!)
58
- // IMPORTANT: encode/decode ensure signed values aren't clipped on RGBA8 (no float textures/SwiftShader)
57
+ // Only use iChannel0 (read currentBuf), write to nextBuf (must be different!)
58
+ // encode/decode ensure signed values aren't clipped on RGBA8 (no float textures/SwiftShader)
59
59
  uniform int useFloatTex;
60
60
  float decode(float v) { return useFloatTex == 1 ? v : v * 2.0 - 1.0; }
61
61
  float encode(float v) { return useFloatTex == 1 ? v : v * 0.5 + 0.5; }
@@ -244,7 +244,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord)
244
244
  #define SIZY 64.0 // adjustable: cloth height (particle count)
245
245
 
246
246
  // Left half [0, SIZX) stores positions, right half [SIZX, 2*SIZX) stores velocities
247
- // IMPORTANT: In WebGL2, getpos/getvel both read from iChannel0 (currentBuf, read-only),
247
+ // In WebGL2, getpos/getvel both read from iChannel0 (currentBuf, read-only),
248
248
  // write target is nextBuf (separate buffer), avoiding read-write conflict
249
249
  vec3 getpos(vec2 id)
250
250
  {
@@ -298,7 +298,7 @@ void edge(vec2 dif)
298
298
  vec3 posdif = getpos(dif + c) - pos;
299
299
  vec3 veldif = getvel(dif + c) - ovel;
300
300
 
301
- // IMPORTANT: Must check for zero length, otherwise normalize(vec3(0)) produces NaN
301
+ // Must check for zero length, otherwise normalize(vec3(0)) produces NaN
302
302
  float plen = length(posdif);
303
303
  if (plen < 0.0001) return;
304
304
  vec3 dir = posdif / plen;
@@ -498,7 +498,7 @@ if (length(pos - ballPos) < ballRadius) {
498
498
  }
499
499
  ```
500
500
 
501
- > **IMPORTANT: Common Pitfalls**:
501
+ > **Common Pitfalls**:
502
502
  > - **Cloth Image Pass must project world coordinates to screen**: You cannot use `uv * vec2(SIZX, SIZY)` to map screen UV to grid ID, because particles have moved from their initial positions, producing scattered fragments. You must iterate over mesh faces, projecting vertex world coordinates to screen space for triangle rasterization
503
503
  > - GLSL is strictly typed; you cannot write `float / vec2`. Wrong example: `length(dif) / vec2(SIZX, SIZY).x` will first execute float/vec2 causing a compile error; use `length(dif) / SIZX` instead
504
504
  > - `normalize(vec3(0))` produces NaN; all `normalize()` calls must include a length check beforehand
@@ -619,7 +619,7 @@ A complete voxel water scene with surface wave reflections, underwater refractio
619
619
  float waterY = 0.0;
620
620
 
621
621
  // Underwater voxel scene (sand + seaweed)
622
- // IMPORTANT: c.xz returns vec2, which only has .x/.y components — never use .z!
622
+ // c.xz returns vec2, which only has .x/.y components — never use .z!
623
623
  int getVoxel(vec3 c) {
624
624
  float sandHeight = -3.0 + 0.5 * sin(c.x * 0.3) * cos(c.z * 0.4);
625
625
  if (c.y < sandHeight) return 1; // Sand interior
@@ -1,6 +1,6 @@
1
1
  ## WebGL2 Adaptation Requirements
2
2
 
3
- **IMPORTANT: GLSL Type Strictness**: float and vec types cannot be implicitly converted. `vec3 v = 1.0;` is illegal; you must use the vector form (e.g., `vec3(1.0)`, `vec3(1.0) * x`, `value * vec3(1.0)`).
3
+ **GLSL Type Strictness**: float and vec types cannot be implicitly converted. `vec3 v = 1.0;` is illegal; you must use the vector form (e.g., `vec3(1.0)`, `vec3(1.0) * x`, `value * vec3(1.0)`).
4
4
 
5
5
  The code templates in this document use ShaderToy GLSL style. When generating standalone HTML pages, you must adapt for WebGL2:
6
6
 
@@ -193,7 +193,7 @@ vec3 shade(vec3 pos, vec3 normal, vec3 rd, vec3 albedo) {
193
193
  }
194
194
  ```
195
195
 
196
- > **IMPORTANT: Critical pitfall**: `d.xy` must be passed as distBound, and `d.y` must be updated each time a closer intersection is found! If the deployed code passes the original `dist` directly without updating, the intersection logic will fail (all object distance tests become invalid), resulting in a completely black screen.
196
+ > **Critical pitfall**: `d.xy` must be passed as distBound, and `d.y` must be updated each time a closer intersection is found! If the deployed code passes the original `dist` directly without updating, the intersection logic will fail (all object distance tests become invalid), resulting in a completely black screen.
197
197
 
198
198
  ```glsl
199
199
  #define MAX_BOUNCES 4
@@ -229,7 +229,7 @@ vec3 radiance(vec3 ro, vec3 rd) {
229
229
 
230
230
  Runs directly on ShaderToy, includes sphere, plane, and box primitives with reflection and Blinn-Phong shading.
231
231
 
232
- > **IMPORTANT: Must follow**: All intersection function calls must use `d.xy` as the `distBound` parameter, and update `d.y` after each closer intersection is found. Incorrect usage: `iSphere(ro, rd, dist, ...)` (always using the original dist). Correct usage: `iSphere(ro, rd, d.xy, ...)` followed by `if (t < d.y) { d.y = t; ... }` to update.
232
+ > **Must follow**: All intersection function calls must use `d.xy` as the `distBound` parameter, and update `d.y` after each closer intersection is found. Incorrect usage: `iSphere(ro, rd, dist, ...)` (always using the original dist). Correct usage: `iSphere(ro, rd, d.xy, ...)` followed by `if (t < d.y) { d.y = t; ... }` to update.
233
233
 
234
234
  ```glsl
235
235
  // Analytic Ray Tracing - Complete ShaderToy Template
@@ -117,8 +117,8 @@ float fbm(vec2 p) {
117
117
 
118
118
  ```glsl
119
119
  // Map scalar field (0~1) to color using a mix chain
120
- // IMPORTANT: Note: GLSL is strictly typed. Variable declarations must be complete, e.g. vec3 col = vec3(0.2, 0.1, 0.4)
121
- // IMPORTANT: Decimals must be written as 0.x, not .x (division by zero errors)
120
+ // GLSL is strictly typed. Variable declarations must be complete, e.g. vec3 col = vec3(0.2, 0.1, 0.4)
121
+ // Decimals must be written as 0.x, not .x (division by zero errors)
122
122
  vec3 palette(float t) {
123
123
  vec3 col = vec3(0.2, 0.1, 0.4); // deep purple base
124
124
  col = mix(col, vec3(0.3, 0.05, 0.05), t); // dark red
@@ -13,23 +13,23 @@ const source = document.getElementById('fs').textContent.trim();
13
13
  // ...
14
14
  ```
15
15
 
16
- ### IMPORTANT: Float Texture Compatibility (Most Critical Issue for Fluid Simulation)
16
+ ### Float Texture Compatibility (Most Critical Issue for Fluid Simulation)
17
17
 
18
18
  Fluid simulation requires float textures to store velocity (can be negative), pressure, and ink concentration (can exceed 1.0).
19
19
 
20
- **IMPORTANT: Must use RGBA16F instead of RGBA32F**: Many environments (headless Chrome, SwiftShader, mobile) do not support `RGBA32F` render targets. Even when the `EXT_color_buffer_float` extension claims to be available, `RGBA32F` FBOs may silently fail (framebuffer reports complete but renders all zeros or all ones). `RGBA16F + HALF_FLOAT` has far better compatibility than `RGBA32F`, and its precision is more than sufficient for fluid simulation.
20
+ **Must use RGBA16F instead of RGBA32F**: Many environments (headless Chrome, SwiftShader, mobile) do not support `RGBA32F` render targets. Even when the `EXT_color_buffer_float` extension claims to be available, `RGBA32F` FBOs may silently fail (framebuffer reports complete but renders all zeros or all ones). `RGBA16F + HALF_FLOAT` has far better compatibility than `RGBA32F`, and its precision is more than sufficient for fluid simulation.
21
21
 
22
22
  ```javascript
23
23
  const gl = canvas.getContext("webgl2");
24
24
  if (!gl) { /* error handling */ }
25
25
 
26
26
  const ext = gl.getExtension("EXT_color_buffer_float");
27
- // IMPORTANT: Continue even if ext is null — some environments support RGBA16F without this extension
27
+ // Continue even if ext is null — some environments support RGBA16F without this extension
28
28
 
29
29
  function createFloatTexture(w, h) {
30
30
  const tex = gl.createTexture();
31
31
  gl.bindTexture(gl.TEXTURE_2D, tex);
32
- // IMPORTANT: Must use RGBA16F + HALF_FLOAT, do NOT use RGBA32F + FLOAT
32
+ // Must use RGBA16F + HALF_FLOAT, do NOT use RGBA32F + FLOAT
33
33
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, w, h, 0, gl.RGBA, gl.HALF_FLOAT, null);
34
34
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
35
35
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
@@ -57,7 +57,7 @@ function createFBO(w, h) {
57
57
  Fluid simulation requires tracking mouse position and drag direction. iMouse uniform convention: **xy=current mouse position, z=mouse down flag (>0 means pressed), w=unused**. Mouse velocity is calculated from the position difference between current and previous frames:
58
58
 
59
59
  ```javascript
60
- // IMPORTANT: iMouse convention: xy=current position, z=pressed flag (1.0=down, 0.0=up), w=0
60
+ // iMouse convention: xy=current position, z=pressed flag (1.0=down, 0.0=up), w=0
61
61
  // Mouse velocity is computed via prevMouse on the JS side, passed through a separate uniform
62
62
  let iMouse = [0, 0, 0, 0]; // [x, y, pressed, 0]
63
63
  let prevMouse = [0, 0];
@@ -93,7 +93,7 @@ canvas.addEventListener('mouseup', () => {
93
93
  // Pass uniforms in render loop
94
94
  // iMouse: xy=position, z=pressed flag, w=0
95
95
  gl.uniform4f(uMouse, iMouse[0], iMouse[1], iMouse[2], 0.0);
96
- // IMPORTANT: Mouse velocity must be clamped, otherwise fast dragging produces huge velocity deltas causing NaN explosion
96
+ // Mouse velocity must be clamped, otherwise fast dragging produces huge velocity deltas causing NaN explosion
97
97
  const mvx = Math.max(-50, Math.min(50, iMouse[0] - prevMouse[0]));
98
98
  const mvy = Math.max(-50, Math.min(50, iMouse[1] - prevMouse[1]));
99
99
  gl.uniform2f(uMouseVel, mvx, mvy);
@@ -211,9 +211,9 @@ c.z -= DT * (dx.z * c.x + dy.z * c.y + div * c.z);
211
211
 
212
212
  ### Step 6: Mouse Interaction
213
213
  ```glsl
214
- // IMPORTANT: iMouse.z is the mouse-down flag (>0=pressed), not a position coordinate
214
+ // iMouse.z is the mouse-down flag (>0=pressed), not a position coordinate
215
215
  // iMouseVel is mouse movement velocity, passed via a separate uniform
216
- // IMPORTANT: Must clamp mouseVel to prevent NaN explosion
216
+ // Must clamp mouseVel to prevent NaN explosion
217
217
  if (iMouse.z > 0.0) {
218
218
  vec2 mouseVel = clamp(iMouseVel, vec2(-50.0), vec2(50.0));
219
219
  float dist2 = dot(p - iMouse.xy, p - iMouse.xy);
@@ -225,7 +225,7 @@ if (iMouse.z > 0.0) {
225
225
 
226
226
  ### Step 6b: Vorticity Confinement (Required for Ink Curling Effects)
227
227
  ```glsl
228
- // IMPORTANT: Ink diffusion/swirl effects require vorticity confinement, otherwise small vortices dissipate quickly leaving only smooth flow
228
+ // Ink diffusion/swirl effects require vorticity confinement, otherwise small vortices dissipate quickly leaving only smooth flow
229
229
  // Vorticity confinement re-injects energy into small-scale vortices, producing characteristic curling textures
230
230
  #define VORT_STR 0.035 // [0.01=subtle, 0.05=noticeable, 0.1=strong]
231
231
  float curl_c = dx.y - dy.x;
@@ -243,9 +243,9 @@ c.xy += DT * VORT_STR * vec2(eta.y, -eta.x) * curl_c;
243
243
 
244
244
  ### Step 7: Automatic Ink Sources (Critical: Ensures Visible Output Without Interaction)
245
245
  ```glsl
246
- // IMPORTANT: Must have automatic ink sources! Otherwise the screen is completely black without mouse interaction
247
- // IMPORTANT: Ink injection and decay must be balanced! Too-strong injection or too-weak decay causes ink saturation across the entire screen → solid color with no features
248
- // IMPORTANT: Gaussian denominator controls emitter radius — larger denominator means larger emitter!
246
+ // Must have automatic ink sources! Otherwise the screen is completely black without mouse interaction
247
+ // Ink injection and decay must be balanced! Too-strong injection or too-weak decay causes ink saturation across the entire screen → solid color with no features
248
+ // Gaussian denominator controls emitter radius — larger denominator means larger emitter!
249
249
  // Denominator > 300 makes emitter cover most of the screen, ink saturates quickly
250
250
  // Recommended 100~200, keeping it locally concentrated with visible gradient falloff at distance
251
251
  float t = iTime;
@@ -277,7 +277,7 @@ if (p.x < 1.0 || p.y < 1.0 ||
277
277
  c.xyw *= 0.0;
278
278
  }
279
279
 
280
- // IMPORTANT: Ink decay — must use multiplicative decay (e.g., *= 0.99), NOT subtractive decay (-= constant)
280
+ // Ink decay — must use multiplicative decay (e.g., *= 0.99), NOT subtractive decay (-= constant)
281
281
  // Subtractive decay zeros out quickly at small ink values and decays too slowly at large values, causing saturation
282
282
  // Multiplicative decay scales proportionally, maintaining contrast at any concentration
283
283
  c.w *= 0.99; // 1% decay per frame, adjustable [0.98=fast dissipation, 0.995=persistent]
@@ -291,19 +291,19 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
291
291
  vec2 uv = fragCoord / iResolution.xy;
292
292
  vec4 c = texture(iChannel0, uv);
293
293
 
294
- // IMPORTANT: Color base must be bright enough! 0.5+0.5*cos produces [0,1] range bright colors
294
+ // Color base must be bright enough! 0.5+0.5*cos produces [0,1] range bright colors
295
295
  // Never use vec3(0.02, 0.01, 0.08) or similar near-zero base colors — they become invisible when multiplied by ink
296
296
  float angle = atan(c.y, c.x);
297
297
  vec3 col = 0.5 + 0.5 * cos(angle + vec3(0.0, 2.1, 4.2));
298
298
 
299
- // IMPORTANT: Use smoothstep to map ink concentration; upper limit should exceed actual ink range to preserve gradients
299
+ // Use smoothstep to map ink concentration; upper limit should exceed actual ink range to preserve gradients
300
300
  float ink = smoothstep(0.0, 2.0, c.w);
301
301
  col *= ink;
302
302
 
303
303
  // Pressure highlights
304
304
  col += vec3(0.05) * clamp(c.z - 1.0, 0.0, 1.0);
305
305
 
306
- // IMPORTANT: Background color must be visible (RGB at least > 5/255 ≈ 0.02), otherwise users think the page is all black
306
+ // Background color must be visible (RGB at least > 5/255 ≈ 0.02), otherwise users think the page is all black
307
307
  col = max(col, vec3(0.02, 0.012, 0.035));
308
308
 
309
309
  fragColor = vec4(col, 1.0);
@@ -315,7 +315,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
315
315
  Lava/fire requires FBM noise for turbulent textures + temperature color band mapping. **Key: Must use FBM noise to distort UV coordinates and temperature values, otherwise the image is too smooth and looks like a plain gradient rather than lava.**
316
316
 
317
317
  ```glsl
318
- // IMPORTANT: FBM noise is the core of lava/fire visualization! Without it the image is a smooth gradient with no lava texture
318
+ // FBM noise is the core of lava/fire visualization! Without it the image is a smooth gradient with no lava texture
319
319
  // These noise functions must be defined in the Image Pass
320
320
 
321
321
  float hash(vec2 p) {
@@ -333,7 +333,7 @@ float noise(vec2 p) {
333
333
  return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
334
334
  }
335
335
 
336
- // IMPORTANT: octaves=4~6 produces sufficient detail; fewer than 3 gives too-coarse textures
336
+ // octaves=4~6 produces sufficient detail; fewer than 3 gives too-coarse textures
337
337
  float fbm(vec2 p, int octaves) {
338
338
  float val = 0.0;
339
339
  float amp = 0.5;
@@ -360,7 +360,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
360
360
  float speed = length(c.xy);
361
361
 
362
362
  // Temperature = ink concentration + noise perturbation + speed contribution
363
- // IMPORTANT: Noise perturbation amplitude of 0.2~0.4 produces visible texture without becoming noisy
363
+ // Noise perturbation amplitude of 0.2~0.4 produces visible texture without becoming noisy
364
364
  float temp = ink * 0.7 + n1 * 0.25 + speed * 0.1;
365
365
  // Second noise layer for cracks/dark veins
366
366
  temp -= (1.0 - n2) * 0.15 * ink;
@@ -399,14 +399,14 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
399
399
  vec2 uv = fragCoord / iResolution.xy;
400
400
  vec2 texel = 1.0 / iResolution.xy;
401
401
 
402
- // IMPORTANT: Sample from wave height field: .g channel stores current frame height
402
+ // Sample from wave height field: .g channel stores current frame height
403
403
  float h = texture(iChannel0, uv).g;
404
404
  float hn = texture(iChannel0, uv + vec2(0.0, texel.y)).g;
405
405
  float hs = texture(iChannel0, uv - vec2(0.0, texel.y)).g;
406
406
  float he = texture(iChannel0, uv + vec2(texel.x, 0.0)).g;
407
407
  float hw = texture(iChannel0, uv - vec2(texel.x, 0.0)).g;
408
408
 
409
- // IMPORTANT: Normal perturbation factor must be large enough (50~100), otherwise ripples are invisible
409
+ // Normal perturbation factor must be large enough (50~100), otherwise ripples are invisible
410
410
  // If drop strength is 1.0 and radius is 8~15px, using 80.0 produces clearly visible ripples
411
411
  vec3 normal = normalize(vec3((hw - he) * 80.0, (hs - hn) * 80.0, 1.0));
412
412
 
@@ -417,7 +417,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
417
417
  float diffuse = max(dot(normal, lightDir), 0.0);
418
418
  float specular = pow(max(dot(normal, halfVec), 0.0), 64.0);
419
419
 
420
- // IMPORTANT: Water base color must be bright enough! Deep color no darker than vec3(0.02, 0.08, 0.2), shallow color use vec3(0.1, 0.3, 0.6)
420
+ // Water base color must be bright enough! Deep color no darker than vec3(0.02, 0.08, 0.2), shallow color use vec3(0.1, 0.3, 0.6)
421
421
  vec3 deepColor = vec3(0.02, 0.08, 0.22);
422
422
  vec3 shallowColor = vec3(0.1, 0.35, 0.65);
423
423
  vec3 waterColor = mix(deepColor, shallowColor, 0.5 + h * 5.0);
@@ -439,7 +439,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
439
439
  }
440
440
  ```
441
441
 
442
- ## IMPORTANT: Common Fatal Errors
442
+ ## Common Fatal Errors
443
443
 
444
444
  1. **RGBA32F silently fails in headless/SwiftShader environments**: Must use `RGBA16F + HALF_FLOAT`
445
445
  2. **Ink saturates entire screen**: Gaussian denominator too large (>300) or decay too weak (>0.995). Fix: denominator 100~200, decay `*= 0.99`
@@ -464,7 +464,7 @@ Fluid simulation requires framebuffer self-feedback + float textures. The follow
464
464
  <meta name="viewport" content="width=device-width,initial-scale=1">
465
465
  <title>Fluid Simulation</title>
466
466
  <style>
467
- /* IMPORTANT: Critical: canvas must fill the viewport, otherwise it may be invisible or clipped */
467
+ /* canvas must fill the viewport, otherwise it may be invisible or clipped */
468
468
  *{margin:0;padding:0}
469
469
  html,body{width:100%;height:100%;overflow:hidden;background:#000}
470
470
  canvas{display:block;width:100%;height:100%}
@@ -474,7 +474,7 @@ canvas{display:block;width:100%;height:100%}
474
474
  <canvas id="c"></canvas>
475
475
  <script>
476
476
  let frameCount = 0;
477
- // IMPORTANT: iMouse convention: [x, y, pressedFlag, 0] — z is the pressed flag (1 or 0), not a coordinate
477
+ // iMouse convention: [x, y, pressedFlag, 0] — z is the pressed flag (1 or 0), not a coordinate
478
478
  let mouse = [0, 0, 0, 0];
479
479
  let prevMouse = [0, 0];
480
480
  let mouseDown = false;
@@ -510,7 +510,7 @@ in vec2 pos;
510
510
  void main(){ gl_Position=vec4(pos,0,1); }`;
511
511
 
512
512
  // fsBuffer / fsImage: adapt from the Buffer A / Image templates below
513
- // IMPORTANT: Fragment shaders must declare these uniforms:
513
+ // Fragment shaders must declare these uniforms:
514
514
  // uniform sampler2D iChannel0;
515
515
  // uniform vec2 iResolution;
516
516
  // uniform float iTime;
@@ -521,7 +521,7 @@ void main(){ gl_Position=vec4(pos,0,1); }`;
521
521
  const progBuf = createProgram(vsSource, fsBuffer);
522
522
  const progImg = createProgram(vsSource, fsImage);
523
523
 
524
- // IMPORTANT: Must use RGBA16F + HALF_FLOAT, do NOT use RGBA32F + FLOAT
524
+ // Must use RGBA16F + HALF_FLOAT, do NOT use RGBA32F + FLOAT
525
525
  // RGBA32F may render all zeros in headless Chrome / SwiftShader even when framebuffer reports complete
526
526
  function createFBO(w, h) {
527
527
  const tex = gl.createTexture();
@@ -573,12 +573,12 @@ canvas.addEventListener('mousedown', e => {
573
573
  const dpr = Math.min(devicePixelRatio, 1.5);
574
574
  mouse[0] = e.clientX * dpr;
575
575
  mouse[1] = H - e.clientY * dpr;
576
- mouse[2] = 1.0; // IMPORTANT: Pressed flag, not a coordinate
576
+ mouse[2] = 1.0; // Pressed flag, not a coordinate
577
577
  prevMouse[0] = mouse[0]; prevMouse[1] = mouse[1];
578
578
  });
579
579
  canvas.addEventListener('mouseup', () => {
580
580
  mouseDown = false;
581
- mouse[2] = 0.0; // IMPORTANT: Released flag
581
+ mouse[2] = 0.0; // Released flag
582
582
  });
583
583
 
584
584
  // Touch events (mobile)
@@ -628,7 +628,7 @@ function render(t) {
628
628
  gl.uniform1f(uBuf.time, t);
629
629
  gl.uniform1i(uBuf.frame, frameCount);
630
630
  gl.uniform4f(uBuf.mouse, mouse[0], mouse[1], mouse[2], 0.0);
631
- // IMPORTANT: Must clamp mouse velocity! Fast movement or first-frame clicks can produce huge velocity values,
631
+ // Must clamp mouse velocity! Fast movement or first-frame clicks can produce huge velocity values,
632
632
  // causing shader velocity explosion → NaN propagation → page crash
633
633
  const mvx = Math.max(-50, Math.min(50, mouse[0] - prevMouse[0]));
634
634
  const mvy = Math.max(-50, Math.min(50, mouse[1] - prevMouse[1]));
@@ -699,7 +699,7 @@ void mainImage(out vec4 fragColor, in vec2 p) {
699
699
  c.xy -= K * vec2(dx.z, dy.z);
700
700
 
701
701
  // Mouse interaction: iMouse.z is the pressed flag (>0), velocity obtained via iMouseVel uniform
702
- // IMPORTANT: mouseVel must be clamped to prevent NaN explosion (JS side should also clamp — double safety)
702
+ // mouseVel must be clamped to prevent NaN explosion (JS side should also clamp — double safety)
703
703
  if (iMouse.z > 0.0) {
704
704
  vec2 mouseVel = clamp(iMouseVel, vec2(-50.0), vec2(50.0));
705
705
  float dist2 = dot(p - iMouse.xy, p - iMouse.xy);
@@ -709,7 +709,7 @@ void mainImage(out vec4 fragColor, in vec2 p) {
709
709
  }
710
710
 
711
711
  // Vorticity confinement: prevents small vortices from dissipating too quickly, producing curling textures
712
- // IMPORTANT: Ink diffusion/swirl effects (e.g., ink diffusing in water) require vorticity confinement, otherwise curl dissipates quickly leaving only smooth flow
712
+ // Ink diffusion/swirl effects (e.g., ink diffusing in water) require vorticity confinement, otherwise curl dissipates quickly leaving only smooth flow
713
713
  float curl_c = dx.y - dy.x;
714
714
  float curl_n = (T(p + vec2(1,1)).y - T(p + vec2(-1,1)).y) / 2.0
715
715
  - (T(p + vec2(0,2)).x - T(p).x) / 2.0;
@@ -723,7 +723,7 @@ void mainImage(out vec4 fragColor, in vec2 p) {
723
723
  eta = normalize(eta + vec2(1e-5));
724
724
  c.xy += DT * 0.035 * vec2(eta.y, -eta.x) * curl_c;
725
725
 
726
- // IMPORTANT: Automatic ink sources: ensure visible fluid motion without mouse interaction
726
+ // Automatic ink sources: ensure visible fluid motion without mouse interaction
727
727
  // Emitter positions must move over time, and Gaussian radius must be small enough to maintain locality
728
728
  float t = iTime;
729
729
  vec2 em1 = iResolution.xy * vec2(0.25, 0.5 + 0.2 * sin(t * 0.7));
@@ -739,7 +739,7 @@ void mainImage(out vec4 fragColor, in vec2 p) {
739
739
  c.xy += DT * r3 * vec2(sin(t * 1.1), -cos(t)) * 2.0;
740
740
  c.w += DT * (r1 + r2 + r3) * 2.0;
741
741
 
742
- // IMPORTANT: Ink decay: must use multiplicative decay, do NOT use subtractive (subtractive causes saturation)
742
+ // Ink decay: must use multiplicative decay, do NOT use subtractive (subtractive causes saturation)
743
743
  c.w *= 0.99;
744
744
 
745
745
  c = clamp(c, vec4(-5, -5, 0.5, 0), vec4(5, 5, 3, 5));
@@ -762,19 +762,19 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
762
762
  vec2 uv = fragCoord / iResolution.xy;
763
763
  vec4 c = texture(iChannel0, uv);
764
764
 
765
- // IMPORTANT: Color base must be bright enough! 0.5+0.5*cos produces [0,1] range bright colors
765
+ // Color base must be bright enough! 0.5+0.5*cos produces [0,1] range bright colors
766
766
  // Never use vec3(0.02, 0.01, 0.08) or similar extremely dark base colors — they become invisible when multiplied by ink
767
767
  float angle = atan(c.y, c.x);
768
768
  vec3 col = 0.5 + 0.5 * cos(angle + vec3(0.0, 2.1, 4.2));
769
769
 
770
- // IMPORTANT: smoothstep upper limit should cover actual ink range to preserve gradient variation
770
+ // smoothstep upper limit should cover actual ink range to preserve gradient variation
771
771
  float ink = smoothstep(0.0, 2.0, c.w);
772
772
  col *= ink;
773
773
 
774
774
  // Pressure highlights
775
775
  col += vec3(0.05) * clamp(c.z - 1.0, 0.0, 1.0);
776
776
 
777
- // IMPORTANT: Background color must be visible (RGB at least > 5/255 ≈ 0.02), otherwise users think the page is all black
777
+ // Background color must be visible (RGB at least > 5/255 ≈ 0.02), otherwise users think the page is all black
778
778
  col = max(col, vec3(0.02, 0.012, 0.035));
779
779
 
780
780
  fragColor = vec4(col, 1.0);
@@ -785,7 +785,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
785
785
 
786
786
  Water surface ripples use the wave equation rather than Navier-Stokes. Clicks/touches generate concentric ripples that interfere with each other and gradually decay.
787
787
 
788
- **IMPORTANT: Water ripple drop injection must be implemented directly in the shader using iMouse**, do not use custom uniform arrays to pass click positions — that adds complexity on both JS/GLSL sides and is error-prone (uniform location not found, array length mismatch, etc.).
788
+ **Water ripple drop injection must be implemented directly in the shader using iMouse**, do not use custom uniform arrays to pass click positions — that adds complexity on both JS/GLSL sides and is error-prone (uniform location not found, array length mismatch, etc.).
789
789
 
790
790
  ### Water Ripple Buffer Pass (Wave Equation Solver)
791
791
 
@@ -793,7 +793,7 @@ Water surface ripples use the wave equation rather than Navier-Stokes. Clicks/to
793
793
  // Water Ripple — Buffer Pass (Wave Equation Solver)
794
794
  // Data encoding: .r = previous frame height (prev), .g = current frame height (curr)
795
795
  // iChannel0 = self-feedback (ping-pong)
796
- // IMPORTANT: Drop injection is done directly in the shader via iMouse, no custom uniforms needed
796
+ // Drop injection is done directly in the shader via iMouse, no custom uniforms needed
797
797
 
798
798
  void main() {
799
799
  vec2 p = gl_FragCoord.xy;
@@ -823,7 +823,7 @@ void main() {
823
823
  float damping = 0.996;
824
824
  next *= damping;
825
825
 
826
- // IMPORTANT: Mouse click drop injection — directly using iMouse, simple and reliable
826
+ // Mouse click drop injection — directly using iMouse, simple and reliable
827
827
  // iMouse.z > 0 indicates mouse is pressed
828
828
  if (iMouse.z > 0.0) {
829
829
  float dist = length(p - iMouse.xy);
@@ -832,7 +832,7 @@ void main() {
832
832
  next += strength * exp(-dist * dist / (2.0 * radius * radius));
833
833
  }
834
834
 
835
- // IMPORTANT: Automatic ripples: ensure visible ripples even without interaction
835
+ // Automatic ripples: ensure visible ripples even without interaction
836
836
  // Use periodic functions of iTime to control auto-drop position and timing
837
837
  float autoPhase = iTime * 0.5;
838
838
  float autoPeriod = fract(autoPhase);
@@ -854,7 +854,7 @@ void main() {
854
854
  next *= 0.0;
855
855
  }
856
856
 
857
- // IMPORTANT: Output: .r = current frame (becomes next frame's prev), .g = newly computed (becomes next frame's curr)
857
+ // Output: .r = current frame (becomes next frame's prev), .g = newly computed (becomes next frame's curr)
858
858
  fragColor = vec4(curr, next, 0.0, 1.0);
859
859
  }
860
860
  ```
@@ -877,7 +877,7 @@ See Step 9c above.
877
877
 
878
878
  When multiple ink colors need to interpenetrate and blend, a single scalar `c.w` is insufficient. You need **two Buffers**: Buffer A stores velocity/pressure (same as above), Buffer B stores RGB three-channel ink concentration, sharing the same velocity field for advection.
879
879
 
880
- **IMPORTANT: Key for multi-color ink: Buffer B's RGB channels independently store the concentration of each ink color, using Buffer A's velocity field for semi-Lagrangian advection. Different ink colors naturally blend during advection and diffusion.**
880
+ **Key for multi-color ink: Buffer B's RGB channels independently store the concentration of each ink color, using Buffer A's velocity field for semi-Lagrangian advection. Different ink colors naturally blend during advection and diffusion.**
881
881
 
882
882
  ### JS Side Changes (Three Buffer Ping-Pong)
883
883
 
@@ -1020,7 +1020,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
1020
1020
  vec3 mapped = smoothstep(vec3(0.0), vec3(2.5), ink);
1021
1021
 
1022
1022
  // Color mapping: map RGB concentrations to actual visible colors
1023
- // IMPORTANT: Base colors must be bright, do not use extremely dark values
1023
+ // Base colors must be bright, do not use extremely dark values
1024
1024
  vec3 col1 = vec3(0.9, 0.15, 0.2); // red ink
1025
1025
  vec3 col2 = vec3(0.1, 0.8, 0.3); // green ink
1026
1026
  vec3 col3 = vec3(0.15, 0.3, 0.95); // blue ink
@@ -1040,7 +1040,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
1040
1040
  }
1041
1041
  ```
1042
1042
 
1043
- **IMPORTANT: The multi-color ink Buffer A velocity field template is identical to the single-color version**, except `c.w` is no longer used for ink (ink is in Buffer B). Buffer A only handles velocity + pressure.
1043
+ **The multi-color ink Buffer A velocity field template is identical to the single-color version**, except `c.w` is no longer used for ink (ink is in Buffer B). Buffer A only handles velocity + pressure.
1044
1044
 
1045
1045
  ## Common Variants
1046
1046
 
@@ -1,6 +1,6 @@
1
1
  ### Standalone HTML Complete Shader Template (Must Be Strictly Followed)
2
2
 
3
- **IMPORTANT: The following template can be copied directly; every line must be strictly followed**:
3
+ **The following template can be copied directly; every line must be strictly followed**:
4
4
 
5
5
  **Vertex Shader** (common to all shaders):
6
6
  ```glsl
@@ -16,13 +16,13 @@ void main() {
16
16
  #version 300 es
17
17
  precision highp float;
18
18
 
19
- // IMPORTANT: Critical: uniforms must be declared; ShaderToy's iTime/iResolution etc. are global variables
19
+ // uniforms must be declared; ShaderToy's iTime/iResolution etc. are global variables
20
20
  uniform float iTime;
21
21
  uniform vec2 iResolution;
22
22
  uniform int iFrame;
23
23
  uniform vec4 iMouse;
24
24
 
25
- // IMPORTANT: Critical: mainImage parameters need manual extraction
25
+ // mainImage parameters need manual extraction
26
26
  // ShaderToy: void mainImage(out vec4 fragColor, in vec2 fragCoord)
27
27
  // Adapted to:
28
28
  out vec4 fragColor;
@@ -30,7 +30,7 @@ void main() {
30
30
  vec2 fragCoord = gl_FragCoord.xy;
31
31
  vec2 uv = fragCoord / iResolution;
32
32
 
33
- // IMPORTANT: Critical: texture2D → texture
33
+ // texture2D → texture
34
34
  vec4 prev = texture(iChannel0, uv);
35
35
 
36
36
  // ... particle physics logic ...
@@ -56,7 +56,7 @@ void main() {
56
56
  vec2 fragCoord = gl_FragCoord.xy;
57
57
  vec2 uv = fragCoord / iResolution;
58
58
 
59
- // IMPORTANT: Critical: texture2D → texture, mainImage → standard main
59
+ // texture2D → texture, mainImage → standard main
60
60
  vec4 col = texture(iChannel0, uv);
61
61
 
62
62
  // Rendering logic
@@ -66,7 +66,7 @@ void main() {
66
66
  }
67
67
  ```
68
68
 
69
- **IMPORTANT: Common GLSL ES 3.00 Errors** (must be avoided):
69
+ **Common GLSL ES 3.00 Errors** (must be avoided):
70
70
  1. **#version must be on the first line** - Any comments/blank lines will cause "version directive must occur on the first line" error
71
71
  2. **in/out qualifiers** - WebGL1's attribute/varying must be changed to in/out in ES3
72
72
  3. **texture function** - ES3 uses `texture(sampler, uv)`, not `texture2D(sampler, uv)`
@@ -74,7 +74,7 @@ void main() {
74
74
 
75
75
  ## Standalone HTML Multi-Channel Framebuffer Implementation
76
76
 
77
- **IMPORTANT: Multi-Channel Rendering Pipeline Core Pitfalls**: ShaderToy code requires manual Framebuffer rendering pipeline implementation. The following template demonstrates the correct approach:
77
+ **Multi-Channel Rendering Pipeline Core Pitfalls**: ShaderToy code requires manual Framebuffer rendering pipeline implementation. The following template demonstrates the correct approach:
78
78
 
79
79
  ```javascript
80
80
  // Correct multi-channel Framebuffer creation
@@ -91,7 +91,7 @@ for (let i = 0; i < NUM_BUFFERS; i++) {
91
91
  const texture = gl.createTexture();
92
92
  gl.bindTexture(gl.TEXTURE_2D, texture);
93
93
 
94
- // IMPORTANT: Critical: Must use UNSIGNED_BYTE format without EXT_color_buffer_float extension!
94
+ // Must use UNSIGNED_BYTE format without EXT_color_buffer_float extension!
95
95
  // RGBA16F/RGBA32F require the extension, otherwise GL_INVALID_OPERATION
96
96
  // Float textures need EXT_color_buffer_float; RGBA16F supports HDR data
97
97
  if (ext) {
@@ -100,14 +100,14 @@ for (let i = 0; i < NUM_BUFFERS; i++) {
100
100
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
101
101
  }
102
102
 
103
- // IMPORTANT: Critical: Texture parameters must be set, otherwise GL_INVALID_FRAMEBUFFER
104
- // IMPORTANT: Float textures use NEAREST, or require OES_texture_float_linear extension for LINEAR
105
- // IMPORTANT: Critical: Float textures must use CLAMP_TO_EDGE wrap mode; REPEAT is not supported for float textures
106
- // IMPORTANT: Critical: Must fall back to UNSIGNED_BYTE format without EXT_color_buffer_float extension
103
+ // Texture parameters must be set, otherwise GL_INVALID_FRAMEBUFFER
104
+ // Float textures use NEAREST, or require OES_texture_float_linear extension for LINEAR
105
+ // Float textures must use CLAMP_TO_EDGE wrap mode; REPEAT is not supported for float textures
106
+ // Must fall back to UNSIGNED_BYTE format without EXT_color_buffer_float extension
107
107
  const filterMode = (ext && floatLinear) ? gl.LINEAR : gl.NEAREST;
108
108
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filterMode);
109
109
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filterMode);
110
- // IMPORTANT: Must use CLAMP_TO_EDGE: float textures do not support REPEAT
110
+ // Must use CLAMP_TO_EDGE: float textures do not support REPEAT
111
111
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
112
112
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
113
113
 
@@ -115,7 +115,7 @@ for (let i = 0; i < NUM_BUFFERS; i++) {
115
115
  gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
116
116
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
117
117
 
118
- // IMPORTANT: Critical: Check Framebuffer completeness
118
+ // Check Framebuffer completeness
119
119
  const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
120
120
  if (status !== gl.FRAMEBUFFER_COMPLETE) {
121
121
  console.error("Framebuffer incomplete:", status);
@@ -138,7 +138,7 @@ function render() {
138
138
  // Execute shader rendering
139
139
 
140
140
  // 2. Swap Buffers (simulate self-feedback)
141
- // IMPORTANT: Critical: Must swap textures for next frame reading; FBO handles remain unchanged
141
+ // Must swap textures for next frame reading; FBO handles remain unchanged
142
142
  [textures[0], textures[1]] = [textures[1], textures[0]];
143
143
 
144
144
  // 3. Render to screen
@@ -150,7 +150,7 @@ function render() {
150
150
  }
151
151
  ```
152
152
 
153
- **IMPORTANT: Common Errors** (JavaScript/WebGL side):
153
+ **Common Errors** (JavaScript/WebGL side):
154
154
  1. **Missing texture parameters** - Must set `TEXTURE_MIN_FILTER`, `TEXTURE_MAG_FILTER`, `TEXTURE_WRAP_S/T`
155
155
  2. **Missing Framebuffer completeness check** - `gl.checkFramebufferStatus()` must return `FRAMEBUFFER_COMPLETE` before use
156
156
  3. **Float texture extension** - `gl.RGBA16F` requires `EXT_color_buffer_float` extension, otherwise fall back to `gl.UNSIGNED_BYTE`
@@ -620,13 +620,13 @@ for (int i = 0; i < NUM_SCALES; i++) {
620
620
 
621
621
  ### Variant 6: Particle System (Position-Velocity Storage)
622
622
 
623
- **IMPORTANT: Particle System Implementation Key**: Particle state is stored in texture pixels, one particle per pixel. Rendering must iterate over the particle texture for sampling.
623
+ **Particle System Implementation Key**: Particle state is stored in texture pixels, one particle per pixel. Rendering must iterate over the particle texture for sampling.
624
624
 
625
625
  **Buffer A (Particle Physics Simulation)**:
626
626
  ```glsl
627
627
  // Each texture pixel stores one particle: xy=position, zw=velocity
628
628
 
629
- // IMPORTANT: Critical: hash function must return vec2! Returning float causes type mismatch errors
629
+ // hash function must return vec2! Returning float causes type mismatch errors
630
630
  vec2 hash2(vec2 p) {
631
631
  return fract(sin(mat2(127.1, 311.7, 269.5, 183.3) * p) * 43758.5453);
632
632
  }
@@ -638,10 +638,10 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
638
638
  vec2 pos = prev.xy;
639
639
  vec2 vel = prev.zw;
640
640
 
641
- // IMPORTANT: Initialization guard: use integer comparison + pixel-coordinate-based random (avoids particle overlap when time is too small)
641
+ // Initialization guard: use integer comparison + pixel-coordinate-based random (avoids particle overlap when time is too small)
642
642
  if (iFrame < 3) {
643
643
  // Use fragCoord (pixel coordinates) to ensure each particle has a unique position, independent of time
644
- // IMPORTANT: Critical: hash2 returns vec2, assign directly to pos/vel
644
+ // hash2 returns vec2, assign directly to pos/vel
645
645
  pos = hash2(fragCoord * 0.01 + vec2(1.7, 9.3));
646
646
  vel = (hash2(fragCoord * 0.01 + vec2(5.3, 2.8)) - 0.5) * 0.02;
647
647
  fragColor = vec4(pos, vel);
@@ -676,7 +676,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
676
676
 
677
677
  // Iterate over particle texture for sampling (performance-sensitive, balance sample count)
678
678
  float glow = 0.0;
679
- for (float y = 0.0; y < 1.0; y += 0.02) { // IMPORTANT: Step size determines sampling density
679
+ for (float y = 0.0; y < 1.0; y += 0.02) { // Step size determines sampling density
680
680
  for (float x = 0.0; x < 1.0; x += 0.02) {
681
681
  vec4 particle = texture(iChannel0, vec2(x, y));
682
682
  vec2 pPos = particle.xy;
@@ -712,7 +712,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
712
712
  // Buffer B: Density accumulation (reads Buffer A) - uses FBO 2/3 ping-pong
713
713
  // Image: Final rendering (reads Buffer A + Buffer B)
714
714
 
715
- // IMPORTANT: Critical: Must use 2 FBOs for ping-pong! Single FBO + texture swap causes
715
+ // Must use 2 FBOs for ping-pong! Single FBO + texture swap causes
716
716
  // "Feedback loop formed between Framebuffer and active Texture" error
717
717
  const buffers = [null, null, null, null]; // [A_FBO0, A_FBO1, B_FBO0, B_FBO1]
718
718
  const textures = [null, null, null, null]; // [A_tex0, A_tex1, B_tex0, B_tex1]
@@ -741,7 +741,7 @@ function createBuffers() {
741
741
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
742
742
  }
743
743
 
744
- // IMPORTANT: Critical: Initialization pre-rendering - must execute before the first frame!
744
+ // Initialization pre-rendering - must execute before the first frame!
745
745
  // Empty textures cause particle initialization failure (reading 0,0,0,0 makes all particles overlap)
746
746
  let aReadIdx = 0; // Current read FBO index (0 or 1)
747
747
  let bReadIdx = 0; // Buffer B current read FBO index (0 or 1)
@@ -893,7 +893,7 @@ function render() {
893
893
  }
894
894
  ```
895
895
 
896
- **IMPORTANT: Key Points**:
896
+ **Key Points**:
897
897
  - **Must use 2 FBOs for ping-pong**: Each Buffer needs two independent FBOs (read FBO + write FBO); a single FBO + texture swap causes "Feedback loop" error
898
898
  - Use FBO index switching (not texture swapping): bind target FBO when writing, bind source texture when reading
899
899
  - Image pass binds the latest Buffer results (obtained via read index)
@@ -1,6 +1,6 @@
1
1
  ## WebGL2 Adaptation Requirements
2
2
 
3
- **IMPORTANT: GLSL Type Strictness Warning**:
3
+ **GLSL Type Strictness Warning**:
4
4
  - GLSL is a strongly typed language with **no `string` type**; using string types is forbidden
5
5
  - Common illegal types: `string`, `int` (can only use `int` literals, cannot declare variable types as `int`)
6
6
  - vec2/vec3/vec4 cannot be implicitly converted between each other; explicit construction is required