Open fuzhenn opened 4 years ago
Looks like someone already made a WebGPU version of REGL: https://github.com/stackgpu/Simple-GPU
You can pay attention to GEngine, the engine provides a Model class that can be used for basic rendering packaging, very flexible and easy to use
example:
<script type="module">
import { Model, Context, Texture, RenderTarget, Attachment, Sampler } from "../../dist/index.js";
import { mat4, vec3 } from "../lib/esm/index.js";
import { cubeVertexArray, cubeVertexCount, uvs, colors, positions } from "../asset/cude.js";
const init = async () => {
const context = new Context({
canvas: null,
container: document.getElementById("app"),
pixelRatio: 1
});
const { canvas, presentationFormat } = context;
await context.init();
const { width, height, depth } = context.presentationSize;
const colorAttachment = new Attachment(
{ r: 0.0, g: 0.0, b: 0.0, a: 0 },
{
textureView: () => {
return context.context.getCurrentTexture().createView();
}
}
);
const response = await fetch(new URL("../textures/Di-3d.png", import.meta.url).toString());
const imageBitmap = await createImageBitmap(await response.blob());
const depthTexture = new Texture({
label: "resolveDepth",
size: { width, height, depth },
format: "depth24plus",
usage: GPUTextureUsage.RENDER_ATTACHMENT
});
const texture = new Texture({
size: {
width: imageBitmap.width,
height: imageBitmap.height,
depth: 1
},
format: "rgba8unorm",
usage:
GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
data: {
source: imageBitmap,
width: imageBitmap.width,
height: imageBitmap.height
}
});
const sampler = new Sampler({
magFilter: "linear",
minFilter: "linear"
// addressModeU: "repeat",
// addressModeV: "repeat"
});
const depthAttachment = new Attachment(1.0, { texture: depthTexture });
const canvasRenderTarget = new RenderTarget("render", [colorAttachment], depthAttachment);
const aspect = canvas.width / canvas.height;
const projectionMatrix = mat4.perspective([], (2 * Math.PI) / 5, aspect, 1, 100.0);
const modelViewProjectionMatrix = mat4.create();
const model = new Model({
shaderId: "model",
frag: `
@group(0) @binding(2) var mySampler: sampler;
@group(0) @binding(1) var myTexture: texture_2d<f32>;
@fragment
fn main(
@location(0) fragUV: vec2<f32>,
@location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
return textureSample(myTexture, mySampler, fragUV) * fragPosition;
}
`,
vert: `
struct Uniforms {
modelViewProjectionMatrix : mat4x4<f32>,
}
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) fragPosition: vec4<f32>,
}
@vertex
fn main(
@location(0) position : vec4<f32>,
@location(1) color : vec4<f32>,
@location(2) uv : vec2<f32>
) -> VertexOutput {
var output : VertexOutput;
output.Position = uniforms.modelViewProjectionMatrix * position;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0));
return output;
}
`,
vertexBuffers: [
{
stepMode: "vertex",
uid: "vertAttr",
attributes: {
interleaved: {
names: ["position", "color", "uv"],
value: cubeVertexArray,
itemSizes: [4, 4, 2]
} //interleaved buffer
}
}
],
uniformBuffers: [
{
type: "uniform",
uid: "systemMatrix",
uniforms: {
modelViewProjectionMatrix: {
type: "mat4x4<f32>",
value: () => {
let viewMatrix = mat4.identity([]);
mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -4));
const now = Date.now() / 1000;
mat4.rotate(
viewMatrix,
viewMatrix,
1,
vec3.fromValues(Math.sin(now), Math.cos(now), 0)
);
mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
return modelViewProjectionMatrix;
}
}
}
}
],
uniformTextureAndSampler: {
myTexture: { type: "texture", value: texture },
mySampler: { type: "sampler", value: sampler }
},
renderState: {
targets: [
{
format: presentationFormat
}
],
primitive: {
topology: "triangle-list",
cullMode: "back"
},
depthStencil: {
depthWriteEnabled: true,
depthCompare: "less",
format: "depth24plus"
}
},
draw: {
count: 36,
instanceCount: 1
}
});
const model1 = new Model({
shaderId: "model",
frag: `
@group(0) @binding(2) var mySampler: sampler;
@group(0) @binding(1) var myTexture: texture_2d<f32>;
@fragment
fn main(
@location(0) fragUV: vec2<f32>,
@location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
return textureSample(myTexture, mySampler, fragUV) * fragPosition;
}
`,
vert: `
struct Uniforms {
modelViewProjectionMatrix : mat4x4<f32>,
}
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) fragPosition: vec4<f32>,
}
@vertex
fn main(
@location(0) position : vec4<f32>,
@location(1) color : vec4<f32>,
@location(2) uv : vec2<f32>
) -> VertexOutput {
var output : VertexOutput;
output.Position = uniforms.modelViewProjectionMatrix * position;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0));
return output;
}
`,
vertexBuffers: [
{
stepMode: "vertex",
uid: "vertAttr",
attributes: {
interleaved: {
names: ["position", "color", "uv"],
value: cubeVertexArray,
itemSizes: [4, 4, 2]
} //interleaved buffer
}
}
],
uniformBuffers: [
{
type: "uniform",
uid: "systemMatrix",
uniforms: {
modelViewProjectionMatrix: {
type: "mat4x4<f32>",
value: () => {
let viewMatrix = mat4.identity([]);
mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, -4, -8));
const now = Date.now() / 1000;
mat4.rotate(
viewMatrix,
viewMatrix,
1,
vec3.fromValues(Math.sin(now), Math.cos(now), 0)
);
mat4.multiply(modelViewProjectionMatrix, projectionMatrix, viewMatrix);
return modelViewProjectionMatrix;
}
}
}
}
],
uniformTextureAndSampler: {
myTexture: { type: "texture", value: texture },
mySampler: { type: "sampler", value: sampler }
},
renderState: {
targets: [
{
format: presentationFormat
}
],
primitive: {
topology: "triangle-list",
cullMode: "back"
},
depthStencil: {
depthWriteEnabled: true,
depthCompare: "less",
format: "depth24plus"
}
},
draw: {
count: 36,
instanceCount: 1
}
});
function animate() {
requestAnimationFrame(animate);
const passEncoder = canvasRenderTarget.beginRenderPass(context.device);
model.render({ device: context.device, passEncoder });
model1.render({ device: context.device, passEncoder });
canvasRenderTarget.endRenderPass();
}
animate();
};
init();
</script>
WebGPU is coming, although it's still in a early stage and needs years for wide supports, for sure now is the right time to do some investigations.
IMHO, with a simple and low level design, regl has the potential to migrate to WebGPU in a smooth way with few backward compatible API changes. And that would be a great news for all the regl users.
References:
378 Investigate WebGL 2.0 support