wgsl-renderer 0.0.1 → 0.0.2

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 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
@@ -4,11 +4,12 @@
4
4
 
5
5
  ## ✨ 特性
6
6
 
7
- - 🔗 **自动Pass链** - Binding 0自动绑定到上一个pass的输出
8
- - 🖼️ **多Pass渲染** - 支持背景、纹理渲染、后处理效果等多通道
7
+ - 🔗 **灵活的Pass链** - Pass 2开始自动绑定上一个pass的输出
8
+ - 🖼️ **多Pass渲染** - 支持纹理渲染、后处理效果等多通道
9
9
  - ⚡ **高性能渲染循环** - 支持单帧渲染和循环渲染模式
10
10
  - 🛠️ **TypeScript支持** - 完整的类型定义和清晰的API分离
11
11
  - 🎮 **Uniform系统** - 内置uniform buffer管理,支持动态参数
12
+ - 🔄 **自动Resize** - 内置ResizeObserver自动处理canvas大小变化
12
13
 
13
14
  ## 🚀 快速开始
14
15
 
@@ -21,47 +22,116 @@ npm i wgls-renderer
21
22
  ### 基础使用
22
23
 
23
24
  ```typescript
24
- import { createWGSLRenderer } from 'wgls-renderer';
25
+ import { createWGSLRenderer } from 'wgls-renderer'
25
26
 
26
- const canvas = document.getElementById('canvas');
27
- const renderer = await createWGSLRenderer(canvas, {
28
- backgroundColor: 0x66CCFF // 支持多种格式:0xRRGGBB, "#RRGGBB", {r, g, b}
29
- });
27
+ const canvas = document.getElementById('canvas')
28
+ const renderer = await createWGSLRenderer(canvas)
30
29
 
31
30
  // 创建采样器
32
- const sampler = renderer.createSampler();
31
+ const sampler = renderer.createSampler()
33
32
 
34
- // 加载纹理
35
- const { texture } = await renderer.loadTexture('image.jpg');
33
+ // 加载图片纹理
34
+ const { texture } = await renderer.loadImageTexture('image.jpg')
36
35
 
37
36
  // 添加Pass 1: 渲染纹理
38
37
  renderer.addPass({
39
38
  name: 'texture_pass',
40
39
  shaderCode: textureShader,
41
40
  blendMode: 'alpha',
42
- resources: [texture.createView(), sampler] // binding 1, 2
43
- });
41
+ resources: [texture, sampler], // binding 0, 1
42
+ })
44
43
 
45
- // 添加Pass 2: 后处理效果
46
- const uniforms = renderer.createUniforms(4); // time, resolution.x, resolution.y, padding
44
+ // 添加Pass 2: 后处理效果 (自动绑定Pass 1的输出到binding 0)
45
+ const uniforms = renderer.createUniforms(16) // 支持复杂的uniform结构
47
46
  renderer.addPass({
48
47
  name: 'post_process',
49
48
  shaderCode: postProcessShader,
50
49
  blendMode: 'alpha',
51
- resources: [sampler, uniforms.getBuffer()] // binding 1, 2
52
- });
50
+ resources: [sampler, uniforms.getBuffer()], // 对应binding 1, 2 (binding 0自动绑定到Pass 1的输出)
51
+ })
53
52
 
54
- // 启动循环渲染,支持uniforms更新
53
+ // 启动循环渲染,可以在回调函数中更新uniforms
55
54
  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
- });
55
+
56
+ // 更新uniforms (注意WebGPU的内存对齐规则)
57
+ uniforms.values[0] = canvas.width // resolution.x
58
+ uniforms.values[1] = canvas.height // resolution.y
59
+ uniforms.values[2] = performance.now() // time
60
+ uniforms.values[3] = 0 // padding (vec3对齐)
61
+ uniforms.values[4] = 1024 // textureResolution.x
62
+ uniforms.values[5] = 1024 // textureResolution.y
63
+ uniforms.apply()
64
+ })
62
65
 
63
66
  // 或者单帧渲染
64
- renderer.renderFrame();
67
+ renderer.renderFrame()
68
+ ```
69
+
70
+
71
+ ## 🎨 着色器示例
72
+
73
+ ### Pass 1: 纹理渲染
74
+
75
+ ```wgsl
76
+ // textureShader
77
+ struct VSOut {
78
+ @builtin(position) pos: vec4<f32>,
79
+ @location(0) uv: vec2<f32>,
80
+ };
81
+
82
+ @vertex
83
+ fn vs_main(@location(0) p: vec3<f32>) -> VSOut {
84
+ var o: VSOut;
85
+ o.pos = vec4<f32>(p, 1.0);
86
+ o.uv = p.xy * 0.5 + vec2<f32>(0.5, 0.5);
87
+ o.uv.y = 1.0 - o.uv.y;
88
+ return o;
89
+ }
90
+
91
+ @group(0) @binding(0) var myTexture: texture_2d<f32>;
92
+ @group(0) @binding(1) var mySampler: sampler;
93
+
94
+ @fragment
95
+ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
96
+ return textureSample(myTexture, mySampler, uv);
97
+ }
98
+ ```
99
+
100
+ ### Pass 2: 动态后处理效果
101
+
102
+ ```wgsl
103
+ // postProcessShader
104
+ struct Uniforms {
105
+ resolution: vec2<f32>, // offset 0-7
106
+ time: f32, // offset 8
107
+ // 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
+ }
112
+
113
+ @group(0) @binding(0) var prevTexture: texture_2d<f32>; // 自动绑定到Pass 1的输出纹理
114
+ @group(0) @binding(1) var mySampler: sampler;
115
+ @group(0) @binding(2) var<uniform> uniforms: Uniforms;
116
+
117
+ @fragment
118
+ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
119
+ var color = textureSample(prevTexture, mySampler, uv);
120
+
121
+ // 动态扫描线效果
122
+ let scanline = 0.8 + 0.2 * sin(uv.y * 600.0 + uniforms.time * 5.0);
123
+ color = vec4<f32>(color.r * scanline, color.g * scanline, color.b * scanline, color.a);
124
+
125
+ // 动态波纹效果
126
+ let waveAmplitude = 0.05 + 0.02 * sin(uniforms.time * 2.0);
127
+ let waveX = sin(uv.x * 10.0 + uniforms.time * 3.0) * cos(uv.y * 8.0 + uniforms.time * 2.0) * waveAmplitude;
128
+
129
+ let finalR = clamp(color.r + waveX, 0.0, 1.0);
130
+ let finalG = clamp(color.g - waveX * 0.5, 0.0, 1.0);
131
+ let finalB = clamp(color.b + waveX * 0.3, 0.0, 1.0);
132
+
133
+ return vec4<f32>(finalR, finalG, finalB, color.a);
134
+ }
65
135
  ```
