p3nGu1nZz commited on
Commit
b7e38c4
1 Parent(s): 78c9607

shaders should be in their own files

Browse files
Files changed (2) hide show
  1. index.html +33 -43
  2. shaders.wgsl +32 -0
index.html CHANGED
@@ -34,9 +34,10 @@
34
  const adapter = await navigator.gpu?.requestAdapter();
35
  const device = await adapter?.requestDevice();
36
  if (!device) {
37
- fail('need a browser that supports WebGPU');
38
  return;
39
  }
 
40
  const canvas = document.querySelector('canvas');
41
  const context = canvas.getContext('webgpu');
42
  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
@@ -44,46 +45,23 @@
44
  device,
45
  format: presentationFormat,
46
  });
47
- const module = device.createShaderModule({
48
- label: 'our hardcoded textured quad shaders',
49
- code: `
50
- struct VSInput {
51
- @location(0) position: vec4f,
52
- @location(1) texcoord: vec2f,
53
- @location(2) color: vec4f,
54
- };
55
-
56
- struct VSOutput {
57
- @builtin(position) position: vec4f,
58
- @location(0) texcoord: vec2f,
59
- @location(1) color: vec4f,
60
- };
61
-
62
- struct Uniforms {
63
- matrix: mat4x4f,
64
- };
65
 
66
- @group(0) @binding(2) var<uniform> uni: Uniforms;
67
-
68
- @vertex fn vs(vin: VSInput) -> VSOutput {
69
- var vsOutput: VSOutput;
70
- vsOutput.position = uni.matrix * vin.position;
71
- vsOutput.texcoord = vin.texcoord;
72
- vsOutput.color = vin.color;
73
- return vsOutput;
74
- }
75
-
76
- @group(0) @binding(0) var ourSampler: sampler;
77
- @group(0) @binding(1) var ourTexture: texture_2d<f32>;
78
 
79
- @fragment fn fs(fsInput: VSOutput) -> @location(0) vec4f {
80
- return textureSample(ourTexture, ourSampler, fsInput.texcoord) * fsInput.color;
81
- }
82
- `,
83
  });
 
84
  const glyphCanvas = genreateGlyphTextureAtlas();
85
  document.body.appendChild(glyphCanvas);
86
  glyphCanvas.style.backgroundColor = '#222';
 
87
  const maxGlyphs = 100;
88
  const floatsPerVertex = 2 + 2 + 4;
89
  const vertexSize = floatsPerVertex * 4;
@@ -99,24 +77,25 @@
99
  size: maxGlyphs * vertsPerGlyph * 4,
100
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
101
  });
102
- {
103
- const indices = [];
104
- for (let i = 0; i < maxGlyphs; ++i) {
105
- const ndx = i * 4;
106
- indices.push(ndx, ndx + 1, ndx + 2, ndx + 2, ndx + 1, ndx + 3);
107
- }
108
- device.queue.writeBuffer(indexBuffer, 0, new Uint32Array(indices));
109
- }
110
  function generateGlyphVerticesForText(s, colors = [[1, 1, 1, 1]]) {
111
  const vertexData = new Float32Array(maxGlyphs * floatsPerVertex * vertsPerGlyph);
112
  const glyphUVWidth = glyphWidth / glyphCanvas.width;
113
  const glyphUVHeight = glyphHeight / glyphCanvas.height;
114
  let offset = 0, x0 = 0, y0 = 0, x1 = 1, y1 = 1, width = 0;
115
  let colorNdx = 0;
 
116
  const addVertex = (x, y, u, v, color) => {
117
  vertexData.set([x, y, u, v, ...color], offset);
118
  offset += 8;
119
  };
 
120
  for (let i = 0; i < s.length; ++i) {
121
  const c = s.charCodeAt(i);
122
  if (c >= 33) {
@@ -144,6 +123,7 @@
144
  }
145
  return { vertexData, numGlyphs: offset / floatsPerVertex, width, height: y1 };
146
  }
 
147
  const colors = [
148
  [1, 1, 0, 1],
149
  [0, 1, 1, 1],
@@ -153,6 +133,7 @@
153
  ];
154
  const { vertexData, numGlyphs, width, height } = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', colors);
155
  device.queue.writeBuffer(vertexBuffer, 0, vertexData);
 
156
  const pipeline = device.createRenderPipeline({
157
  label: 'textured quad pipeline',
158
  layout: 'auto',
@@ -182,6 +163,7 @@
182
  }],
183
  },
