wgsl-renderer 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +236 -228
- package/README.zh-CN.md +417 -0
- package/dist/cjs/index.js +111 -114
- package/dist/esm/index.d.ts +154 -0
- package/dist/esm/index.js +111 -114
- package/package.json +6 -4
- package/dist/types/index.d.ts +0 -85
- package/dist/types/index.js +0 -434
package/README.md
CHANGED
|
@@ -1,25 +1,61 @@
|
|
|
1
1
|
# WGSL Multi-Pass Renderer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English | [中文](./README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A multi-pass renderer based on WebGPU and WGSL.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- 🖼️ **多Pass渲染** - 支持纹理渲染、后处理效果等多通道
|
|
9
|
-
- ⚡ **高性能渲染循环** - 支持单帧渲染和循环渲染模式
|
|
10
|
-
- 🛠️ **TypeScript支持** - 完整的类型定义和清晰的API分离
|
|
11
|
-
- 🎮 **Uniform系统** - 内置uniform buffer管理,支持动态参数
|
|
12
|
-
- 🔄 **自动Resize** - 内置ResizeObserver自动处理canvas大小变化
|
|
7
|
+
## ✨ Features
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
- 🖼️ **Multi-Pass Rendering** - Support for texture rendering, post-processing effects, and other multi-pass rendering
|
|
10
|
+
- ⚡ **High-Performance Rendering Loop** - Support for single-frame rendering and loop rendering modes
|
|
11
|
+
- 🛠️ **TypeScript Support** - Complete type definitions and clear API separation
|
|
12
|
+
- 🎮 **Uniform System** - Built-in uniform buffer management with dynamic parameter support
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
## 🚀 Quick Start
|
|
15
|
+
|
|
16
|
+
### Installation
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
npm i wgls-renderer
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
###
|
|
22
|
+
### Add Pass
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { createWGSLRenderer } from 'wgls-renderer'
|
|
26
|
+
|
|
27
|
+
const canvas = document.querySelector('canvas');
|
|
28
|
+
const renderer = await createWGSLRenderer(canvas)
|
|
29
|
+
|
|
30
|
+
renderer.addPass({
|
|
31
|
+
name: 'my-pass',
|
|
32
|
+
shaderCode: `
|
|
33
|
+
struct VSOut {
|
|
34
|
+
@builtin(position) pos: vec4<f32>,
|
|
35
|
+
@location(0) uv: vec2<f32>,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
@vertex
|
|
39
|
+
fn vs_main(@location(0) p: vec3<f32>) -> VSOut {
|
|
40
|
+
var o: VSOut;
|
|
41
|
+
o.pos = vec4<f32>(p, 1.0);
|
|
42
|
+
o.uv = p.xy * 0.5 + vec2<f32>(0.5, 0.5);
|
|
43
|
+
o.uv.y = 1.0 - o.uv.y;
|
|
44
|
+
return o;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@fragment
|
|
48
|
+
fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
|
|
49
|
+
return vec4(1.0, 1.0, 0.0, 1.0);
|
|
50
|
+
}`,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
renderer.renderFrame()
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### Basic Multi-Pass Usage
|
|
23
59
|
|
|
24
60
|
```typescript
|
|
25
61
|
import { createWGSLRenderer } from 'wgls-renderer'
|
|
@@ -27,50 +63,54 @@ import { createWGSLRenderer } from 'wgls-renderer'
|
|
|
27
63
|
const canvas = document.getElementById('canvas')
|
|
28
64
|
const renderer = await createWGSLRenderer(canvas)
|
|
29
65
|
|
|
30
|
-
//
|
|
66
|
+
// Create sampler
|
|
31
67
|
const sampler = renderer.createSampler()
|
|
32
68
|
|
|
33
|
-
//
|
|
34
|
-
const { texture } = await renderer.loadImageTexture('image.jpg')
|
|
69
|
+
// Load image texture
|
|
70
|
+
const { texture, width, height } = await renderer.loadImageTexture('image.jpg')
|
|
35
71
|
|
|
36
|
-
//
|
|
72
|
+
// Add Pass 1: Render texture
|
|
37
73
|
renderer.addPass({
|
|
38
74
|
name: 'texture_pass',
|
|
39
75
|
shaderCode: textureShader,
|
|
40
|
-
blendMode: 'alpha',
|
|
41
76
|
resources: [texture, sampler], // binding 0, 1
|
|
42
77
|
})
|
|
43
78
|
|
|
44
|
-
//
|
|
45
|
-
const uniforms = renderer.createUniforms(
|
|
79
|
+
// Add Pass 2: Post-processing effect
|
|
80
|
+
const uniforms = renderer.createUniforms(8) // Create uniform variable binding
|
|
81
|
+
|
|
82
|
+
// Get Pass 1 output texture and bind to Pass 2
|
|
83
|
+
const texturePassOutput = renderer.getPassTexture('texture_pass')
|
|
46
84
|
renderer.addPass({
|
|
47
85
|
name: 'post_process',
|
|
48
86
|
shaderCode: postProcessShader,
|
|
49
|
-
|
|
50
|
-
|
|
87
|
+
resources: [
|
|
88
|
+
texturePassOutput, // @group(0) @binding(0)
|
|
89
|
+
sampler, // @group(0) @binding(1)
|
|
90
|
+
uniforms.getBuffer(), // @group(0) @binding(2)
|
|
91
|
+
],
|
|
51
92
|
})
|
|
52
93
|
|
|
53
|
-
//
|
|
54
|
-
renderer.loopRender(() => {
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
uniforms.values[0] = canvas.width
|
|
58
|
-
uniforms.values[1] = canvas.height
|
|
59
|
-
uniforms.values[2] =
|
|
60
|
-
uniforms.values[3] = 0
|
|
61
|
-
uniforms.values[4] =
|
|
62
|
-
uniforms.values[5] =
|
|
63
|
-
uniforms.apply()
|
|
94
|
+
// Start loop rendering, can update uniforms in callback
|
|
95
|
+
renderer.loopRender((t) => {
|
|
96
|
+
|
|
97
|
+
// Update uniforms (Note WebGPU memory alignment rules)
|
|
98
|
+
uniforms.values[0] = canvas.width // resolution.x
|
|
99
|
+
uniforms.values[1] = canvas.height // resolution.y
|
|
100
|
+
uniforms.values[2] = t / 1000 // time
|
|
101
|
+
uniforms.values[3] = 0 // padding (leave empty)
|
|
102
|
+
uniforms.values[4] = width // textureResolution.x
|
|
103
|
+
uniforms.values[5] = height // textureResolution.y
|
|
104
|
+
uniforms.apply() // Apply to GPU
|
|
64
105
|
})
|
|
65
106
|
|
|
66
|
-
//
|
|
107
|
+
// Or manually execute single frame render
|
|
67
108
|
renderer.renderFrame()
|
|
68
109
|
```
|
|
69
110
|
|
|
111
|
+
## 🎨 Shader Examples
|
|
70
112
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
### Pass 1: 纹理渲染
|
|
113
|
+
### Pass 1: Texture Rendering
|
|
74
114
|
|
|
75
115
|
```wgsl
|
|
76
116
|
// textureShader
|
|
@@ -97,20 +137,18 @@ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
|
|
|
97
137
|
}
|
|
98
138
|
```
|
|
99
139
|
|
|
100
|
-
### Pass 2:
|
|
140
|
+
### Pass 2: Brightness & Contrast Adjustment
|
|
101
141
|
|
|
102
142
|
```wgsl
|
|
103
143
|
// postProcessShader
|
|
104
144
|
struct Uniforms {
|
|
105
|
-
|
|
106
|
-
|
|
145
|
+
brightness: f32, // offset 0
|
|
146
|
+
contrast: f32, // offset 4
|
|
147
|
+
saturation: f32, // offset 8
|
|
107
148
|
// 4 bytes padding for vec3 alignment
|
|
108
|
-
texResolution: vec2<f32>, // offset 16-23
|
|
109
|
-
speed: f32, // offset 24
|
|
110
|
-
// 8 bytes padding for next vec3
|
|
111
149
|
}
|
|
112
150
|
|
|
113
|
-
@group(0) @binding(0) var prevTexture: texture_2d<f32>; //
|
|
151
|
+
@group(0) @binding(0) var prevTexture: texture_2d<f32>; // Pass 1 output texture
|
|
114
152
|
@group(0) @binding(1) var mySampler: sampler;
|
|
115
153
|
@group(0) @binding(2) var<uniform> uniforms: Uniforms;
|
|
116
154
|
|
|
@@ -118,277 +156,247 @@ struct Uniforms {
|
|
|
118
156
|
fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
|
|
119
157
|
var color = textureSample(prevTexture, mySampler, uv);
|
|
120
158
|
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
color = vec4<f32>(color.r * scanline, color.g * scanline, color.b * scanline, color.a);
|
|
159
|
+
// Apply brightness
|
|
160
|
+
color.rgb += uniforms.brightness;
|
|
124
161
|
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
let waveX = sin(uv.x * 10.0 + uniforms.time * 3.0) * cos(uv.y * 8.0 + uniforms.time * 2.0) * waveAmplitude;
|
|
162
|
+
// Apply contrast
|
|
163
|
+
color.rgb = (color.rgb - 0.5) * uniforms.contrast + 0.5;
|
|
128
164
|
|
|
129
|
-
|
|
130
|
-
let
|
|
131
|
-
|
|
165
|
+
// Apply saturation
|
|
166
|
+
let gray = dot(color.rgb, vec3<f32>(0.299, 0.587, 0.114));
|
|
167
|
+
color.rgb = mix(vec3<f32>(gray), color.rgb, uniforms.saturation);
|
|
132
168
|
|
|
133
|
-
return vec4<f32>(
|
|
169
|
+
return clamp(color, vec4<f32>(0.0), vec4<f32>(1.0));
|
|
134
170
|
}
|
|
135
171
|
```
|
|
136
172
|
|
|
173
|
+
|
|
137
174
|
## 📋 API
|
|
138
175
|
|
|
139
176
|
### createWGSLRenderer(canvas, options?)
|
|
140
177
|
|
|
141
|
-
|
|
178
|
+
Create WGSL renderer instance.
|
|
142
179
|
|
|
143
180
|
```typescript
|
|
181
|
+
import { createWGSLRenderer } from 'wgsl-renderer'
|
|
144
182
|
const renderer = await createWGSLRenderer(canvas)
|
|
145
183
|
```
|
|
146
184
|
|
|
147
|
-
|
|
185
|
+
options:
|
|
148
186
|
|
|
149
|
-
|
|
187
|
+
```ts
|
|
188
|
+
interface WGSLRendererOptions {
|
|
189
|
+
config?: GPUCanvasConfiguration;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
150
192
|
|
|
151
|
-
|
|
152
|
-
|
|
193
|
+
### renderer.addPass(passOptions)
|
|
194
|
+
|
|
195
|
+
Add a render pass.
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
interface RenderPassOptions {
|
|
199
|
+
name: string;
|
|
200
|
+
shaderCode: string;
|
|
201
|
+
entryPoints?: {
|
|
202
|
+
vertex?: string; // Default is 'vs_main' function
|
|
203
|
+
fragment?: string; // Default is 'fs_main' function
|
|
204
|
+
};
|
|
205
|
+
clearColor?: { r: number; g: number; b: number; a: number };
|
|
206
|
+
blendMode?: 'additive' | 'alpha' | 'multiply' | 'none';
|
|
207
|
+
resources?: GPUBindingResource[];
|
|
208
|
+
view?: GPUTextureView; // Optional custom view for this pass
|
|
209
|
+
format?: GPUTextureFormat; // Optional format for the view (required when using custom view with different format)
|
|
210
|
+
}
|
|
153
211
|
```
|
|
154
212
|
|
|
155
|
-
###
|
|
213
|
+
### renderer.getPassTexture(passName)
|
|
156
214
|
|
|
157
|
-
|
|
215
|
+
Get the output texture of the specified pass. The return value is not a real texture but a placeholder that automatically binds the output texture to the shader during actual rendering.
|
|
158
216
|
|
|
159
217
|
```typescript
|
|
160
|
-
|
|
218
|
+
// Get output texture of my_pass
|
|
219
|
+
const passOutputTexture = renderer.getPassTexture('my_pass')
|
|
220
|
+
const sampler = renderer.createSampler()
|
|
221
|
+
renderer.addPass({
|
|
222
|
+
name: 'my_pass2',
|
|
223
|
+
shaderCode: wgslShaderCode,
|
|
224
|
+
resources: [
|
|
225
|
+
passOutputTexture,
|
|
226
|
+
sampler,
|
|
227
|
+
],
|
|
228
|
+
})
|
|
161
229
|
```
|
|
162
230
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
获取WebGPU设备对象。
|
|
231
|
+
**Corresponding WGSL binding:**
|
|
166
232
|
|
|
167
|
-
```
|
|
168
|
-
|
|
233
|
+
```wgsl
|
|
234
|
+
@group(0) @binding(0) var myTexture: texture_2d<f32>; // resources[0]
|
|
235
|
+
@group(0) @binding(1) var mySampler: sampler; // resources[1]
|
|
169
236
|
```
|
|
170
237
|
|
|
171
|
-
### 渲染控制
|
|
172
238
|
|
|
173
|
-
#### renderFrame()
|
|
174
|
-
单帧渲染,不循环。
|
|
175
239
|
|
|
176
|
-
|
|
177
|
-
renderer.renderFrame()
|
|
178
|
-
```
|
|
240
|
+
### renderer.createUniforms(length)
|
|
179
241
|
|
|
180
|
-
|
|
181
|
-
循环渲染,支持每帧回调,可用于时时更新uniforms。
|
|
242
|
+
Create uniform variables using Float32Array, length unit is the number of floats.
|
|
182
243
|
|
|
183
244
|
```typescript
|
|
184
|
-
renderer.
|
|
245
|
+
const myUniforms = renderer.createUniforms(8) // 8 floats
|
|
185
246
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
247
|
+
// Bind to shader
|
|
248
|
+
renderer.addPass({
|
|
249
|
+
name: 'my_pass',
|
|
250
|
+
shaderCode: wgslShaderCode,
|
|
251
|
+
resources: [
|
|
252
|
+
myUniforms.getBuffer(), // group(0) binding(0) var<uniform>
|
|
253
|
+
],
|
|
189
254
|
})
|
|
255
|
+
|
|
256
|
+
myUniforms.values[0] = 1.0 // Set value
|
|
257
|
+
myUniforms.apply() // Apply to GPU
|
|
190
258
|
```
|
|
191
259
|
|
|
192
|
-
|
|
193
|
-
|
|
260
|
+
### renderer.getContext()
|
|
261
|
+
|
|
262
|
+
Get WebGPU canvas context.
|
|
194
263
|
|
|
195
264
|
```typescript
|
|
196
|
-
renderer.
|
|
265
|
+
const context = renderer.getContext()
|
|
197
266
|
```
|
|
198
267
|
|
|
199
|
-
###
|
|
268
|
+
### renderer.getDevice()
|
|
200
269
|
|
|
201
|
-
|
|
270
|
+
Get WebGPU device object.
|
|
202
271
|
|
|
203
272
|
```typescript
|
|
204
|
-
renderer.
|
|
205
|
-
name: 'my_pass',
|
|
206
|
-
shaderCode: wgslShaderCode,
|
|
207
|
-
blendMode: 'alpha',
|
|
208
|
-
resources: [textureView, sampler], // 资源数组
|
|
209
|
-
})
|
|
273
|
+
const device = renderer.getDevice()
|
|
210
274
|
```
|
|
211
275
|
|
|
212
|
-
|
|
276
|
+
### Render Control
|
|
213
277
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
- **Binding 1**: `resources[1]`
|
|
217
|
-
- 以此类推...
|
|
278
|
+
#### renderer.renderFrame()
|
|
279
|
+
Single frame rendering.
|
|
218
280
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
- **Binding 1**: `resources[0]`
|
|
222
|
-
- **Binding 2**: `resources[1]`
|
|
223
|
-
- 以此类推...
|
|
281
|
+
#### renderer.loopRender(callback?)
|
|
282
|
+
Built-in loop rendering with per-frame callback for real-time uniform updates.
|
|
224
283
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
```wgsl
|
|
228
|
-
// Pass 1:
|
|
229
|
-
@group(0) @binding(0) var myTexture: texture_2d<f32>; // resources[0]
|
|
230
|
-
@group(0) @binding(1) var mySampler: sampler; // resources[1]
|
|
284
|
+
```typescript
|
|
285
|
+
renderer.loopRender(time => {
|
|
231
286
|
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
287
|
+
// Update uniforms every frame
|
|
288
|
+
myUniforms.values[2] = time * 0.001
|
|
289
|
+
myUniforms.apply()
|
|
290
|
+
})
|
|
236
291
|
```
|
|
237
292
|
|
|
238
|
-
|
|
293
|
+
#### renderer.stopLoop()
|
|
294
|
+
Stop loop rendering.
|
|
239
295
|
|
|
240
|
-
#### createUniforms(length)
|
|
241
|
-
创建uniform buffer管理对象。
|
|
242
296
|
|
|
243
|
-
```typescript
|
|
244
|
-
const uniforms = renderer.createUniforms(4) // 4个float
|
|
245
|
-
uniforms.values[0] = 1.0 // 设置值
|
|
246
|
-
uniforms.apply() // 应用到GPU
|
|
247
|
-
const buffer = uniforms.getBuffer() // 获取GPUBuffer
|
|
248
|
-
```
|
|
249
297
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
```javascript
|
|
253
|
-
const uniforms = renderer.createUniforms(16) // 64字节
|
|
254
|
-
uniforms.values[0] = canvas.width // resolution.x
|
|
255
|
-
uniforms.values[1] = canvas.height // resolution.y
|
|
256
|
-
uniforms.values[2] = performance.now() // time
|
|
257
|
-
uniforms.values[3] = 0 // padding (vec3对齐)
|
|
258
|
-
uniforms.values[4] = 1024 // texResolution.x
|
|
259
|
-
uniforms.values[5] = 1024 // texResolution.y
|
|
260
|
-
uniforms.values[6] = 1.0 // speed
|
|
261
|
-
uniforms.values[7] = 0 // padding
|
|
262
|
-
uniforms.values[8] = 0 // padding
|
|
263
|
-
uniforms.apply()
|
|
264
|
-
```
|
|
298
|
+
### renderer.createSampler(options?)
|
|
265
299
|
|
|
266
|
-
|
|
300
|
+
Create sampler with default parameters:
|
|
267
301
|
|
|
268
|
-
|
|
302
|
+
```ts
|
|
303
|
+
const options = {
|
|
304
|
+
magFilter: 'linear',
|
|
305
|
+
minFilter: 'linear',
|
|
306
|
+
addressModeU: 'clamp-to-edge',
|
|
307
|
+
addressModeV: 'clamp-to-edge',
|
|
308
|
+
}
|
|
269
309
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const { texture, width, height } = await renderer.loadImageTexture('image.png')
|
|
310
|
+
const sampler = renderer.createSampler(options)
|
|
311
|
+
```
|
|
273
312
|
|
|
274
|
-
|
|
275
|
-
const sampler = renderer.createSampler()
|
|
313
|
+
## 🎯 Pass Flow
|
|
276
314
|
|
|
277
|
-
|
|
278
|
-
const textureView = texture.createView()
|
|
279
|
-
renderer.addPass({
|
|
280
|
-
name: 'texture-pass',
|
|
281
|
-
shaderCode: shaderCode,
|
|
282
|
-
resources: [
|
|
283
|
-
textureView,
|
|
284
|
-
sampler,
|
|
285
|
-
],
|
|
286
|
-
})
|
|
287
|
-
```
|
|
315
|
+
The renderer provides the following management features:
|
|
288
316
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
317
|
+
1. **User-defined all Passes**
|
|
318
|
+
- Users have complete control over all resource binding
|
|
319
|
+
- Can get output texture of any pass through `getPassTexture(passName)`
|
|
320
|
+
- Can get pass object through `getPassByName(passName)`
|
|
293
321
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
```
|
|
322
|
+
2. **Texture Management**
|
|
323
|
+
- Each pass automatically creates output texture (format: `{passName}_output`)
|
|
324
|
+
- Users can manually bind these textures to other passes
|
|
325
|
+
- The last pass automatically renders to canvas
|
|
299
326
|
|
|
300
|
-
|
|
327
|
+
3. **Complete Flexibility**
|
|
328
|
+
- Users decide binding order and method
|
|
329
|
+
- Support arbitrarily complex pass connections
|
|
330
|
+
- Can create circular dependencies (if needed)
|
|
301
331
|
|
|
332
|
+
**Example Usage:**
|
|
302
333
|
```typescript
|
|
303
|
-
//
|
|
304
|
-
const uniforms = renderer.createUniforms(8) // 8个float (32字节)
|
|
305
|
-
uniforms.values[0] = 1.0 // 设置第一个float值
|
|
306
|
-
uniforms.values[1] = 0.5 // 设置第二个float值
|
|
307
|
-
uniforms.values[2] = 0.25 // 设置第三个float值
|
|
308
|
-
// 向量值需要内存对齐,这里的offset必须是4的倍数,因此跳过uniforms.values[3]
|
|
309
|
-
uniforms.values[4] = 1.0 // texResolution.x
|
|
310
|
-
uniforms.values[5] = 1024.0 // texResolution.y
|
|
311
|
-
|
|
312
|
-
uniforms.apply() // 应用到GPU
|
|
313
|
-
const uniformBuffer = uniforms.getBuffer() // 获取GPUBuffer
|
|
314
|
-
|
|
315
|
-
// 绑定到Pass
|
|
334
|
+
// Method 1: Simple chain reference
|
|
316
335
|
renderer.addPass({
|
|
317
|
-
name: '
|
|
318
|
-
|
|
319
|
-
|
|
336
|
+
name: 'background',
|
|
337
|
+
resources: [bgTexture, sampler1],
|
|
338
|
+
})
|
|
320
339
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
],
|
|
340
|
+
renderer.addPass({
|
|
341
|
+
name: 'main_effect',
|
|
342
|
+
resources: [renderer.getPassTexture('background'), sampler2], // Reference background output
|
|
324
343
|
})
|
|
325
|
-
```
|
|
326
344
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
value3: f32, // 对应 uniforms.values[2]
|
|
332
|
-
textureResolution: vec2<f32>, // x, y分别对应 uniforms.values[4], uniforms.values[5]
|
|
333
|
-
// ...
|
|
334
|
-
}
|
|
345
|
+
renderer.addPass({
|
|
346
|
+
name: 'post_process',
|
|
347
|
+
resources: [renderer.getPassTexture('main_effect'), sampler3], // Reference main_effect output
|
|
348
|
+
})
|
|
335
349
|
|
|
336
|
-
//
|
|
337
|
-
|
|
350
|
+
// Method 2: Complex multi-pass blending
|
|
351
|
+
renderer.addPass({ name: 'layer1', resources: [textureA, sampler] })
|
|
352
|
+
renderer.addPass({ name: 'layer2', resources: [textureB, sampler] })
|
|
353
|
+
renderer.addPass({ name: 'layer3', resources: [textureC, sampler] })
|
|
338
354
|
|
|
339
|
-
//
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
355
|
+
// Create blend pass, referencing multiple different passes simultaneously
|
|
356
|
+
const layer1Output = renderer.getPassTexture('layer1')
|
|
357
|
+
const layer2Output = renderer.getPassTexture('layer2')
|
|
358
|
+
const layer3Output = renderer.getPassTexture('layer3')
|
|
343
359
|
|
|
344
|
-
|
|
360
|
+
renderer.addPass({
|
|
361
|
+
name: 'composite',
|
|
362
|
+
resources: [layer1Output, layer2Output, layer3Output, finalSampler],
|
|
363
|
+
})
|
|
345
364
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
365
|
+
// Method 3: Dynamic update binding
|
|
366
|
+
const mainPass = renderer.getPassByName('main_effect')
|
|
367
|
+
if (mainPass) {
|
|
349
368
|
|
|
350
|
-
//
|
|
351
|
-
renderer.
|
|
369
|
+
// Dynamically change reference relationship at runtime
|
|
370
|
+
mainPass.updateBindGroup([renderer.getPassTexture('layer1'), newSampler])
|
|
371
|
+
}
|
|
352
372
|
```
|
|
353
373
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
2. **User Pass 2**
|
|
364
|
-
- Binding 0: Pass 1输出纹理(自动)
|
|
365
|
-
- Binding 1+: 用户资源
|
|
366
|
-
- 输出到 `pass_1_output`
|
|
367
|
-
|
|
368
|
-
3. **User Pass 3+**
|
|
369
|
-
- Binding 0: 上一个pass输出纹理(自动)
|
|
370
|
-
- Binding 1+: 用户资源
|
|
371
|
-
- 输出到 `pass_N-1_output`
|
|
374
|
+
**Error Handling Example:**
|
|
375
|
+
```typescript
|
|
376
|
+
// If referencing non-existent pass, will throw detailed error during rendering
|
|
377
|
+
const invalidTexture = renderer.getPassTexture('nonexistent_pass') // This pass doesn't exist
|
|
378
|
+
renderer.addPass({
|
|
379
|
+
name: 'test',
|
|
380
|
+
resources: [invalidTexture, sampler], // Will throw error during rendering
|
|
381
|
+
})
|
|
372
382
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
- Binding 1+: 用户资源
|
|
376
|
-
- 渲染到canvas
|
|
383
|
+
// Error message: Cannot find pass named 'nonexistent_pass'. Available passes: [background, main_effect, ...]
|
|
384
|
+
```
|
|
377
385
|
|
|
378
|
-
## 🛠️
|
|
386
|
+
## 🛠️ Development
|
|
379
387
|
|
|
380
388
|
```bash
|
|
381
|
-
#
|
|
389
|
+
# Development mode
|
|
382
390
|
pnpm dev
|
|
383
391
|
|
|
384
|
-
#
|
|
392
|
+
# Build
|
|
385
393
|
pnpm build
|
|
386
394
|
```
|
|
387
395
|
|
|
388
|
-
## 📝
|
|
396
|
+
## 📝 License
|
|
389
397
|
|
|
390
398
|
MIT License
|
|
391
399
|
|
|
392
|
-
## 🤝
|
|
400
|
+
## 🤝 Contributing
|
|
393
401
|
|
|
394
|
-
|
|
402
|
+
Issues and Pull Requests are welcome!
|