66
136
 
67
137
  ## 📋 API
@@ -71,15 +141,32 @@ renderer.renderFrame();
71
141
  创建WGSL渲染器实例。
72
142
 
73
143
  ```typescript
74
- const renderer = await createWGSLRenderer(canvas, {
75
- backgroundColor: 0x66CCFF // 支持多种格式
76
- });
144
+ const renderer = await createWGSLRenderer(canvas)
145
+ ```
146
+
147
+ ### createUniforms(length)
148
+
149
+ 创建uniform变量,length单位为float数量。
150
+
151
+ ```typescript
152
+ const myUniforms = renderer.createUniforms(8) // 8个float
153
+ ```
154
+
155
+ ### getContext()
156
+
157
+ 获取WebGPU画布上下文。
158
+
159
+ ```typescript
160
+ const context = renderer.getContext()
77
161
  ```
78
162
 
163
+ ### getDevice()
79
164
 
80
- - `number`: 十六进制颜色 `0xRRGGBB`
81
- - `string`: 十六进制字符串 `"#RRGGBB"`
82
- - `object`: RGB对象 `{r: 0-1, g: 0-1, b: 0-1}`
165
+ 获取WebGPU设备对象。
166
+
167
+ ```typescript
168
+ const device = renderer.getDevice()
169
+ ```
83
170
 