184
  });
 
185
  function copySourceToTexture(device, texture, source, { flipY } = {}) {
186
  device.queue.copyExternalImageToTexture(
187
  { source, flipY },
@@ -189,6 +171,7 @@
189
  { width: source.width, height: source.height }
190
  );
191
  }
 
192
  function createTextureFromSource(device, source, options = {}) {
193
  const texture = device.createTexture({
194
  format: 'rgba8unorm',
@@ -198,11 +181,13 @@
198
  copySourceToTexture(device, texture, source, options);
199
  return texture;
200
  }
 
201
  const texture = createTextureFromSource(device, glyphCanvas, { mips: true });
202
  const sampler = device.createSampler({
203
  minFilter: 'linear',
204
  magFilter: 'linear'
205
  });
 
206
  const uniformBufferSize = 16 * 4; //f32
207
  const uniformBuffer = device.createBuffer({
208
  label: 'uniforms for quad',
@@ -211,6 +196,7 @@
211
  });
212
  const uniformValues = new Float32Array(uniformBufferSize / 4);
213
  const matrix = uniformValues.subarray(0, 16);
 
214
  const bindGroup = device.createBindGroup({
215
  layout: pipeline.getBindGroupLayout(0),
216
  entries: [
@@ -219,6 +205,7 @@
219
  { binding: 2, resource: { buffer: uniformBuffer } },
220
  ],
221
  });
 
222
  const renderPassDescriptor = {
223
  label: 'canvas render pass',
224
  colorAttachments: [{
@@ -227,6 +214,7 @@
227
  storeOp: 'store',
228
  }],
229
  };
 
230
  function render(time) {
231
  time *= 0.001;
232
  const fov = 60 * Math.PI / 180;
@@ -235,6 +223,7 @@
235
  const projectionMatrix = mat4.perspective(fov, aspect, zNear, zFar);
236
  const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
237
  const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
 
238
  renderPassDescriptor.colorAttachments[0].view = context.getCurrentTexture().createView();
239
  const encoder = device.createCommandEncoder();
240
  const pass = encoder.beginRenderPass(renderPassDescriptor);
@@ -250,6 +239,7 @@
250
  device.queue.submit([encoder.finish()]);
251
  requestAnimationFrame(render);
252
  }
 
253
  requestAnimationFrame(render);
254
  }
255
  function fail(msg) {
 
34
  const adapter = await navigator.gpu?.requestAdapter();
35
  const device = await adapter?.requestDevice();
36
  if (!device) {
37
+ alert('need a browser that supports WebGPU');
38
  return;
39
  }
40
+
41
  const canvas = document.querySelector('canvas');
42
  const context = canvas.getContext('webgpu');
43
  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
 
45
  device,
46
  format: presentationFormat,
47
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ // Fetch shader code from external file
50
+ async function fetchShaderCode(url) {
51
+ const response = await fetch(url);
52
+ return await response.text();
53
+ }
 
 
 
 
 
 
 
54
 
55
+ const shaderCode = await fetchShaderCode('shaders.wgsl');
56
+ const module = device.createShaderModule({
57
+ label: 'textured quad shaders',
58
+ code: shaderCode,
59
  });
60
+
61
  const glyphCanvas = genreateGlyphTextureAtlas();
62
  document.body.appendChild(glyphCanvas);
63
  glyphCanvas.style.backgroundColor = '#222';
64
+
65
  const maxGlyphs = 100;
66
  const floatsPerVertex = 2 + 2 + 4;
67
  const vertexSize = floatsPerVertex * 4;
 
77
  size: maxGlyphs * vertsPerGlyph * 4,
78
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
79
  });
80
+
81
+ const indices = Array.from({ length: maxGlyphs * 6 }, (_, i) => {
82
+ const ndx = Math.floor(i / 6) * 4;
83
+ return (i % 6 < 3 ? [ndx, ndx + 1, ndx + 2] : [ndx + 2, ndx + 1, ndx + 3])[i % 3];
84
+ });
85
+ device.queue.writeBuffer(indexBuffer, 0, new Uint32Array(indices));
86
+
 
