wgsl-renderer 0.0.1 → 0.0.3
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/LICENSE.md +8 -8
- package/README.md +294 -200
- package/README.zh-CN.md +417 -0
- package/dist/cjs/index.js +147 -157
- package/dist/esm/index.js +147 -157
- package/dist/types/index.d.ts +64 -55
- package/dist/types/index.js +147 -157
- package/package.json +4 -2
package/LICENSE.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 taiyuuki
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sub license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
-
|
|
7
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
-
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 taiyuuki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sub license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
9
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,163 +1,119 @@
|
|
|
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管理,支持动态参数
|
|
7
|
+
## ✨ Features
|
|
12
8
|
|
|
13
|
-
|
|
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
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
## 🚀 Quick Start
|
|
15
|
+
|
|
16
|
+
### Installation
|
|
16
17
|
|
|
17
18
|
```bash
|
|
18
19
|
npm i wgls-renderer
|
|
19
20
|
```
|
|
20
21
|
|
|
21
|
-
###
|
|
22
|
+
### Add Pass
|
|
22
23
|
|
|
23
24
|
```typescript
|
|
24
|
-
import { createWGSLRenderer } from 'wgls-renderer'
|
|
25
|
-
|
|
26
|
-
const canvas = document.getElementById('canvas');
|
|
27
|
-
const renderer = await createWGSLRenderer(canvas, {
|
|
28
|
-
backgroundColor: 0x66CCFF // 支持多种格式:0xRRGGBB, "#RRGGBB", {r, g, b}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// 创建采样器
|
|
32
|
-
const sampler = renderer.createSampler();
|
|
25
|
+
import { createWGSLRenderer } from 'wgls-renderer'
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
const
|
|
27
|
+
const canvas = document.querySelector('canvas');
|
|
28
|
+
const renderer = await createWGSLRenderer(canvas)
|
|
36
29
|
|
|
37
|
-
// 添加Pass 1: 渲染纹理
|
|
38
30
|
renderer.addPass({
|
|
39
|
-
name: '
|
|
40
|
-
shaderCode:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// 添加Pass 2: 后处理效果
|
|
46
|
-
const uniforms = renderer.createUniforms(4); // time, resolution.x, resolution.y, padding
|
|
47
|
-
renderer.addPass({
|
|
48
|
-
name: 'post_process',
|
|
49
|
-
shaderCode: postProcessShader,
|
|
50
|
-
blendMode: 'alpha',
|
|
51
|
-
resources: [sampler, uniforms.getBuffer()] // binding 1, 2
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// 启动循环渲染,支持uniforms更新
|
|
55
|
-
renderer.loopRender(() => {
|
|
56
|
-
// 更新uniforms
|
|
57
|
-
uniforms.values[0] = performance.now() / 1000.0; // 时间
|
|
58
|
-
uniforms.values[1] = canvas.width; // 分辨率
|
|
59
|
-
uniforms.values[2] = canvas.height;
|
|
60
|
-
uniforms.apply();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// 或者单帧渲染
|
|
64
|
-
renderer.renderFrame();
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## 📋 API
|
|
31
|
+
name: 'my-pass',
|
|
32
|
+
shaderCode: `
|
|
33
|
+
struct VSOut {
|
|
34
|
+
@builtin(position) pos: vec4<f32>,
|
|
35
|
+
@location(0) uv: vec2<f32>,
|
|
36
|
+
};
|
|
68
37
|
|
|
69
|
-
|
|
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
|
+
}
|
|
70
46
|
|
|
71
|
-
|
|
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
|
+
})
|
|
72
52
|
|
|
73
|
-
|
|
74
|
-
const renderer = await createWGSLRenderer(canvas, {
|
|
75
|
-
backgroundColor: 0x66CCFF // 支持多种格式
|
|
76
|
-
});
|
|
53
|
+
renderer.renderFrame()
|
|
77
54
|
```
|
|
78
55
|
|
|
79
56
|
|
|
80
|
-
- `number`: 十六进制颜色 `0xRRGGBB`
|
|
81
|
-
- `string`: 十六进制字符串 `"#RRGGBB"`
|
|
82
|
-
- `object`: RGB对象 `{r: 0-1, g: 0-1, b: 0-1}`
|
|
83
|
-
|
|
84
|
-
### 渲染控制
|
|
85
|
-
|
|
86
|
-
#### renderFrame()
|
|
87
|
-
单帧渲染,不循环。
|
|
88
|
-
|
|
89
|
-
```typescript
|
|
90
|
-
renderer.renderFrame();
|
|
91
|
-
```
|
|
92
57
|
|
|
93
|
-
|
|
94
|
-
循环渲染,支持每帧回调,可用于时时更新uniforms。
|
|
58
|
+
### Basic Multi-Pass Usage
|
|
95
59
|
|
|
96
60
|
```typescript
|
|
97
|
-
|
|
98
|
-
// 每帧更新uniforms
|
|
99
|
-
myUniforms.values[0] = performance.now() / 1000.0;
|
|
100
|
-
myUniforms.apply();
|
|
101
|
-
});
|
|
102
|
-
```
|
|
61
|
+
import { createWGSLRenderer } from 'wgls-renderer'
|
|
103
62
|
|
|
104
|
-
|
|
105
|
-
|
|
63
|
+
const canvas = document.getElementById('canvas')
|
|
64
|
+
const renderer = await createWGSLRenderer(canvas)
|
|
106
65
|
|
|
107
|
-
|
|
108
|
-
renderer.
|
|
109
|
-
```
|
|
66
|
+
// Create sampler
|
|
67
|
+
const sampler = renderer.createSampler()
|
|
110
68
|
|
|
111
|
-
|
|
69
|
+
// Load image texture
|
|
70
|
+
const { texture, width, height } = await renderer.loadImageTexture('image.jpg')
|
|
112
71
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
```typescript
|
|
72
|
+
// Add Pass 1: Render texture
|
|
116
73
|
renderer.addPass({
|
|
117
|
-
name: '
|
|
118
|
-
shaderCode:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
});
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
**资源数组绑定规则:**
|
|
125
|
-
- **Binding 0**: 自动绑定到上一个pass的输出(无需在数组中指定)
|
|
126
|
-
- **Binding 1**: `resources[0]`
|
|
127
|
-
- **Binding 2**: `resources[1]`
|
|
128
|
-
- 以此类推...
|
|
129
|
-
|
|
130
|
-
**对应的WGSL绑定:**
|
|
131
|
-
```wgsl
|
|
132
|
-
@group(0) @binding(0) var prevTexture: texture_2d<f32>; // 自动
|
|
133
|
-
@group(0) @binding(1) var myTexture: texture_2d<f32>; // resources[0]
|
|
134
|
-
@group(0) @binding(2) var mySampler: sampler; // resources[1]
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Uniform
|
|
138
|
-
|
|
139
|
-
#### createUniforms(length)
|
|
140
|
-
创建uniform buffer管理对象。
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
const uniforms = renderer.createUniforms(4); // 4个float
|
|
144
|
-
uniforms.values[0] = 1.0; // 设置值
|
|
145
|
-
uniforms.apply(); // 应用到GPU
|
|
146
|
-
const buffer = uniforms.getBuffer(); // 获取GPUBuffer
|
|
147
|
-
```
|
|
74
|
+
name: 'texture_pass',
|
|
75
|
+
shaderCode: textureShader,
|
|
76
|
+
resources: [texture, sampler], // binding 0, 1
|
|
77
|
+
})
|
|
148
78
|
|
|
149
|
-
|
|
150
|
-
|
|
79
|
+
// Add Pass 2: Post-processing effect
|
|
80
|
+
const uniforms = renderer.createUniforms(8) // Create uniform variable binding
|
|
151
81
|
|
|
152
|
-
|
|
153
|
-
const
|
|
82
|
+
// Get Pass 1 output texture and bind to Pass 2
|
|
83
|
+
const texturePassOutput = renderer.getPassTexture('texture_pass')
|
|
84
|
+
renderer.addPass({
|
|
85
|
+
name: 'post_process',
|
|
86
|
+
shaderCode: postProcessShader,
|
|
87
|
+
resources: [
|
|
88
|
+
texturePassOutput, // @group(0) @binding(0)
|
|
89
|
+
sampler, // @group(0) @binding(1)
|
|
90
|
+
uniforms.getBuffer(), // @group(0) @binding(2)
|
|
91
|
+
],
|
|
92
|
+
})
|
|
93
|
+
|
|
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
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// Or manually execute single frame render
|
|
108
|
+
renderer.renderFrame()
|
|
154
109
|
```
|
|
155
110
|
|
|
156
|
-
## 🎨
|
|
111
|
+
## 🎨 Shader Examples
|
|
157
112
|
|
|
158
|
-
### Pass 1:
|
|
113
|
+
### Pass 1: Texture Rendering
|
|
159
114
|
|
|
160
115
|
```wgsl
|
|
116
|
+
// textureShader
|
|
161
117
|
struct VSOut {
|
|
162
118
|
@builtin(position) pos: vec4<f32>,
|
|
163
119
|
@location(0) uv: vec2<f32>,
|
|
@@ -172,34 +128,27 @@ fn vs_main(@location(0) p: vec3<f32>) -> VSOut {
|
|
|
172
128
|
return o;
|
|
173
129
|
}
|
|
174
130
|
|
|
175
|
-
@group(0) @binding(0) var
|
|
176
|
-
@group(0) @binding(1) var
|
|
177
|
-
@group(0) @binding(2) var mySampler: sampler;
|
|
131
|
+
@group(0) @binding(0) var myTexture: texture_2d<f32>;
|
|
132
|
+
@group(0) @binding(1) var mySampler: sampler;
|
|
178
133
|
|
|
179
134
|
@fragment
|
|
180
135
|
fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
|
|
181
|
-
|
|
182
|
-
let texColor = textureSample(myTexture, mySampler, uv);
|
|
183
|
-
|
|
184
|
-
// 背景与纹理混合
|
|
185
|
-
return vec4<f32>(
|
|
186
|
-
bgColor.r * (1.0 - texColor.a) + texColor.r * texColor.a,
|
|
187
|
-
bgColor.g * (1.0 - texColor.a) + texColor.g * texColor.a,
|
|
188
|
-
bgColor.b * (1.0 - texColor.a) + texColor.b * texColor.a,
|
|
189
|
-
1.0
|
|
190
|
-
);
|
|
136
|
+
return textureSample(myTexture, mySampler, uv);
|
|
191
137
|
}
|
|
192
138
|
```
|
|
193
139
|
|
|
194
|
-
### Pass 2:
|
|
140
|
+
### Pass 2: Brightness & Contrast Adjustment
|
|
195
141
|
|
|
196
142
|
```wgsl
|
|
143
|
+
// postProcessShader
|
|
197
144
|
struct Uniforms {
|
|
198
|
-
|
|
199
|
-
|
|
145
|
+
brightness: f32, // offset 0
|
|
146
|
+
contrast: f32, // offset 4
|
|
147
|
+
saturation: f32, // offset 8
|
|
148
|
+
// 4 bytes padding for vec3 alignment
|
|
200
149
|
}
|
|
201
150
|
|
|
202
|
-
@group(0) @binding(0) var prevTexture: texture_2d<f32>; // Pass 1
|
|
151
|
+
@group(0) @binding(0) var prevTexture: texture_2d<f32>; // Pass 1 output texture
|
|
203
152
|
@group(0) @binding(1) var mySampler: sampler;
|
|
204
153
|
@group(0) @binding(2) var<uniform> uniforms: Uniforms;
|
|
205
154
|
|
|
@@ -207,102 +156,247 @@ struct Uniforms {
|
|
|
207
156
|
fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
|
|
208
157
|
var color = textureSample(prevTexture, mySampler, uv);
|
|
209
158
|
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
color = vec4<f32>(color.r * scanline, color.g * scanline, color.b * scanline, color.a);
|
|
159
|
+
// Apply brightness
|
|
160
|
+
color.rgb += uniforms.brightness;
|
|
213
161
|
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
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;
|
|
217
164
|
|
|
218
|
-
|
|
219
|
-
let
|
|
220
|
-
|
|
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);
|
|
221
168
|
|
|
222
|
-
return vec4<f32>(
|
|
169
|
+
return clamp(color, vec4<f32>(0.0), vec4<f32>(1.0));
|
|
223
170
|
}
|
|
224
171
|
```
|
|
225
172
|
|
|
226
|
-
## 🔧 内置方法
|
|
227
173
|
|
|
228
|
-
|
|
174
|
+
## 📋 API
|
|
175
|
+
|
|
176
|
+
### createWGSLRenderer(canvas, options?)
|
|
177
|
+
|
|
178
|
+
Create WGSL renderer instance.
|
|
229
179
|
|
|
230
180
|
```typescript
|
|
231
|
-
|
|
232
|
-
const
|
|
181
|
+
import { createWGSLRenderer } from 'wgsl-renderer'
|
|
182
|
+
const renderer = await createWGSLRenderer(canvas)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
options:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
interface WGSLRendererOptions {
|
|
189
|
+
config?: GPUCanvasConfiguration;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
233
192
|
|
|
234
|
-
|
|
235
|
-
|
|
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
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### renderer.getPassTexture(passName)
|
|
214
|
+
|
|
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.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
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
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Corresponding WGSL binding:**
|
|
232
|
+
|
|
233
|
+
```wgsl
|
|
234
|
+
@group(0) @binding(0) var myTexture: texture_2d<f32>; // resources[0]
|
|
235
|
+
@group(0) @binding(1) var mySampler: sampler; // resources[1]
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
### renderer.createUniforms(length)
|
|
241
|
+
|
|
242
|
+
Create uniform variables using Float32Array, length unit is the number of floats.
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
const myUniforms = renderer.createUniforms(8) // 8 floats
|
|
246
|
+
|
|
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
|
+
],
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
myUniforms.values[0] = 1.0 // Set value
|
|
257
|
+
myUniforms.apply() // Apply to GPU
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### renderer.getContext()
|
|
261
|
+
|
|
262
|
+
Get WebGPU canvas context.
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
const context = renderer.getContext()
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### renderer.getDevice()
|
|
269
|
+
|
|
270
|
+
Get WebGPU device object.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const device = renderer.getDevice()
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Render Control
|
|
277
|
+
|
|
278
|
+
#### renderer.renderFrame()
|
|
279
|
+
Single frame rendering.
|
|
280
|
+
|
|
281
|
+
#### renderer.loopRender(callback?)
|
|
282
|
+
Built-in loop rendering with per-frame callback for real-time uniform updates.
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
renderer.loopRender(time => {
|
|
286
|
+
|
|
287
|
+
// Update uniforms every frame
|
|
288
|
+
myUniforms.values[2] = time * 0.001
|
|
289
|
+
myUniforms.apply()
|
|
290
|
+
})
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
#### renderer.stopLoop()
|
|
294
|
+
Stop loop rendering.
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
### renderer.createSampler(options?)
|
|
299
|
+
|
|
300
|
+
Create sampler with default parameters:
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
const options = {
|
|
236
304
|
magFilter: 'linear',
|
|
237
305
|
minFilter: 'linear',
|
|
238
306
|
addressModeU: 'clamp-to-edge',
|
|
239
307
|
addressModeV: 'clamp-to-edge',
|
|
240
|
-
}
|
|
308
|
+
}
|
|
241
309
|
|
|
242
|
-
|
|
243
|
-
const textureView = renderer.createTextureBinding(texture);
|
|
310
|
+
const sampler = renderer.createSampler(options)
|
|
244
311
|
```
|
|
245
312
|
|
|
246
|
-
|
|
313
|
+
## 🎯 Pass Flow
|
|
247
314
|
|
|
248
|
-
|
|
249
|
-
// 调整画布大小
|
|
250
|
-
renderer.resize(800, 600);
|
|
315
|
+
The renderer provides the following management features:
|
|
251
316
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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)`
|
|
321
|
+
|
|
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
|
|
326
|
+
|
|
327
|
+
3. **Complete Flexibility**
|
|
328
|
+
- Users decide binding order and method
|
|
329
|
+
- Support arbitrarily complex pass connections
|
|
330
|
+
- Can create circular dependencies (if needed)
|
|
255
331
|
|
|
256
|
-
|
|
332
|
+
**Example Usage:**
|
|
333
|
+
```typescript
|
|
334
|
+
// Method 1: Simple chain reference
|
|
335
|
+
renderer.addPass({
|
|
336
|
+
name: 'background',
|
|
337
|
+
resources: [bgTexture, sampler1],
|
|
338
|
+
})
|
|
257
339
|
|
|
258
|
-
|
|
340
|
+
renderer.addPass({
|
|
341
|
+
name: 'main_effect',
|
|
342
|
+
resources: [renderer.getPassTexture('background'), sampler2], // Reference background output
|
|
343
|
+
})
|
|
259
344
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
345
|
+
renderer.addPass({
|
|
346
|
+
name: 'post_process',
|
|
347
|
+
resources: [renderer.getPassTexture('main_effect'), sampler3], // Reference main_effect output
|
|
348
|
+
})
|
|
263
349
|
|
|
264
|
-
2
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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] })
|
|
268
354
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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')
|
|
273
359
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
360
|
+
renderer.addPass({
|
|
361
|
+
name: 'composite',
|
|
362
|
+
resources: [layer1Output, layer2Output, layer3Output, finalSampler],
|
|
363
|
+
})
|
|
277
364
|
|
|
278
|
-
|
|
365
|
+
// Method 3: Dynamic update binding
|
|
366
|
+
const mainPass = renderer.getPassByName('main_effect')
|
|
367
|
+
if (mainPass) {
|
|
279
368
|
|
|
369
|
+
// Dynamically change reference relationship at runtime
|
|
370
|
+
mainPass.updateBindGroup([renderer.getPassTexture('layer1'), newSampler])
|
|
371
|
+
}
|
|
280
372
|
```
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
373
|
+
|
|
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
|
+
})
|
|
382
|
+
|
|
383
|
+
// Error message: Cannot find pass named 'nonexistent_pass'. Available passes: [background, main_effect, ...]
|
|
287
384
|
```
|
|
288
385
|
|
|
289
|
-
## 🛠️
|
|
386
|
+
## 🛠️ Development
|
|
290
387
|
|
|
291
388
|
```bash
|
|
292
|
-
#
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
# 构建
|
|
296
|
-
npm run build
|
|
389
|
+
# Development mode
|
|
390
|
+
pnpm dev
|
|
297
391
|
|
|
298
|
-
#
|
|
299
|
-
|
|
392
|
+
# Build
|
|
393
|
+
pnpm build
|
|
300
394
|
```
|
|
301
395
|
|
|
302
|
-
## 📝
|
|
396
|
+
## 📝 License
|
|
303
397
|
|
|
304
398
|
MIT License
|
|
305
399
|
|
|
306
|
-
## 🤝
|
|
400
|
+
## 🤝 Contributing
|
|
307
401
|
|
|
308
|
-
|
|
402
|
+
Issues and Pull Requests are welcome!
|