84
171
  ### 渲染控制
85
172
 
@@ -87,7 +174,7 @@ const renderer = await createWGSLRenderer(canvas, {
87
174
  单帧渲染,不循环。
88
175
 
89
176
  ```typescript
90
- renderer.renderFrame();
177
+ renderer.renderFrame()
91
178
  ```
92
179
 
93
180
  #### loopRender(callback?)
@@ -95,17 +182,18 @@ renderer.renderFrame();
95
182
 
96
183
  ```typescript
97
184
  renderer.loopRender(() => {
185
+
98
186
  // 每帧更新uniforms
99
- myUniforms.values[0] = performance.now() / 1000.0;
100
- myUniforms.apply();
101
- });
187
+ myUniforms.values[0] = performance.now() / 1000.0
188
+ myUniforms.apply()
189
+ })
102
190
  ```
103
191
 
104
192
  #### stopLoop()
105
193
  停止循环渲染。
106
194
 
107
195
  ```typescript
108
- renderer.stopLoop();
196
+ renderer.stopLoop()
109
197
  ```
110
198
 
111
199
  ### addPass(descriptor)
@@ -117,19 +205,32 @@ renderer.addPass({
117
205
  name: 'my_pass',
118
206
  shaderCode: wgslShaderCode,
119
207
  blendMode: 'alpha',
120
- resources: [textureView, sampler] // 资源数组
121
- });
208
+ resources: [textureView, sampler], // 资源数组
209
+ })
122
210
  ```
123
211
 
124
212
  **资源数组绑定规则:**
125
- - **Binding 0**: 自动绑定到上一个pass的输出(无需在数组中指定)
126
- - **Binding 1**: `resources[0]`
127
- - **Binding 2**: `resources[1]`
128
- - 以此类推...
213
+
214
+ - **Pass 1**: 无自动绑定,完全自由
215
+ - **Binding 0**: `resources[0]`
216
+ - **Binding 1**: `resources[1]`
217
+ - 以此类推...
218
+
219
+ - **Pass 2及以上**: 自动绑定上一个pass的输出
220
+ - **Binding 0**: 上一个pass的输出纹理(自动)
221
+ - **Binding 1**: `resources[0]`
222
+ - **Binding 2**: `resources[1]`
223
+ - 以此类推...
129
224
 
130
225
  **对应的WGSL绑定:**
226
+
131
227
  ```wgsl
132
- @group(0) @binding(0) var prevTexture: texture_2d<f32>; // 自动
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]
231
+
232
+ // Pass 2+:
233
+ @group(0) @binding(0) var prevTexture: texture_2d<f32>; // 自动绑定
133
234
  @group(0) @binding(1) var myTexture: texture_2d<f32>; // resources[0]
134
235
  @group(0) @binding(2) var mySampler: sampler; // resources[1]
135
236
  ```
@@ -140,163 +241,148 @@ renderer.addPass({
140
241
  创建uniform buffer管理对象。
141
242
 
142
243
  ```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
244
+ const uniforms = renderer.createUniforms(4) // 4个float
245
+ uniforms.values[0] = 1.0 // 设置值
246
+ uniforms.apply() // 应用到GPU
247
+ const buffer = uniforms.getBuffer() // 获取GPUBuffer
147
248
  ```
148
249
 
149
- #### getUniformsByID(id)
150
- 通过symbol ID获取uniform对象。
151
-
152
- ```typescript
153
- const uniform = renderer.getUniformsByID(myUniformSymbol);
250
+ **JavaScript Uniforms设置 (注意内存对齐):**
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()
154
264
  ```
155
265
 
156
- ## 🎨 着色器示例
157
-
158
- ### Pass 1: 纹理渲染
266
+ ## 🔧 内置方法
159
267
 
160
- ```wgsl
161
- struct VSOut {
162
- @builtin(position) pos: vec4<f32>,
163
- @location(0) uv: vec2<f32>,
164
- };
268
+ ### 纹理相关
165
269
 