87
  function generateGlyphVerticesForText(s, colors = [[1, 1, 1, 1]]) {
88
  const vertexData = new Float32Array(maxGlyphs * floatsPerVertex * vertsPerGlyph);
89
  const glyphUVWidth = glyphWidth / glyphCanvas.width;
90
  const glyphUVHeight = glyphHeight / glyphCanvas.height;
91
  let offset = 0, x0 = 0, y0 = 0, x1 = 1, y1 = 1, width = 0;
92
  let colorNdx = 0;
93
+
94
  const addVertex = (x, y, u, v, color) => {
95
  vertexData.set([x, y, u, v, ...color], offset);
96
  offset += 8;
97
  };
98
+
99
  for (let i = 0; i < s.length; ++i) {
100
  const c = s.charCodeAt(i);
101
  if (c >= 33) {
 
123
  }
124
  return { vertexData, numGlyphs: offset / floatsPerVertex, width, height: y1 };
125
  }
126
+
127
  const colors = [
128
  [1, 1, 0, 1],
129
  [0, 1, 1, 1],
 
133
  ];
134
  const { vertexData, numGlyphs, width, height } = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', colors);
135
  device.queue.writeBuffer(vertexBuffer, 0, vertexData);
136
+
137
  const pipeline = device.createRenderPipeline({
138
  label: 'textured quad pipeline',
139
  layout: 'auto',
 
163
  }],
164
  },
165
  });
166
+
167
  function copySourceToTexture(device, texture, source, { flipY } = {}) {
168
  device.queue.copyExternalImageToTexture(
169
  { source, flipY },
 
171
  { width: source.width, height: source.height }
172
  );
173
  }
174
+
175
  function createTextureFromSource(device, source, options = {}) {
176
  const texture = device.createTexture({
177
  format: 'rgba8unorm',
 
181
  copySourceToTexture(device, texture, source, options);
182
  return texture;
183
  }
184
+
185
  const texture = createTextureFromSource(device, glyphCanvas, { mips: true });
186
  const sampler = device.createSampler({
187
  minFilter: 'linear',
188
  magFilter: 'linear'
189
  });
190
+
191
  const uniformBufferSize = 16 * 4; //f32
192
  const uniformBuffer = device.createBuffer({
193
  label: 'uniforms for quad',
 
196
  });
197
  const uniformValues = new Float32Array(uniformBufferSize / 4);
198
  const matrix = uniformValues.subarray(0, 16);
199
+
200
  const bindGroup = device.createBindGroup({
201
  layout: pipeline.getBindGroupLayout(0),
202
  entries: [
 
205
  { binding: 2, resource: { buffer: uniformBuffer } },
206
  ],
207
  });
208
+
209
  const renderPassDescriptor = {
210
  label: 'canvas render pass',
211
  colorAttachments: [{
 
214
  storeOp: 'store',
215
  }],
216
  };
217
+
218
  function render(time) {
219
  time *= 0.001;
220
  const fov = 60 * Math.PI / 180;
 
223
  const projectionMatrix = mat4.perspective(fov, aspect, zNear, zFar);
224
  const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
225
  const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
226
+
227
  renderPassDescriptor.colorAttachments[0].view = context.getCurrentTexture().createView();
228
  const encoder = device.createCommandEncoder();
229
  const pass = encoder.beginRenderPass(renderPassDescriptor);
 
239
  device.queue.submit([encoder.finish()]);
240
  requestAnimationFrame(render);
241
  }
242
+
243
  requestAnimationFrame(render);
244
  }
245
  function fail(msg) {
shaders.wgsl ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ struct VSInput {
2
+ @location(0) position: vec4f,
3
+ @location(1) texcoord: vec2f,
4
+ @location(2) color: vec4f,
5
+ };
6
+
7
+ struct VSOutput {
8
+ @builtin(position) position: vec4f,
9
+ @location(0) texcoord: vec2f,
10
+ @location(1) color: vec4f,
11
+ };
12
+
13
+ struct Uniforms {
14
+ matrix: mat4x4f,
15
+ };
16
+
17
+ @group(0) @binding(2) var<uniform> uni: Uniforms;
18
+
19
+ @vertex fn vs(vin: VSInput) -> VSOutput {
20
+ var vsOutput: VSOutput;
21
+ vsOutput.position = uni.matrix * vin.position;
22
+ vsOutput.texcoord = vin.texcoord;
23
+ vsOutput.color = vin.color;
24
+ return vsOutput;
25
+ }
26
+
27
+ @group(0) @binding(0) var ourSampler: sampler;
28
+ @group(0) @binding(1) var ourTexture: texture_2d<f32>;
29
+
30
+ @fragment fn fs(fsInput: VSOutput) -> @location(0) vec4f {
31
+ return textureSample(ourTexture, ourSampler, fsInput.texcoord) * fsInput.color;
32
+ }