Files
mantle-ai-trader/skills/web-shader-extractor/references/shader-injection.md
2026-06-06 05:21:10 +00:00

127 lines
4.1 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# onBeforeCompile 注入 GLSL 的陷阱
## 场景
使用 `MeshPhysicalMaterial``transmission` 功能但需要增强效果时(如 drei 的 MeshTransmissionMaterial
通过 `material.onBeforeCompile` 注入自定义 GLSL 代码。
## 常见陷阱
### 1. 函数签名版本差异
Three.js 不同版本的内置函数签名不同:
```glsl
// r166 及之前
vec4 getIBLVolumeRefraction(n, v, roughness, diffuseColor, specularColor, specularF90,
pos, modelMatrix, viewMatrix, projectionMatrix, ior, thickness,
attenuationColor, attenuationDistance)
// r167+ 新增 dispersion 参数
vec4 getIBLVolumeRefraction(n, v, roughness, diffuseColor, specularColor, specularF90,
pos, modelMatrix, viewMatrix, projectionMatrix, dispersion, ior, thickness,
attenuationColor, attenuationDistance)
```
**必须检查目标版本的实际签名**
```bash
curl -s "https://cdn.jsdelivr.net/npm/three@0.167.0/src/renderers/shaders/ShaderChunk/transmission_pars_fragment.glsl.js" \
| tr '\n' ' ' | grep -oE 'vec4 getIBLVolumeRefraction\([^)]+\)'
```
### 2. GLSL 不允许嵌套函数定义
```glsl
// 错误GLSL 不支持函数内定义函数
void main() {
float myRand(vec2 co) { return fract(sin(...)); } // 编译失败
}
// 正确:函数必须在全局作用域
float myRand(vec2 co) { return fract(sin(...)); }
void main() {
float r = myRand(uv);
}
```
### 3. 条件编译宏保护
某些变量只在特定宏下可用:
- `vWorldPosition` → 需要 `USE_TRANSMISSION` 启用
- `vTransmissionMapUv` → 需要 `USE_TRANSMISSIONMAP` 启用
- `roughnessFactor` → 在 `lights_physical_fragment` 之后可用
```glsl
// 在替换 #include <transmission_fragment> 时
// 原始代码自带 #ifdef USE_TRANSMISSION替换代码也必须包含
#ifdef USE_TRANSMISSION
// ... 你的代码
#endif
```
### 4. 变量名冲突
注入的全局函数/变量可能与 Three.js 内部冲突:
- 避免使用 `hash`, `random`, `noise` 等通用名
- 自定义函数加前缀:`snoise` → OK`random` → 可能冲突
- uniform 名称加前缀 `u``uDistortion`, `uNoiseTime`
## 推荐模式
### 安全注入:修改 normal 而不替换整个 chunk
```javascript
material.onBeforeCompile = (shader) => {
shader.uniforms.uDistortion = { value: 0 };
shader.uniforms.uNoiseTime = { value: 0 };
// 在 fragment shader 最前面加 uniform 声明 + 工具函数
shader.fragmentShader = `
uniform float uDistortion;
uniform float uNoiseTime;
${noiseGLSL}
` + shader.fragmentShader;
// 在 transmission_fragment 之前插入法线扰动
shader.fragmentShader = shader.fragmentShader.replace(
'#include <transmission_fragment>',
`
#ifdef USE_TRANSMISSION
{
// 扰动 normal 影响折射方向
if (uDistortion > 0.0) {
normal = normalize(normal + uDistortion * vec3(
snoiseFractal(vWorldPosition * 0.08 + vec3(uNoiseTime)),
snoiseFractal(vWorldPosition.zxy * 0.08 - vec3(uNoiseTime)),
snoiseFractal(vWorldPosition.yxz * 0.08)
));
}
}
#endif
#include <transmission_fragment>
`
);
};
```
### 完整替换:需要随机多采样 + 色差时
当需要 MeshTransmissionMaterial 的颗粒感(随机采样噪声)和色差效果时,
必须完整替换 `#include <transmission_fragment>`。关键点:
1. 保留 `#ifdef USE_TRANSMISSION` / `#endif` 包裹
2. 保留 transmissionMap 和 thicknessMap 的 `#ifdef`
3. 使用正确版本的 `getIBLVolumeRefraction` 签名
4. 自己处理色差时,传 `dispersion = 0.0`,用不同 IOR 采样 R/G/B
5. 低采样数6+ 每像素随机偏移 → 产生可见的胶片颗粒感
## 视觉效果来源速查
| 效果 | 来源 | 实现方式 |
|------|------|----------|
| 玻璃折射 | MeshPhysicalMaterial `transmission` | Three.js 内置 |
| 色差 (chromatic aberration) | 不同 IOR 采样 R/G/B | 替换 transmission_fragment |
| 胶片颗粒感 | 低采样数 + 每像素随机方向 | 替换 transmission_fragment |
| 有机扭曲 | simplex noise 扰动法线/折射方向 | onBeforeCompile 注入 |
| 颜色偏移 | `dispersion` 属性 (r167+) | MeshPhysicalMaterial 内置 |