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.
@@ -0,0 +1,417 @@
1
+ # WGSL Multi-Pass Renderer
2
+
3
+ 一个基于WebGPU和WGSL的多通道渲染器。
4
+
5
+ ## ✨ 特性
6
+
7
+ - 🖼️ **多Pass渲染** - 支持纹理渲染、后处理效果等自定义多通道渲染
8
+ - ⚡ **高性能渲染循环** - 支持单帧渲染和循环渲染模式
9
+ - 🛠️ **TypeScript支持** - 完整的类型定义和清晰的API分离
10
+ - 🎮 **Uniform系统** - 内置uniform buffer管理,支持动态参数
11
+
12
+ ## 🚀 快速开始
13
+
14
+ ### 安装
15
+
16
+ ```bash
17
+ npm i wgls-renderer
18
+ ```
19
+
20
+ ### 添加渲染通道
21
+
22
+ ```typescript
23
+ import { createWGSLRenderer } from 'wgls-renderer'
24
+
25
+ const canvas = document.querySelector('canvas');
26
+ const renderer = await createWGSLRenderer(canvas)
27
+
28
+ renderer.addPass({
29
+ name: 'my-pass',
30
+ shaderCode: `
31
+ struct VSOut {
32
+ @builtin(position) pos: vec4<f32>,
33
+ @location(0) uv: vec2<f32>,
34
+ };
35
+
36
+ @vertex
37
+ fn vs_main(@location(0) p: vec3<f32>) -> VSOut {
38
+ var o: VSOut;
39
+ o.pos = vec4<f32>(p, 1.0);
40
+ o.uv = p.xy * 0.5 + vec2<f32>(0.5, 0.5);
41
+ o.uv.y = 1.0 - o.uv.y;
42
+ return o;
43
+ }
44
+
45
+ @fragment
46
+ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
47
+ return vec4(1.0, 1.0, 0.0, 1.0);
48
+ }`,
49
+ })
50
+
51
+ renderer.renderFrame()
52
+ ```
53
+
54
+
55
+
56
+
57
+
58
+ ### 基础多通道使用
59
+
60
+ ```typescript
61
+ import { createWGSLRenderer } from 'wgls-renderer'
62
+
63
+ const canvas = document.getElementById('canvas')
64
+ const renderer = await createWGSLRenderer(canvas)
65
+
66
+ // 创建采样器
67
+ const sampler = renderer.createSampler()
68
+
69
+ // 加载图片纹理
70
+ const { texture, width, height } = await renderer.loadImageTexture('image.jpg')
71
+
72
+ // 添加Pass 1: 渲染纹理
73
+ renderer.addPass({
74
+ name: 'texture_pass',
75
+ shaderCode: textureShader,
76
+ resources: [texture, sampler], // binding 0, 1
77
+ })
78
+
79
+ // 添加Pass 2: 后处理效果
80
+ const uniforms = renderer.createUniforms(8) // 创建uniform变量的绑定
81
+
82
+ // 获取Pass 1的输出纹理并绑定到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
+ // 启动循环渲染,可以在回调函数中更新uniforms
95
+ renderer.loopRender((t) => {
96
+
97
+ // 更新uniforms (注意WebGPU的内存对齐规则)
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 (留空)
102
+ uniforms.values[4] = width // textureResolution.x
103
+ uniforms.values[5] = height // textureResolution.y
104
+ uniforms.apply() // 应用到GPU
105
+ })
106
+
107
+ // 或者手动改执行官单帧渲染
108
+ renderer.renderFrame()
109
+ ```
110
+
111
+
112
+ ## 🎨 着色器示例
113
+
114
+ ### Pass 1: 纹理渲染
115
+
116
+ ```wgsl
117
+ // textureShader
118
+ struct VSOut {
119
+ @builtin(position) pos: vec4<f32>,
120
+ @location(0) uv: vec2<f32>,
121
+ };
122
+
123
+ @vertex
124
+ fn vs_main(@location(0) p: vec3<f32>) -> VSOut {
125
+ var o: VSOut;
126
+ o.pos = vec4<f32>(p, 1.0);
127
+ o.uv = p.xy * 0.5 + vec2<f32>(0.5, 0.5);
128
+ o.uv.y = 1.0 - o.uv.y;
129
+ return o;
130
+ }
131
+
132
+ @group(0) @binding(0) var myTexture: texture_2d<f32>;
133
+ @group(0) @binding(1) var mySampler: sampler;
134
+
135
+ @fragment
136
+ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
137
+ return textureSample(myTexture, mySampler, uv);
138
+ }
139
+ ```
140
+
141
+ ### Pass 2: 动态后处理效果
142
+
143
+ ```wgsl
144
+ // postProcessShader
145
+ struct Uniforms {
146
+ resolution: vec2<f32>, // offset 0-7
147
+ time: f32, // offset 8
148
+ // 4 bytes padding for vec3 alignment
149
+ texResolution: vec2<f32>, // offset 16-23
150
+ speed: f32, // offset 24
151
+ // 8 bytes padding for next vec3
152
+ }
153
+
154
+ struct VSOut {
155
+ @builtin(position) pos: vec4<f32>,
156
+ @location(0) uv: vec2<f32>,
157
+ };
158
+
159
+ @vertex
160
+ fn vs_main(@location(0) p: vec3<f32>) -> VSOut {
161
+ var o: VSOut;
162
+ o.pos = vec4<f32>(p, 1.0);
163
+ o.uv = p.xy * 0.5 + vec2<f32>(0.5, 0.5);
164
+ o.uv.y = 1.0 - o.uv.y;
165
+ return o;
166
+ }
167
+
168
+ @group(0) @binding(0) var prevTexture: texture_2d<f32>; // Pass 1输出纹理
169
+ @group(0) @binding(1) var mySampler: sampler;
170
+ @group(0) @binding(2) var<uniform> uniforms: Uniforms;
171
+
172
+ @fragment
173
+ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
174
+ var color = textureSample(prevTexture, mySampler, uv);
175
+
176
+ // 动态扫描线效果
177
+ let scanline = 0.8 + 0.2 * sin(uv.y * 600.0 + uniforms.time * 5.0);
178
+ color = vec4<f32>(color.r * scanline, color.g * scanline, color.b * scanline, color.a);
179
+
180
+ // 动态波纹效果
181
+ let waveAmplitude = 0.05 + 0.02 * sin(uniforms.time * 2.0);
182
+ let waveX = sin(uv.x * 10.0 + uniforms.time * 3.0) * cos(uv.y * 8.0 + uniforms.time * 2.0) * waveAmplitude;
183
+
184
+ let finalR = clamp(color.r + waveX, 0.0, 1.0);
185
+ let finalG = clamp(color.g - waveX * 0.5, 0.0, 1.0);
186
+ let finalB = clamp(color.b + waveX * 0.3, 0.0, 1.0);
187
+
188
+ return vec4<f32>(finalR, finalG, finalB, color.a);
189
+ }
190
+ ```
191
+
192
+ ## 📋 API
193
+
194
+ ### createWGSLRenderer(canvas, options?)
195
+
196
+ 创建WGSL渲染器实例。
197
+
198
+ ```typescript
199
+ const renderer = await createWGSLRenderer(canvas)
200
+ ```
201
+
202
+ options:
203
+
204
+ ```ts
205
+ interface WGSLRendererOptions {
206
+ config?: GPUCanvasConfiguration;
207
+ }
208
+ ```
209
+
210
+ ### renderer.addPass(passOptions)
211
+
212
+ 添加渲染通道。
213
+
214
+ ```ts
215
+ interface RenderPassOptions {
216
+ name: string;
217
+ shaderCode: string;
218
+ entryPoints?: {
219
+ vertex?: string; // 默认是 'vs_main' 函数
220
+ fragment?: string; // 默认 'fs_main' 函数
221
+ };
222
+ clearColor?: { r: number; g: number; b: number; a: number };
223
+ blendMode?: 'additive' | 'alpha' | 'multiply' | 'none';
224
+ resources: GPUBindingResource[];
225
+ view?: GPUTextureView; // 可选的自定义View
226
+ format?: GPUTextureFormat; // 可选的自定义格式(使用自定义View时需要指定格式一致)
227
+ }
228
+ ```
229
+
230
+ ### renderer.getPassTexture(passName)
231
+
232
+ 获取指定通道的输出纹理,返回值并不是真正的纹理,而是一个占位符,只在实际渲染时自动将输出纹理绑定到着色器。
233
+
234
+ ```typescript
235
+ // 获取my_pass通道的输出纹理
236
+ const passOutputTexture = renderer.getPassTexture('my_pass')
237
+ const sampler = renderer.createSampler()
238
+ renderer.addPass({
239
+ name: 'my_pass2',
240
+ shaderCode: wgslShaderCode,
241
+ resources: [
242
+ passOutputTexture,
243
+ sampler,
244
+ ],
245
+ })
246
+ ```
247
+
248
+ **对应的WGSL绑定:**
249
+
250
+ ```wgsl
251
+ @group(0) @binding(0) var myTexture: texture_2d<f32>; // resources[0]
252
+ @group(0) @binding(1) var mySampler: sampler; // resources[1]
253
+ ```
254
+
255
+
256
+
257
+
258
+
259
+ ### renderer.createUniforms(length)
260
+
261
+ 创建uniform变量,使用Float32Array,length单位是float的数量。
262
+
263
+ ```typescript
264
+ const myUniforms = renderer.createUniforms(8) // 8个float
265
+
266
+ // 绑定到着色器
267
+ renderer.addPass({
268
+ name: 'my_pass',
269
+ shaderCode: wgslShaderCode,
270
+ resources: [
271
+ myUniforms.getBuffer(), // group(0) binding(0) var<uniform>
272
+ ],
273
+ })
274
+
275
+ myUniforms.values[0] = 1.0 // 设置值
276
+ myUniforms.apply() // 应用到GPU
277
+ ```
278
+
279
+ ### renderer.getContext()
280
+
281
+ 获取WebGPU画布上下文。
282
+
283
+ ```typescript
284
+ const context = renderer.getContext()
285
+ ```
286
+
287
+ ### renderer.getDevice()
288
+
289
+ 获取WebGPU设备对象。
290
+
291
+ ```typescript
292
+ const device = renderer.getDevice()
293
+ ```
294
+
295
+ ### 渲染控制
296
+
297
+ #### renderer.renderFrame()
298
+ 单帧渲染。
299
+
300
+ #### renderer.loopRender(callback?)
301
+ 内置的循环渲染,支持每帧回调,可用于实时更新uniforms。
302
+
303
+ ```typescript
304
+ renderer.loopRender((time) => {
305
+
306
+ // 每帧更新uniforms
307
+ myUniforms.values[2] = time * 0.001
308
+ myUniforms.apply()
309
+ })
310
+ ```
311
+
312
+ #### renderer.stopLoop()
313
+ 停止循环渲染。
314
+
315
+ ### renderer.createSampler(options?)
316
+
317
+ 创建采样器,默认参数:
318
+
319
+ ```ts
320
+ const options = {
321
+ magFilter: 'linear',
322
+ minFilter: 'linear',
323
+ addressModeU: 'clamp-to-edge',
324
+ addressModeV: 'clamp-to-edge',
325
+ }
326
+
327
+ const sampler = renderer.createSampler(options)
328
+ ```
329
+
330
+ ## 🎯 Pass流程
331
+
332
+ 渲染器提供以下管理功能:
333
+
334
+ 1. **用户定义所有Pass**
335
+ - 用户完全控制所有资源的绑定
336
+ - 可以通过`getPassTexture(passName)`获取任意pass的输出纹理
337
+ - 可以通过`getPassByName(passName)`获取pass对象
338
+
339
+ 2. **纹理管理**
340
+ - 每个pass自动创建输出纹理(格式:`{passName}_output`)
341
+ - 用户可以手动将这些纹理绑定到其他pass
342
+ - 最后一个pass自动渲染到canvas
343
+
344
+ 3. **完全灵活性**
345
+ - 用户决定绑定顺序和方式
346
+ - 支持任意复杂的pass连接关系
347
+ - 可以创建循环依赖(如果需要的话)
348
+
349
+ **示例用法:**
350
+ ```typescript
351
+ // 方法1: 简单的链式引用
352
+ renderer.addPass({
353
+ name: 'background',
354
+ resources: [bgTexture, sampler1]
355
+ })
356
+
357
+ renderer.addPass({
358
+ name: 'main_effect',
359
+ resources: [renderer.getPassTexture('background'), sampler2] // 引用background的输出
360
+ })
361
+
362
+ renderer.addPass({
363
+ name: 'post_process',
364
+ resources: [renderer.getPassTexture('main_effect'), sampler3] // 引用main_effect的输出
365
+ })
366
+
367
+ // 方法2: 复杂的多pass混合
368
+ renderer.addPass({ name: 'layer1', resources: [textureA, sampler] })
369
+ renderer.addPass({ name: 'layer2', resources: [textureB, sampler] })
370
+ renderer.addPass({ name: 'layer3', resources: [textureC, sampler] })
371
+
372
+ // 创建混合pass,同时引用多个不同的pass
373
+ const layer1Output = renderer.getPassTexture('layer1')
374
+ const layer2Output = renderer.getPassTexture('layer2')
375
+ const layer3Output = renderer.getPassTexture('layer3')
376
+
377
+ renderer.addPass({
378
+ name: 'composite',
379
+ resources: [layer1Output, layer2Output, layer3Output, finalSampler]
380
+ })
381
+
382
+ // 方法3: 动态更新绑定
383
+ const mainPass = renderer.getPassByName('main_effect')
384
+ if (mainPass) {
385
+ // 运行时动态改变引用关系
386
+ mainPass.updateBindGroup([renderer.getPassTexture('layer1'), newSampler])
387
+ }
388
+ ```
389
+
390
+ **错误处理示例:**
391
+ ```typescript
392
+ // 如果引用不存在的pass,会在渲染时抛出详细错误
393
+ const invalidTexture = renderer.getPassTexture('nonexistent_pass') // 这个pass不存在
394
+ renderer.addPass({
395
+ name: 'test',
396
+ resources: [invalidTexture, sampler] // 渲染时会抛出错误
397
+ })
398
+ // 错误信息: Cannot find pass named 'nonexistent_pass'. Available passes: [background, main_effect, ...]
399
+ ```
400
+
401
+ ## 🛠️ 开发
402
+
403
+ ```bash
404
+ # 开发模式
405
+ pnpm dev
406
+
407
+ # 构建
408
+ pnpm build
409
+ ```
410
+
411
+ ## 📝 许可证
412
+
413
+ MIT License
414
+
415
+ ## 🤝 贡献
416
+
417
+ 欢迎提交Issue和Pull Request!