166
- @vertex
167
- fn vs_main(@location(0) p: vec3<f32>) -> VSOut {
168
- var o: VSOut;
169
- o.pos = vec4<f32>(p, 1.0);
170
- o.uv = p.xy * 0.5 + vec2<f32>(0.5, 0.5);
171
- o.uv.y = 1.0 - o.uv.y;
172
- return o;
173
- }
270
+ ```typescript
271
+ // 从url加载图片纹理
272
+ const { texture, width, height } = await renderer.loadImageTexture('image.png')
174
273
 
175
- @group(0) @binding(0) var prevTexture: texture_2d<f32>; // 内置的纯色背景纹理
176
- @group(0) @binding(1) var myTexture: texture_2d<f32>;
177
- @group(0) @binding(2) var mySampler: sampler;
274
+ // 创建采样器
275
+ const sampler = renderer.createSampler()
178
276
 
179
- @fragment
180
- fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
181
- let bgColor = textureSample(prevTexture, mySampler, uv);
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
- );
191
- }
277
+ // 绑定到Pass
278
+ const textureView = texture.createView()
279
+ renderer.addPass({
280
+ name: 'texture-pass',
281
+ shaderCode: shaderCode,
282
+ resources: [
283
+ textureView,
284
+ sampler,
285
+ ],
286
+ })
192
287
  ```
193
288
 
194
- ### Pass 2: 动态后处理效果
195
-
196
289
  ```wgsl
197
- struct Uniforms {
198
- time: f32,
199
- resolution: vec2<f32>,
200
- }
201
-
202
- @group(0) @binding(0) var prevTexture: texture_2d<f32>; // Pass 1的输出纹理
290
+ // 如果是Pass 1:
291
+ @group(0) @binding(0) var myTexture: texture_2d<f32>;
203
292
  @group(0) @binding(1) var mySampler: sampler;
204
- @group(0) @binding(2) var<uniform> uniforms: Uniforms;
205
-
206
- @fragment
207
- fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
208
- var color = textureSample(prevTexture, mySampler, uv);
209
-
210
- // 动态扫描线效果
211
- let scanline = 0.8 + 0.2 * sin(uv.y * 600.0 + uniforms.time * 5.0);
212
- color = vec4<f32>(color.r * scanline, color.g * scanline, color.b * scanline, color.a);
213
293
 
214
- // 动态波纹效果
215
- let waveAmplitude = 0.05 + 0.02 * sin(uniforms.time * 2.0);
216
- let waveX = sin(uv.x * 10.0 + uniforms.time * 3.0) * cos(uv.y * 8.0 + uniforms.time * 2.0) * waveAmplitude;
294
+ // 如果是Pass 2及以后:
295
+ @group(0) @binding(0) var prevTexture: texture_2d<f32>; // 自动绑定,上一个Pass的输出纹理
296
+ @group(0) @binding(1) var myTexture: texture_2d<f32>;
297
+ @group(0) @binding(2) var mySampler: sampler;
298
+ ```
217
299
 
218
- let finalR = clamp(color.r + waveX, 0.0, 1.0);
219
- let finalG = clamp(color.g - waveX * 0.5, 0.0, 1.0);
220
- let finalB = clamp(color.b + waveX * 0.3, 0.0, 1.0);
300
+ ### Uniform变量
221
301
 
222
- return vec4<f32>(finalR, finalG, finalB, color.a);
223
- }
302
+ ```typescript
303
+ // 创建uniform buffer,length单位为float数量
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
316
+ renderer.addPass({
317
+ name: 'uniform-pass',
318
+ shaderCode: shaderCode,
319
+ resources: [
320
+
321
+ // 数组第0项,Pass 1着色器中对应@group(0) @binding(0),Pass2及以后的着色器中是@group(0) @binding(1)
322
+ uniformBuffer,
323
+ ],
324
+ })
224
325
  ```
