@footgun/cobalt 0.1.0

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.
Files changed (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -0
  3. package/bundle.js +284 -0
  4. package/cobalt2.jpeg +0 -0
  5. package/esbuild.js +20 -0
  6. package/examples/01-primitives/Game.js +8 -0
  7. package/examples/01-primitives/component-animation.js +8 -0
  8. package/examples/01-primitives/component-transform.js +13 -0
  9. package/examples/01-primitives/constants.js +6 -0
  10. package/examples/01-primitives/deps.js +2 -0
  11. package/examples/01-primitives/entity-sprite.js +47 -0
  12. package/examples/01-primitives/index.html +191 -0
  13. package/examples/01-primitives/system-renderer.js +37 -0
  14. package/examples/02-sprites/Game.js +8 -0
  15. package/examples/02-sprites/assets/spritesheet.json +6276 -0
  16. package/examples/02-sprites/assets/spritesheet.png +0 -0
  17. package/examples/02-sprites/assets/spritesheet_emissive.png +0 -0
  18. package/examples/02-sprites/component-animation.js +8 -0
  19. package/examples/02-sprites/component-transform.js +13 -0
  20. package/examples/02-sprites/constants.js +6 -0
  21. package/examples/02-sprites/deps.js +2 -0
  22. package/examples/02-sprites/entity-sprite.js +47 -0
  23. package/examples/02-sprites/index.html +310 -0
  24. package/examples/02-sprites/system-renderer.js +38 -0
  25. package/examples/03-tiles/Game.js +8 -0
  26. package/examples/03-tiles/assets/spelunky-tiles.png +0 -0
  27. package/examples/03-tiles/assets/spelunky0.png +0 -0
  28. package/examples/03-tiles/assets/spelunky1.png +0 -0
  29. package/examples/03-tiles/component-animation.js +8 -0
  30. package/examples/03-tiles/component-transform.js +13 -0
  31. package/examples/03-tiles/constants.js +6 -0
  32. package/examples/03-tiles/deps.js +2 -0
  33. package/examples/03-tiles/entity-sprite.js +47 -0
  34. package/examples/03-tiles/index.html +309 -0
  35. package/examples/03-tiles/system-renderer.js +38 -0
  36. package/examples/04-overlay/assets/spritesheet.json +22 -0
  37. package/examples/04-overlay/assets/spritesheet.png +0 -0
  38. package/examples/04-overlay/assets/spritesheet_emissive.png +0 -0
  39. package/examples/04-overlay/constants.js +6 -0
  40. package/examples/04-overlay/deps.js +1 -0
  41. package/examples/04-overlay/index.html +133 -0
  42. package/examples/05-bloom/Game.js +8 -0
  43. package/examples/05-bloom/assets/spritesheet.json +6276 -0
  44. package/examples/05-bloom/assets/spritesheet.png +0 -0
  45. package/examples/05-bloom/assets/spritesheet_emissive.png +0 -0
  46. package/examples/05-bloom/component-animation.js +8 -0
  47. package/examples/05-bloom/component-transform.js +13 -0
  48. package/examples/05-bloom/constants.js +6 -0
  49. package/examples/05-bloom/deps.js +2 -0
  50. package/examples/05-bloom/entity-sprite.js +47 -0
  51. package/examples/05-bloom/index.html +357 -0
  52. package/examples/05-bloom/system-renderer.js +38 -0
  53. package/examples/06-displacement/Game.js +8 -0
  54. package/examples/06-displacement/assets/displacement_map_repeat.jpg +0 -0
  55. package/examples/06-displacement/assets/spelunky-tiles.png +0 -0
  56. package/examples/06-displacement/assets/spelunky0.png +0 -0
  57. package/examples/06-displacement/assets/spelunky1.png +0 -0
  58. package/examples/06-displacement/component-animation.js +8 -0
  59. package/examples/06-displacement/component-transform.js +13 -0
  60. package/examples/06-displacement/constants.js +6 -0
  61. package/examples/06-displacement/deps.js +2 -0
  62. package/examples/06-displacement/entity-sprite.js +47 -0
  63. package/examples/06-displacement/index.html +350 -0
  64. package/examples/06-displacement/system-renderer.js +38 -0
  65. package/examples/07-sdl/assets/spritesheet.json +22 -0
  66. package/examples/07-sdl/assets/spritesheet.png +0 -0
  67. package/examples/07-sdl/assets/spritesheet_emissive.png +0 -0
  68. package/examples/07-sdl/main.js +109 -0
  69. package/examples/07-sdl/package.json +19 -0
  70. package/examples/08-light/Game.js +8 -0
  71. package/examples/08-light/assets/spelunky-tiles.png +0 -0
  72. package/examples/08-light/assets/spelunky0.png +0 -0
  73. package/examples/08-light/assets/spelunky1.png +0 -0
  74. package/examples/08-light/constants.js +6 -0
  75. package/examples/08-light/deps.js +2 -0
  76. package/examples/08-light/index.html +477 -0
  77. package/package.json +34 -0
  78. package/src/bloom/bloom.js +467 -0
  79. package/src/bloom/bloom.wgsl +176 -0
  80. package/src/cobalt.js +231 -0
  81. package/src/create-texture-from-buffer.js +39 -0
  82. package/src/create-texture-from-url.js +35 -0
  83. package/src/create-texture.js +46 -0
  84. package/src/deps.js +3 -0
  85. package/src/displacement/composition.wgsl +58 -0
  86. package/src/displacement/displacement-composition.ts +161 -0
  87. package/src/displacement/displacement-parameters-buffer.ts +44 -0
  88. package/src/displacement/displacement-texture.ts +221 -0
  89. package/src/displacement/displacement.js +160 -0
  90. package/src/displacement/displacement.wgsl +31 -0
  91. package/src/displacement/triangles-buffer.ts +95 -0
  92. package/src/fb-blit/fb-blit.js +161 -0
  93. package/src/fb-blit/fb-blit.wgsl +40 -0
  94. package/src/fb-texture/fb-texture.js +56 -0
  95. package/src/light/README.md +61 -0
  96. package/src/light/light.js +148 -0
  97. package/src/light/lights-buffer.ts +98 -0
  98. package/src/light/lights-renderer.ts +278 -0
  99. package/src/light/public-api.js +20 -0
  100. package/src/light/readme/01_illumination.webp +0 -0
  101. package/src/light/readme/02_lights_texture.webp +0 -0
  102. package/src/light/readme/03_lights_texture_decomposed.webp +0 -0
  103. package/src/light/readme/04_lights_texture_mask.webp +0 -0
  104. package/src/light/readme/05_lights_obstacle_decomposition.webp +0 -0
  105. package/src/light/readme/06_lights_hard_cast_shadows.webp +0 -0
  106. package/src/light/texture/lights-texture-initializer.ts +191 -0
  107. package/src/light/texture/lights-texture-mask.ts +286 -0
  108. package/src/light/texture/lights-texture.ts +121 -0
  109. package/src/light/types.ts +23 -0
  110. package/src/light/viewport.ts +63 -0
  111. package/src/overlay/constants.js +1 -0
  112. package/src/overlay/overlay.js +341 -0
  113. package/src/overlay/overlay.wgsl +88 -0
  114. package/src/primitives/constants.js +1 -0
  115. package/src/primitives/primitives.js +252 -0
  116. package/src/primitives/primitives.wgsl +54 -0
  117. package/src/primitives/public-api.js +325 -0
  118. package/src/scene-composite/scene-composite.js +168 -0
  119. package/src/scene-composite/scene-composite.wgsl +94 -0
  120. package/src/sprite/constants.js +1 -0
  121. package/src/sprite/create-sprite-quads.js +60 -0
  122. package/src/sprite/public-api.js +215 -0
  123. package/src/sprite/read-spritesheet.js +103 -0
  124. package/src/sprite/sorted-binary-insert.js +45 -0
  125. package/src/sprite/sprite.js +268 -0
  126. package/src/sprite/sprite.wgsl +103 -0
  127. package/src/sprite/spritesheet.js +212 -0
  128. package/src/tile/atlas.js +193 -0
  129. package/src/tile/tile.js +171 -0
  130. package/src/tile/tile.wgsl +105 -0
  131. package/src/uuid.js +3 -0
@@ -0,0 +1,54 @@
1
+ struct TransformData {
2
+ view: mat4x4<f32>,
3
+ projection: mat4x4<f32>
4
+ };
5
+
6
+ @binding(0) @group(0) var<uniform> transformUBO: TransformData;
7
+
8
+ struct Fragment {
9
+ @builtin(position) Position : vec4<f32>,
10
+ @location(0) Color : vec4<f32>,
11
+ };
12
+
13
+ @vertex
14
+ fn vs_main(@location(0) vertexPosition: vec2<f32>,
15
+ @location(1) vertexColor: vec4<f32>) -> Fragment {
16
+
17
+ var sx: f32 = 1.0; //sprites.models[i_id].scale.x;
18
+ var sy: f32 = 1.0; // sprites.models[i_id].scale.y;
19
+ var sz: f32 = 1.0;
20
+
21
+ var rot: f32 = 0.0; //sprites.models[i_id].rotation;
22
+
23
+ var tx: f32 = 1.0; //sprites.models[i_id].translate.x;
24
+ var ty: f32 = 1.0; //sprites.models[i_id].translate.y;
25
+ var tz: f32 = 0;
26
+
27
+ var s = sin(rot);
28
+ var c = cos(rot);
29
+
30
+ // https://webglfundamentals.org/webgl/lessons/webgl-2d-matrices.html
31
+
32
+ var scaleM: mat4x4<f32> = mat4x4<f32>(sx, 0.0, 0.0, 0.0,
33
+ 0.0, sy, 0.0, 0.0,
34
+ 0.0, 0.0, sz, 0.0,
35
+ 0, 0, 0, 1.0);
36
+
37
+ // rotation and translation
38
+ var modelM: mat4x4<f32> = mat4x4<f32>(c, s, 0.0, 0.0,
39
+ -s, c, 0.0, 0.0,
40
+ 0.0, 0.0, 1.0, 0.0,
41
+ tx, ty, tz, 1.0) * scaleM;
42
+
43
+ var output : Fragment;
44
+
45
+ output.Position = transformUBO.projection * transformUBO.view * modelM * vec4<f32>(vertexPosition, 0.0, 1.0);
46
+ output.Color = vertexColor;
47
+
48
+ return output;
49
+ }
50
+
51
+ @fragment
52
+ fn fs_main(@location(0) Color: vec4<f32>) -> @location(0) vec4<f32> {
53
+ return Color;
54
+ }
@@ -0,0 +1,325 @@
1
+ import { vec2 } from '../deps.js'
2
+
3
+
4
+ function line (cobalt, node, start, end, color, lineWidth=1) {
5
+
6
+ const delta = vec2.sub(end, start)
7
+
8
+ const unitBasis = vec2.normalize(delta)
9
+ const perp = perpendicularComponent(unitBasis)
10
+
11
+ const halfLineWidth = lineWidth / 2
12
+
13
+ let i = node.data.vertexCount * 6 // 2 floats position + 4 floats color per vertex
14
+
15
+ // triangle 1
16
+ // pt 1
17
+ node.data.vertices[i + 0] = start[0] + perp[0] * halfLineWidth
18
+ node.data.vertices[i + 1] = start[1] + perp[1] * halfLineWidth
19
+
20
+ // pt1 color
21
+ node.data.vertices[i + 2] = color[0]
22
+ node.data.vertices[i + 3] = color[1]
23
+ node.data.vertices[i + 4] = color[2]
24
+ node.data.vertices[i + 5] = color[3]
25
+
26
+ // pt 2
27
+ node.data.vertices[i + 6] = start[0] - perp[0] * halfLineWidth
28
+ node.data.vertices[i + 7] = start[1] - perp[1] * halfLineWidth
29
+
30
+ // pt2 color
31
+ node.data.vertices[i + 8] = color[0]
32
+ node.data.vertices[i + 9] = color[1]
33
+ node.data.vertices[i + 10] = color[2]
34
+ node.data.vertices[i + 11] = color[3]
35
+
36
+ // pt 3
37
+ node.data.vertices[i + 12] = end[0] + perp[0] * halfLineWidth
38
+ node.data.vertices[i + 13] = end[1] + perp[1] * halfLineWidth
39
+
40
+ // pt3 color
41
+ node.data.vertices[i + 14] = color[0]
42
+ node.data.vertices[i + 15] = color[1]
43
+ node.data.vertices[i + 16] = color[2]
44
+ node.data.vertices[i + 17] = color[3]
45
+
46
+
47
+ // triangle 2
48
+ // pt 2
49
+ node.data.vertices[i + 18] = start[0] - perp[0] * halfLineWidth
50
+ node.data.vertices[i + 19] = start[1] - perp[1] * halfLineWidth
51
+
52
+ // pt2 color
53
+ node.data.vertices[i + 20] = color[0]
54
+ node.data.vertices[i + 21] = color[1]
55
+ node.data.vertices[i + 22] = color[2]
56
+ node.data.vertices[i + 23] = color[3]
57
+
58
+ // pt 3
59
+ node.data.vertices[i + 24] = end[0] + perp[0] * halfLineWidth
60
+ node.data.vertices[i + 25] = end[1] + perp[1] * halfLineWidth
61
+
62
+ // pt3 color
63
+ node.data.vertices[i + 26] = color[0]
64
+ node.data.vertices[i + 27] = color[1]
65
+ node.data.vertices[i + 28] = color[2]
66
+ node.data.vertices[i + 29] = color[3]
67
+
68
+ // pt 4
69
+ node.data.vertices[i + 30] = end[0] - perp[0] * halfLineWidth
70
+ node.data.vertices[i + 31] = end[1] - perp[1] * halfLineWidth
71
+
72
+ // pt4 color
73
+ node.data.vertices[i + 32] = color[0]
74
+ node.data.vertices[i + 33] = color[1]
75
+ node.data.vertices[i + 34] = color[2]
76
+ node.data.vertices[i + 35] = color[3]
77
+
78
+
79
+ node.data.vertexCount += 6
80
+
81
+ node.data.dirty = true
82
+ }
83
+
84
+
85
+ // return component of vector perpendicular to a unit basis vector
86
+ // (IMPORTANT NOTE: assumes "basis" has unit magnitude (length==1))
87
+ function perpendicularComponent (inp) {
88
+ return [ -inp[1], inp[0] ]
89
+ }
90
+
91
+
92
+ export default {
93
+ line,
94
+
95
+ ellipse: function (cobalt, node, center, halfWidth, halfHeight, numSegments, color, lineWidth=1) {
96
+
97
+ const [ x, y ] = center
98
+
99
+ // angle between each segment
100
+ const deltaAngle = 2 * Math.PI / numSegments
101
+
102
+ // Generate points for the ellipsoid
103
+ for (let i = 0; i < numSegments; i++) {
104
+ // Angle for this and the next segment
105
+ const angle = i * deltaAngle
106
+ const nextAngle = (i + 1) * deltaAngle
107
+
108
+ // Calculate x and y for the current and next points on the ellipse
109
+ const currX = x + halfWidth * Math.cos(angle)
110
+ const currY = y + halfHeight * Math.sin(angle)
111
+ const nextX = x + halfWidth * Math.cos(nextAngle)
112
+ const nextY = y + halfHeight * Math.sin(nextAngle)
113
+
114
+ line(cobalt, node, [ currX, currY ], [nextX, nextY ], color, lineWidth)
115
+ }
116
+ },
117
+
118
+ filledEllipse: function (cobalt, node, center, halfWidth, halfHeight, numSegments, color) {
119
+
120
+ const [ x, y ] = center
121
+
122
+ // angle between each segment
123
+ const deltaAngle = 2 * Math.PI / numSegments
124
+
125
+ // Generate points for the ellipsoid
126
+ for (let i = 0; i < numSegments; i++) {
127
+ // Angle for this and the next segment
128
+ const angle = i * deltaAngle
129
+ const nextAngle = (i + 1) * deltaAngle
130
+
131
+ // Calculate x and y for the current and next points on the ellipse
132
+ const currX = x + halfWidth * Math.cos(angle)
133
+ const currY = y + halfHeight * Math.sin(angle)
134
+ const nextX = x + halfWidth * Math.cos(nextAngle)
135
+ const nextY = y + halfHeight * Math.sin(nextAngle)
136
+
137
+ // Add vertices for the triangles (first point is always the center)
138
+ // First triangle vertex (center of ellipse)
139
+
140
+ const stride = 18 // 2 floats position + 4 floats color per vertex * 3 vertices
141
+ const vi = (node.data.vertexCount * 6) + (i * stride)
142
+
143
+ // position
144
+ node.data.vertices[vi + 0] = x
145
+ node.data.vertices[vi + 1] = y
146
+
147
+ // color
148
+ node.data.vertices[vi + 2] = color[0]
149
+ node.data.vertices[vi + 3] = color[1]
150
+ node.data.vertices[vi + 4] = color[2]
151
+ node.data.vertices[vi + 5] = color[3]
152
+
153
+
154
+ // Second triangle vertex (current point on ellipse)
155
+
156
+ // position
157
+ node.data.vertices[vi + 6] = currX
158
+ node.data.vertices[vi + 7] = currY
159
+
160
+ // color
161
+ node.data.vertices[vi + 8] = color[0]
162
+ node.data.vertices[vi + 9] = color[1]
163
+ node.data.vertices[vi + 10] = color[2]
164
+ node.data.vertices[vi + 11] = color[3]
165
+
166
+
167
+ // Third triangle vertex (next point on ellipse)
168
+ // position
169
+ node.data.vertices[vi + 12] = nextX
170
+ node.data.vertices[vi + 13] = nextY
171
+
172
+ // color
173
+ node.data.vertices[vi + 14] = color[0]
174
+ node.data.vertices[vi + 15] = color[1]
175
+ node.data.vertices[vi + 16] = color[2]
176
+ node.data.vertices[vi + 17] = color[3]
177
+ }
178
+
179
+ node.data.vertexCount += (3 * numSegments)
180
+
181
+ node.data.dirty = true
182
+ },
183
+
184
+ // @param Number angle rotation (radians)
185
+ box: function (cobalt, node, center, width, height, color, angle=0, lineWidth=1) {
186
+ const [ x, y ] = center
187
+
188
+ const halfWidth = width / 2
189
+ const halfHeight = height / 2
190
+
191
+ const topLeft = [ x - halfWidth, y - halfHeight ]
192
+ const topRight = [ x + halfWidth, y - halfHeight ]
193
+ const bottomLeft = [ x - halfWidth, y + halfHeight ]
194
+ const bottomRight = [ x + halfWidth, y + halfHeight ]
195
+
196
+ if (angle !== 0) {
197
+ // rotate the point by <angle> rads around origin
198
+ // point origin rads out
199
+ _rotate(topLeft, center, angle, topLeft)
200
+ _rotate(topRight, center, angle, topRight)
201
+ _rotate(bottomLeft, center, angle, bottomLeft)
202
+ _rotate(bottomRight, center, angle, bottomRight)
203
+ }
204
+
205
+ line(cobalt, node, topLeft, topRight, color, lineWidth)
206
+ line(cobalt, node, bottomLeft, bottomRight, color, lineWidth)
207
+ line(cobalt, node, topLeft, bottomLeft, color, lineWidth)
208
+ line(cobalt, node, topRight, bottomRight, color, lineWidth)
209
+ },
210
+
211
+
212
+
213
+ // @param Number angle rotation (radians)
214
+ filledBox: function (cobalt, node, center, width, height, color, angle=0) {
215
+ const [ x, y ] = center
216
+
217
+ const halfWidth = width / 2
218
+ const halfHeight = height / 2
219
+
220
+ const topLeft = [ x - halfWidth, y - halfHeight ];
221
+ const topRight = [ x + halfWidth, y - halfHeight ];
222
+ const bottomLeft = [ x - halfWidth, y + halfHeight ];
223
+ const bottomRight = [ x + halfWidth, y + halfHeight ];
224
+
225
+ if (angle !== 0) {
226
+ // rotate the point by <angle> rads around origin
227
+ // point origin rads out
228
+ _rotate(topLeft, center, angle, topLeft)
229
+ _rotate(topRight, center, angle, topRight)
230
+ _rotate(bottomLeft, center, angle, bottomLeft)
231
+ _rotate(bottomRight, center, angle, bottomRight)
232
+ }
233
+
234
+ let i = node.data.vertexCount * 6 // 2 floats position + 4 floats color per vertex
235
+
236
+
237
+ // triangle 1
238
+ // pt 1
239
+ node.data.vertices[i + 0] = topLeft[0]
240
+ node.data.vertices[i + 1] = topLeft[1]
241
+
242
+ // pt1 color
243
+ node.data.vertices[i + 2] = color[0]
244
+ node.data.vertices[i + 3] = color[1]
245
+ node.data.vertices[i + 4] = color[2]
246
+ node.data.vertices[i + 5] = color[3]
247
+
248
+ // pt 2
249
+ node.data.vertices[i + 6] = bottomLeft[0]
250
+ node.data.vertices[i + 7] = bottomLeft[1]
251
+
252
+ // pt2 color
253
+ node.data.vertices[i + 8] = color[0]
254
+ node.data.vertices[i + 9] = color[1]
255
+ node.data.vertices[i + 10] = color[2]
256
+ node.data.vertices[i + 11] = color[3]
257
+
258
+ // pt 3
259
+ node.data.vertices[i + 12] = topRight[0]
260
+ node.data.vertices[i + 13] = topRight[1]
261
+
262
+ // pt3 color
263
+ node.data.vertices[i + 14] = color[0]
264
+ node.data.vertices[i + 15] = color[1]
265
+ node.data.vertices[i + 16] = color[2]
266
+ node.data.vertices[i + 17] = color[3]
267
+
268
+
269
+ // triangle 2
270
+ // pt 2
271
+ node.data.vertices[i + 18] = bottomLeft[0]
272
+ node.data.vertices[i + 19] = bottomLeft[1]
273
+
274
+ // pt2 color
275
+ node.data.vertices[i + 20] = color[0]
276
+ node.data.vertices[i + 21] = color[1]
277
+ node.data.vertices[i + 22] = color[2]
278
+ node.data.vertices[i + 23] = color[3]
279
+
280
+ // pt 3
281
+ node.data.vertices[i + 24] = bottomRight[0]
282
+ node.data.vertices[i + 25] = bottomRight[1]
283
+
284
+ // pt3 color
285
+ node.data.vertices[i + 26] = color[0]
286
+ node.data.vertices[i + 27] = color[1]
287
+ node.data.vertices[i + 28] = color[2]
288
+ node.data.vertices[i + 29] = color[3]
289
+
290
+ // pt 4
291
+ node.data.vertices[i + 30] = topRight[0]
292
+ node.data.vertices[i + 31] = topRight[1]
293
+
294
+ // pt4 color
295
+ node.data.vertices[i + 32] = color[0]
296
+ node.data.vertices[i + 33] = color[1]
297
+ node.data.vertices[i + 34] = color[2]
298
+ node.data.vertices[i + 35] = color[3]
299
+
300
+
301
+ node.data.vertexCount += 6 // 2 triangles in a box, baby
302
+
303
+ node.data.dirty = true
304
+ },
305
+
306
+ clear: function (cobalt, node) {
307
+ node.data.vertexCount = 0
308
+ node.data.dirty = true
309
+ },
310
+ }
311
+
312
+
313
+ function _rotate (a, b, rad, out) {
314
+ //Translate point to the origin
315
+ let p0 = a[0] - b[0],
316
+ p1 = a[1] - b[1],
317
+ sinC = Math.sin(rad),
318
+ cosC = Math.cos(rad);
319
+
320
+ //perform rotation and translate to correct position
321
+ out[0] = p0 * cosC - p1 * sinC + b[0];
322
+ out[1] = p0 * sinC + p1 * cosC + b[1];
323
+ return out;
324
+ }
325
+
@@ -0,0 +1,168 @@
1
+ import * as Cobalt from '../cobalt.js'
2
+ import sceneCompositeWGSL from './scene-composite.wgsl'
3
+
4
+
5
+ export default {
6
+ type: 'cobalt:bloom',
7
+ refs: [
8
+ { name: 'hdr', type: 'textureView', format: 'rgba16', access: 'read' },
9
+ { name: 'bloom', type: 'textureView', format: 'rgba16', access: 'read' },
10
+ { name: 'combined', type: 'textureView', format: 'rgba8unorm', access: 'write' },
11
+ ],
12
+ // @params Object cobalt renderer world object
13
+ // @params Object options optional data passed when initing this node
14
+ onInit: async function (cobalt, options={}) {
15
+ return init(cobalt, options)
16
+ },
17
+
18
+ onRun: function (cobalt, node, webGpuCommandEncoder) {
19
+ // do whatever you need for this node. webgpu renderpasses, etc.
20
+ draw(cobalt, node, webGpuCommandEncoder)
21
+ },
22
+
23
+ onDestroy: function (cobalt, node) {
24
+ // any cleanup for your node should go here (releasing textures, etc.)
25
+ //destroy(node)
26
+ },
27
+
28
+ onResize: function (cobalt, node) {
29
+ // do whatever you need when the dimensions of the renderer change (resize textures, etc.)
30
+ resize(cobalt, node)
31
+ },
32
+
33
+ onViewportPosition: function (cobalt, node) { },
34
+ }
35
+
36
+
37
+ function init (cobalt, node) {
38
+ const { options, refs } = node
39
+
40
+ const { device } = cobalt
41
+ const format = Cobalt.getPreferredFormat(cobalt) // bgra8unorm
42
+
43
+ const bloom_intensity = options.bloom_intensity ?? 40.0
44
+ const bloom_combine_constant = options.bloom_combine_constant ?? 0.68
45
+ const dat = new Float32Array([ bloom_intensity, bloom_combine_constant ])
46
+ const params_buf = device.createBuffer({
47
+ label: 'scene composite params buffer',
48
+ size: dat.byteLength, // vec4<f32> and f32 and u32 with 4 bytes per float32 and 4 bytes per u32
49
+ mappedAtCreation: true,
50
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
51
+ })
52
+
53
+ new Float32Array(params_buf.getMappedRange()).set(dat)
54
+
55
+ params_buf.unmap()
56
+
57
+ const pipeline = device.createRenderPipeline({
58
+ layout: 'auto',
59
+ vertex: {
60
+ module: device.createShaderModule({
61
+ code: sceneCompositeWGSL,
62
+ }),
63
+ entryPoint: 'vert_main',
64
+ },
65
+ fragment: {
66
+ module: device.createShaderModule({
67
+ code: sceneCompositeWGSL,
68
+ }),
69
+ entryPoint: 'frag_main',
70
+ targets: [
71
+ {
72
+ format,
73
+ },
74
+ ],
75
+ },
76
+ primitive: {
77
+ topology: 'triangle-list',
78
+ },
79
+ })
80
+
81
+ const bindGroup = device.createBindGroup({
82
+ layout: pipeline.getBindGroupLayout(0),
83
+ entries: [
84
+ {
85
+ binding: 0,
86
+ resource: refs.hdr.data.sampler,
87
+ },
88
+ // color
89
+ {
90
+ binding: 1,
91
+ resource: refs.hdr.data.view,
92
+ },
93
+ // emissive
94
+ {
95
+ binding: 2,
96
+ resource: refs.bloom.data.mip_view[0],
97
+ },
98
+ {
99
+ binding: 3,
100
+ resource: {
101
+ buffer: params_buf,
102
+ },
103
+ },
104
+ ],
105
+ })
106
+
107
+ return {
108
+ bindGroup,
109
+ pipeline,
110
+ params_buf,
111
+ }
112
+ }
113
+
114
+
115
+ // combine bloom and color textures and draw to a fullscreen quad
116
+ function draw (cobalt, node, commandEncoder) {
117
+
118
+ const passEncoder = commandEncoder.beginRenderPass({
119
+ colorAttachments: [
120
+ {
121
+ view: node.refs.combined.data.view, //getCurrentTextureView(cobalt)
122
+ clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
123
+ loadOp: 'clear',
124
+ storeOp: 'store',
125
+ },
126
+ ],
127
+ })
128
+
129
+ const { pipeline, bindGroup } = node.data
130
+
131
+ passEncoder.setPipeline(pipeline)
132
+ passEncoder.setBindGroup(0, bindGroup)
133
+ passEncoder.draw(3)
134
+ passEncoder.end()
135
+ }
136
+
137
+
138
+ function resize (cobalt, node) {
139
+ const { pipeline, params_buf } = node.data
140
+ const { device } = cobalt
141
+
142
+ node.data.bindGroup = device.createBindGroup({
143
+ layout: pipeline.getBindGroupLayout(0),
144
+ entries: [
145
+ {
146
+ binding: 0,
147
+ resource: node.refs.hdr.data.sampler,
148
+ },
149
+ // color
150
+ {
151
+ binding: 1,
152
+ resource: node.refs.hdr.data.view,
153
+ },
154
+ // emissive
155
+ {
156
+ binding: 2,
157
+ resource: node.refs.bloom.data.mip_view[0], //bloom_mat.bind_groups_textures[2].mip_view[0],
158
+ },
159
+ {
160
+ binding: 3,
161
+ resource: {
162
+ buffer: params_buf,
163
+ },
164
+ },
165
+ ],
166
+ })
167
+ }
168
+
@@ -0,0 +1,94 @@
1
+
2
+ struct BloomComposite {
3
+ bloom_intensity: f32,
4
+ bloom_combine_constant: f32,
5
+ }
6
+
7
+ @group(0) @binding(0) var mySampler : sampler;
8
+ @group(0) @binding(1) var colorTexture : texture_2d<f32>;
9
+ @group(0) @binding(2) var emissiveTexture : texture_2d<f32>;
10
+ @group(0) @binding(3) var<uniform> composite_parameter: BloomComposite;
11
+
12
+ struct VertexOutput {
13
+ @builtin(position) Position : vec4<f32>,
14
+ @location(0) fragUV : vec2<f32>,
15
+ }
16
+
17
+ // fullscreen triangle position and uvs
18
+ const positions = array<vec2<f32>, 3>(
19
+ vec2<f32>(-1.0, -3.0),
20
+ vec2<f32>(3.0, 1.0),
21
+ vec2<f32>(-1.0, 1.0)
22
+ );
23
+
24
+ const uvs = array<vec2<f32>, 3>(
25
+ vec2<f32>(0.0, 2.0),
26
+ vec2<f32>(2.0, 0.0),
27
+ vec2<f32>(0.0, 0.0)
28
+ );
29
+
30
+
31
+ @vertex
32
+ fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
33
+
34
+ var output : VertexOutput;
35
+ output.Position = vec4<f32>(positions[VertexIndex], 0.0, 1.0);
36
+ output.fragUV = vec2<f32>(uvs[VertexIndex]);
37
+
38
+ return output;
39
+ }
40
+
41
+
42
+ // can be optimized into lut (compute can gen it)
43
+ fn GTTonemap_point(x: f32) -> f32{
44
+ let m: f32 = 0.22; // linear section start
45
+ let a: f32 = 1.0; // contrast
46
+ let c: f32 = 1.33; // black brightness
47
+ let P: f32 = 1.0; // maximum brightness
48
+ let l: f32 = 0.4; // linear section length
49
+ let l0: f32 = ((P-m)*l) / a; // 0.312
50
+ let S0: f32 = m + l0; // 0.532
51
+ let S1: f32 = m + a * l0; // 0.532
52
+ let C2: f32 = (a*P) / (P - S1); // 2.13675213675
53
+ let L: f32 = m + a * (x - m);
54
+ let T: f32 = m * pow(x/m, c);
55
+ let S: f32 = P - (P - S1) * exp(-C2*(x - S0)/P);
56
+ let w0: f32 = 1.0 - smoothstep(0.0, m, x);
57
+ var w2: f32 = 1.0;
58
+ if (x < m+l) {
59
+ w2 = 0.0;
60
+ }
61
+ let w1: f32 = 1.0 - w0 - w2;
62
+ return f32(T * w0 + L * w1 + S * w2);
63
+ }
64
+
65
+ // this costs about 0.2-0.3ms more than aces, as-is
66
+ fn GTTonemap(x: vec3<f32>) -> vec3<f32>{
67
+ return vec3<f32>(GTTonemap_point(x.r), GTTonemap_point(x.g), GTTonemap_point(x.b));
68
+ }
69
+
70
+
71
+ fn aces(x: vec3<f32>) -> vec3<f32> {
72
+ let a: f32 = 2.51;
73
+ let b: f32 = 0.03;
74
+ let c: f32 = 2.43;
75
+ let d: f32 = 0.59;
76
+ let e: f32 = 0.14;
77
+ return clamp((x * (a * x + b)) / (x * (c * x + d) + e), vec3<f32>(0.0), vec3<f32>(1.0));
78
+ }
79
+
80
+
81
+ @fragment
82
+ fn frag_main(@location(0) fragUV : vec2<f32>) -> @location(0) vec4<f32> {
83
+
84
+ let hdr_color = textureSample(colorTexture, mySampler, fragUV);
85
+ let bloom_color = textureSample(emissiveTexture, mySampler, fragUV);
86
+
87
+ let combined_color = ((bloom_color * composite_parameter.bloom_intensity) * composite_parameter.bloom_combine_constant);
88
+
89
+ let mapped_color = GTTonemap(combined_color.rgb);
90
+ //let mapped_color = aces(combined_color.rgb);
91
+ let gamma_corrected_color = pow(mapped_color, vec3<f32>(1.0 / 2.2));
92
+
93
+ return vec4<f32>(gamma_corrected_color + hdr_color.rgb, 1.0);
94
+ }
@@ -0,0 +1 @@
1
+ export const FLOAT32S_PER_SPRITE = 12 // vec2(translate) + vec2(scale) + vec4(tint) + opacity + rotation + emissiveIntensity + sortValue
@@ -0,0 +1,60 @@
1
+ export default function createSpriteQuads (device, spritesheet) {
2
+
3
+ // u,v coordinates specify top left as 0,0 bottom right as 1,1
4
+
5
+ /*
6
+ const vertices = new Float32Array([
7
+ // position tex
8
+ // x y z u v
9
+ -0.5, -0.5, 0.0, 0.0, 0.0,
10
+ -0.5, 0.5, 0.0, 0.0, 1.0,
11
+ 0.5, 0.5, 0.0, 1.0, 1.0,
12
+
13
+ // triangle 2 (2nd half of quad)
14
+ -0.5, -0.5, 0.0, 0.0, 0.0,
15
+ 0.5, 0.5, 0.0, 1.0, 1.0,
16
+ 0.5, -0.5, 0.0, 1.0, 0.0,
17
+ ])
18
+ */
19
+
20
+ const vertices = spritesheet.vertices
21
+
22
+ const usage = GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
23
+
24
+ const descriptor = {
25
+ size: vertices.byteLength,
26
+ usage,
27
+ // make this memory space accessible from the CPU (host visible)
28
+ mappedAtCreation: true
29
+ }
30
+
31
+ const buffer = device.createBuffer(descriptor)
32
+
33
+ new Float32Array(buffer.getMappedRange()).set(vertices)
34
+
35
+ buffer.unmap()
36
+
37
+ const bufferLayout = {
38
+ arrayStride: 20,
39
+ stepMode: 'vertex',
40
+ attributes: [
41
+ // position
42
+ {
43
+ shaderLocation: 0,
44
+ format: 'float32x3',
45
+ offset: 0
46
+ },
47
+ // uv
48
+ {
49
+ shaderLocation: 1,
50
+ format: 'float32x2',
51
+ offset: 12
52
+ }
53
+ ]
54
+ }
55
+
56
+ return {
57
+ buffer,
58
+ bufferLayout,
59
+ }
60
+ }