Compare commits
2 Commits
4648ca10ad
...
14c01ea55a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14c01ea55a | ||
|
|
7d5265bced |
BIN
assets/UVEditorColorGrid.png
Normal file
BIN
assets/UVEditorColorGrid.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 MiB |
30
src/buffer-util.ts
Normal file
30
src/buffer-util.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
export class BufferUtil {
|
||||||
|
|
||||||
|
public static createVertexBuffer(device: GPUDevice, data: Float32Array ): GPUBuffer {
|
||||||
|
|
||||||
|
const buffer = device.createBuffer({
|
||||||
|
size: data.byteLength,
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
mappedAtCreation: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
new Float32Array(buffer.getMappedRange()).set(data);
|
||||||
|
buffer.unmap();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createIndexBuffer(device: GPUDevice, data: Uint16Array): GPUBuffer {
|
||||||
|
|
||||||
|
const buffer = device.createBuffer({
|
||||||
|
size: data.byteLength,
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.INDEX,
|
||||||
|
mappedAtCreation: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
new Uint16Array(buffer.getMappedRange()).set(data);
|
||||||
|
buffer.unmap();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/geometry.ts
Normal file
35
src/geometry.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
export class QuadGeometry {
|
||||||
|
public positions: number[];
|
||||||
|
public colors: number[];
|
||||||
|
public texCoords: number[];
|
||||||
|
public indices: number[];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.positions = [
|
||||||
|
-0.5, -0.5,
|
||||||
|
0.5, -0.5,
|
||||||
|
-0.5, 0.5,
|
||||||
|
0.5, 0.5,
|
||||||
|
]
|
||||||
|
|
||||||
|
this.colors = [
|
||||||
|
1.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0,
|
||||||
|
1.0, 1.0, 1.0,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
this.texCoords = [
|
||||||
|
0.0, 1.0,
|
||||||
|
1.0, 1.0,
|
||||||
|
0.0, 0.0,
|
||||||
|
1.0, 0.0,
|
||||||
|
]
|
||||||
|
|
||||||
|
this.indices = [
|
||||||
|
0, 1, 2,
|
||||||
|
1, 2, 3,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
127
src/main.ts
127
src/main.ts
@@ -1,11 +1,18 @@
|
|||||||
import shaderSource from './shaders/shader.wgsl?raw';
|
import shaderSource from './shaders/shader.wgsl?raw';
|
||||||
|
import { QuadGeometry } from './geometry';
|
||||||
|
import { Texture } from './texture';
|
||||||
|
import { BufferUtil } from './buffer-util';
|
||||||
class Renderer{
|
class Renderer{
|
||||||
private device! : GPUDevice;
|
private device! : GPUDevice;
|
||||||
private context! : GPUCanvasContext;
|
private context! : GPUCanvasContext;
|
||||||
private pipeline! : GPURenderPipeline;
|
private pipeline! : GPURenderPipeline;
|
||||||
private postitionBuffer! : GPUBuffer;
|
private postitionBuffer! : GPUBuffer;
|
||||||
|
private indexBuffer! : GPUBuffer;
|
||||||
private colorBuffer! : GPUBuffer;
|
private colorBuffer! : GPUBuffer;
|
||||||
|
private texCoordBuffer! : GPUBuffer;
|
||||||
|
private textureBindGroup! : GPUBindGroup;
|
||||||
|
|
||||||
|
private testTexture! : Texture;
|
||||||
|
|
||||||
public async initialize()
|
public async initialize()
|
||||||
{
|
{
|
||||||
@@ -37,42 +44,20 @@ class Renderer{
|
|||||||
format: navigator.gpu.getPreferredCanvasFormat()
|
format: navigator.gpu.getPreferredCanvasFormat()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.testTexture = await Texture.createTextureFromUrl(this.device, 'assets/UVEditorColorGrid.png');
|
||||||
this.prepareModel();
|
this.prepareModel();
|
||||||
|
|
||||||
this.postitionBuffer = this.CreateBuffer(new Float32Array([
|
|
||||||
-0.5, -0.5,
|
|
||||||
0.5, -0.5,
|
|
||||||
-0.5, 0.5,
|
|
||||||
-0.5, 0.5,
|
|
||||||
0.5, 0.5,
|
|
||||||
0.5, -0.5
|
|
||||||
]));
|
|
||||||
|
|
||||||
this.colorBuffer = this.CreateBuffer(new Float32Array([
|
|
||||||
1.0, 0.0, 0.0,
|
const geometry = new QuadGeometry();
|
||||||
0.0, 1.0, 0.0,
|
|
||||||
0.0, 0.0, 1.0,
|
this.postitionBuffer = BufferUtil.createVertexBuffer(this.device,new Float32Array(geometry.positions));
|
||||||
1.0, 0.0, 0.0,
|
this.colorBuffer = BufferUtil.createVertexBuffer(this.device, new Float32Array(geometry.colors));
|
||||||
0.0, 1.0, 0.0,
|
this.texCoordBuffer = BufferUtil.createVertexBuffer(this.device,new Float32Array(geometry.texCoords));
|
||||||
0.0, 0.0, 1.0
|
this.indexBuffer = BufferUtil.createIndexBuffer(this.device, new Uint16Array(geometry.indices));
|
||||||
]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CreateBuffer(data: Float32Array)
|
|
||||||
{
|
|
||||||
const buffer = this.device.createBuffer({
|
|
||||||
size: data.byteLength,
|
|
||||||
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
||||||
mappedAtCreation: true
|
|
||||||
});
|
|
||||||
|
|
||||||
new Float32Array(buffer.getMappedRange()).set(data);
|
|
||||||
buffer.unmap();
|
|
||||||
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private prepareModel() {
|
private prepareModel() {
|
||||||
const shaderModule = this.device.createShaderModule({code: shaderSource});
|
const shaderModule = this.device.createShaderModule({code: shaderSource});
|
||||||
@@ -102,12 +87,27 @@ class Renderer{
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const textureCoordBufferLayout : GPUVertexBufferLayout =
|
||||||
|
{
|
||||||
|
arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT,
|
||||||
|
attributes: [
|
||||||
|
{
|
||||||
|
shaderLocation: 2,
|
||||||
|
offset: 0,
|
||||||
|
format: 'float32x2'
|
||||||
|
}],
|
||||||
|
stepMode: 'vertex'
|
||||||
|
}
|
||||||
|
|
||||||
const vertexState: GPUVertexState = {
|
const vertexState: GPUVertexState = {
|
||||||
module : shaderModule,
|
module : shaderModule,
|
||||||
entryPoint: "VertexMain",
|
entryPoint: "VertexMain",
|
||||||
buffers:
|
buffers:
|
||||||
[postitionBufferLayout,
|
[
|
||||||
colorBufferLayout]
|
postitionBufferLayout,
|
||||||
|
colorBufferLayout,
|
||||||
|
textureCoordBufferLayout
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const fragmentState: GPUFragmentState = {
|
const fragmentState: GPUFragmentState = {
|
||||||
@@ -115,18 +115,65 @@ class Renderer{
|
|||||||
entryPoint: "FragmentMain",
|
entryPoint: "FragmentMain",
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
format: navigator.gpu.getPreferredCanvasFormat()
|
format: navigator.gpu.getPreferredCanvasFormat(),
|
||||||
|
blend: {
|
||||||
|
color: {
|
||||||
|
srcFactor: 'one',
|
||||||
|
dstFactor: 'one-minus-src-alpha',
|
||||||
|
operation: 'add'
|
||||||
|
},
|
||||||
|
alpha: {
|
||||||
|
srcFactor: 'one',
|
||||||
|
dstFactor: 'one-minus-src-alpha',
|
||||||
|
operation: 'add'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const textureBindGroupLayout = this.device.createBindGroupLayout({
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
binding: 0,
|
||||||
|
visibility: GPUShaderStage.FRAGMENT,
|
||||||
|
sampler: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
binding: 1,
|
||||||
|
visibility: GPUShaderStage.FRAGMENT,
|
||||||
|
texture: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const pipelineLayout = this.device.createPipelineLayout({
|
||||||
|
bindGroupLayouts: [
|
||||||
|
textureBindGroupLayout
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.textureBindGroup = this.device.createBindGroup({
|
||||||
|
layout: textureBindGroupLayout,
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
binding: 0,
|
||||||
|
resource: this.testTexture.sampler
|
||||||
|
},
|
||||||
|
{
|
||||||
|
binding: 1,
|
||||||
|
resource: this.testTexture.texture.createView()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
this.pipeline = this.device.createRenderPipeline({
|
this.pipeline = this.device.createRenderPipeline({
|
||||||
vertex: vertexState,
|
vertex: vertexState,
|
||||||
fragment: fragmentState,
|
fragment: fragmentState,
|
||||||
primitive:{
|
primitive:{
|
||||||
topology: 'triangle-list',
|
topology: 'triangle-list',
|
||||||
},
|
},
|
||||||
layout: "auto"
|
layout: pipelineLayout
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -152,12 +199,16 @@ class Renderer{
|
|||||||
|
|
||||||
//Draw here
|
//Draw here
|
||||||
passEncoder.setPipeline(this.pipeline );
|
passEncoder.setPipeline(this.pipeline );
|
||||||
|
passEncoder.setIndexBuffer(this.indexBuffer, 'uint16');
|
||||||
passEncoder.setVertexBuffer(0, this.postitionBuffer);
|
passEncoder.setVertexBuffer(0, this.postitionBuffer);
|
||||||
passEncoder.setVertexBuffer(1, this.colorBuffer);
|
passEncoder.setVertexBuffer(1, this.colorBuffer);
|
||||||
passEncoder.draw(6);
|
passEncoder.setVertexBuffer(2, this.texCoordBuffer);
|
||||||
|
passEncoder.setBindGroup(0, this.textureBindGroup)
|
||||||
|
passEncoder.drawIndexed(6, 1, 0, 0, 0);
|
||||||
|
|
||||||
passEncoder.end();
|
passEncoder.end();
|
||||||
|
|
||||||
|
|
||||||
this.device.queue.submit([commandEncoder.finish()]);
|
this.device.queue.submit([commandEncoder.finish()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,34 @@
|
|||||||
struct VertexOut {
|
struct VertexOut {
|
||||||
@builtin(position) pos: vec4<f32>,
|
@builtin(position) pos: vec4<f32>,
|
||||||
@location(0) color: vec4<f32>,
|
@location(0) color: vec4<f32>,
|
||||||
|
@location(1) texcoord: vec2<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn VertexMain(
|
fn VertexMain(
|
||||||
@location(0) pos: vec2<f32>,
|
@location(0) pos: vec2<f32>,
|
||||||
@location(1) color: vec3<f32>,
|
@location(1) color: vec3<f32>,
|
||||||
|
@location(2) texcoord: vec2<f32>,
|
||||||
@builtin(vertex_index) vertexIndex: u32,
|
@builtin(vertex_index) vertexIndex: u32,
|
||||||
) -> VertexOut
|
) -> VertexOut
|
||||||
{
|
{
|
||||||
var output : VertexOut;
|
var output : VertexOut;
|
||||||
output.pos = vec4<f32>(pos, 0.0, 1.0);
|
output.pos = vec4<f32>(pos, 0.0, 1.0);
|
||||||
output.color = vec4<f32>(color, 1.0);
|
output.color = vec4<f32>(color, 1.0);
|
||||||
|
output.texcoord = vec2<f32>(texcoord);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var texSampler : sampler;
|
||||||
|
|
||||||
|
@group(0) @binding(1)
|
||||||
|
var tex : texture_2d<f32>;
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn FragmentMain(fragData: VertexOut) -> @location(0) vec4<f32>
|
fn FragmentMain(fragData: VertexOut) -> @location(0) vec4<f32>
|
||||||
{
|
{
|
||||||
return fragData.color;
|
var texColor = textureSample(tex, texSampler, fragData.texcoord);
|
||||||
|
return fragData.color * texColor;
|
||||||
}
|
}
|
||||||
45
src/texture.ts
Normal file
45
src/texture.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
export class Texture{
|
||||||
|
constructor(public texture: GPUTexture, public sampler: GPUSampler){}
|
||||||
|
|
||||||
|
public static async createTexture(device: GPUDevice, image: HTMLImageElement)
|
||||||
|
: Promise<Texture>
|
||||||
|
{
|
||||||
|
const texture = device.createTexture({
|
||||||
|
size: { width: image.width, height: image.height},
|
||||||
|
format: "rgba8unorm",
|
||||||
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await createImageBitmap(image );
|
||||||
|
|
||||||
|
device.queue.copyExternalImageToTexture(
|
||||||
|
{ source: data },
|
||||||
|
{ texture: texture },
|
||||||
|
{ width: image.width, height: image.height }
|
||||||
|
);
|
||||||
|
|
||||||
|
const sampler = device.createSampler({
|
||||||
|
magFilter: "linear",
|
||||||
|
minFilter: "linear",
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Texture(texture, sampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async createTextureFromUrl(device: GPUDevice, url: string): Promise<Texture>
|
||||||
|
{
|
||||||
|
const imagePromise = new Promise<HTMLImageElement>((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
img.onload = () => resolve(img);
|
||||||
|
img.onerror = () => {
|
||||||
|
console.error('Failed to load image from url: ' + url);
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const image = await imagePromise;
|
||||||
|
return await Texture.createTexture(device, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"erasableSyntaxOnly": true,
|
"erasableSyntaxOnly": false,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUncheckedSideEffectImports": true
|
"noUncheckedSideEffectImports": true
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user