225
326
 
226
- ## 🔧 内置方法
227
-
228
- ### 纹理相关
327
+ ```wgsl
328
+ struct Uniforms {
329
+ value1: f32, // 对应 uniforms.values[0]
330
+ value2: f32, // 对应 uniforms.values[1]
331
+ value3: f32, // 对应 uniforms.values[2]
332
+ textureResolution: vec2<f32>, // x, y分别对应 uniforms.values[4], uniforms.values[5]
333
+ // ...
334
+ }
229
335
 
230
- ```typescript
231
- // 加载纹理
232
- const { texture, width, height } = await renderer.loadTexture('image.png');
336
+ // 如果是Pass 1:
337
+ @group(0) @binding(0) var<uniform> uniforms: Uniforms;
233
338
 
234
- // 创建采样器
235
- const sampler = renderer.createSampler({
236
- magFilter: 'linear',
237
- minFilter: 'linear',
238
- addressModeU: 'clamp-to-edge',
239
- addressModeV: 'clamp-to-edge',
240
- });
241
-
242
- // 创建纹理绑定
243
- const textureView = renderer.createTextureBinding(texture);
339
+ // 如果是Pass 2及以后:
340
+ @group(0) @binding(0) var prevTexture: texture_2d<f32>; // 自动绑定,上一个Pass的输出纹理
341
+ @group(0) @binding(1) var<uniform> uniforms: Uniforms;
244
342
  ```
245
343
 
246
344
  ### 控制相关
247
345
 
248
346
  ```typescript
249
347
  // 调整画布大小
250
- renderer.resize(800, 600);
348
+ renderer.resize(800, 600)
251
349
 
252
350
  // 停止渲染
253
- renderer.stopLoop();
351
+ renderer.stopLoop()
254
352
  ```
255
353
 
256
354
  ## 🎯 Pass流程
257
355
 
258
356
  渲染器自动管理以下pass流程:
259
357
 
260
- 1. **Background Pass** (内置)
261
- - 渲染纯色背景
358
+ 1. **User Pass 1**
359
+ - 无自动绑定,完全自由
360
+ - Binding 0+: 用户资源
262
361
  - 输出到 `pass_0_output`
263
362
 
264
- 2. **User Pass 1**
265
- - Binding 0: 背景输出
363
+ 2. **User Pass 2**
364
+ - Binding 0: Pass 1输出纹理(自动)
266
365
  - Binding 1+: 用户资源
267
366
  - 输出到 `pass_1_output`
268
367
 
269
- 3. **User Pass 2**
270
- - Binding 0: Pass 1输出
368
+ 3. **User Pass 3+**
369
+ - Binding 0: 上一个pass输出纹理(自动)
271
370
  - Binding 1+: 用户资源
272
- - 输出到 `pass_2_output`
371
+ - 输出到 `pass_N-1_output`
273
372
 
274
373
  4. **Final Pass**
275
- - Binding 0: 上一个pass输出
374
+ - Binding 0: 上一个pass输出纹理(自动)
375
+ - Binding 1+: 用户资源
276
376
  - 渲染到canvas
277
377
 
278
- ## 📁 项目结构
279
-
280
- ```
281
- src/
282
- ├── index.ts # 主渲染器类,包含完整的API
283
- ├── RenderPass.ts # Pass渲染逻辑和类型定义
284
- ├── TextureManager.ts # 纹理管理
285
- examples/
286
- └── multi-pass-demo.html # 完整示例,包含纹理、动态uniforms效果
287
- ```
288
-
289
378
  ## 🛠️ 开发
290
379
 
291
380
  ```bash
292
381
  # 开发模式
293
- npm run dev
382
+ pnpm dev
294
383
 
295
384
  # 构建
296
- npm run build
297
-
298
- # 类型检查
299
- npm run type-check
385
+ pnpm build
300
386
  ```
301
387
 
302
388
  ## 📝 许可证