Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

132 multi draw #133

Merged
merged 10 commits into from
Feb 21, 2023
2 changes: 2 additions & 0 deletions examples/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"image",
"instancing",
"msaa",
"multi-draw",
"multi-draw-instanced",
"picking",
"primitives",
"project",
Expand Down
148 changes: 148 additions & 0 deletions examples/multi-draw-instanced.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
//Requires: WEBGL_multi_draw
//Requires: WEBGL_multi_draw_instanced_base_vertex_base_instance
import createContext from "../index.js";

import { perspective as createCamera, orbiter as createOrbiter } from "pex-cam";

import { cube, sphere, torus } from "primitive-geometry";
import typedArrayConcat from "typed-array-concat";

import basicFrag from "./shaders/basic.frag.js";
// import merge from "geom-merge";

const ctx = createContext({ pixelRatio: devicePixelRatio });

const CellsConstructor = Uint16Array;

const sphereGeometry = sphere({ radius: 0.2 });
const cubeGeometry = cube({ sx: 0.3 });
const torusGeometry = torus({ radius: 0.2, minorRadius: 0.05 });
const geometries = [sphereGeometry, cubeGeometry, torusGeometry];

let geom = {
positions: typedArrayConcat(
Float32Array,
...geometries.map((g) => g.positions)
),
normals: typedArrayConcat(Float32Array, ...geometries.map((g) => g.normals)),
uvs: typedArrayConcat(Float32Array, ...geometries.map((g) => g.uvs)),
cells: typedArrayConcat(CellsConstructor, ...geometries.map((g) => g.cells)),
};
// geom = merge(geometries);

const camera = createCamera({
position: [0, 0, 7],
});
createOrbiter({ camera });

const clearCmd = {
pass: ctx.pass({
clearColor: [0.2, 0.2, 0.2, 1],
clearDepth: 1,
}),
};

const counts = new Int32Array(geometries.length);
const offsets = new Int32Array(geometries.length);
const baseVertices = new Int32Array(geometries.length);
const baseInstances = new Int32Array(geometries.length);
const instanceCounts = new Int32Array([3, 6, 9]);

const instancePositions = [];

for (let i = 0; i < geometries.length; i++) {
const numInstancesPerShape = instanceCounts[i];
for (let j = 0; j < numInstancesPerShape; j++) {
instancePositions.push([j - 4, i - (geometries.length - 1) / 2, 0]);
}

counts[i] = geometries[i].cells.length;

if (i > 0) {
offsets[i] =
offsets[i - 1] +
geometries[i - 1].cells.length * CellsConstructor.BYTES_PER_ELEMENT;

// baseVertices[i] = 0 //when using geom-merge
baseVertices[i] =
baseVertices[i - 1] + geometries[i - 1].positions.length / 3;

baseInstances[i] = baseInstances[i - 1] + instanceCounts[i - 1];
}
}

const drawCmd = {
pipeline: ctx.pipeline({
depthTest: true,
vert: /* glsl */ `
#extension GL_ANGLE_multi_draw: require

attribute vec3 aPosition;
attribute vec3 aOffset;

uniform mat4 uProjectionMatrix;
uniform mat4 uViewMatrix;

varying vec4 vColor;

void main () {
if (gl_DrawID == 0) {
vColor = vec4(1.0, 0.0, 0.0, 1.0);
} else if (gl_DrawID == 1) {
vColor = vec4(0.0, 1.0, 0.0, 1.0);
} else if (gl_DrawID == 2) {
vColor = vec4(0.0, 0.0, 1.0, 1.0);
} else {
vColor = vec4(1.0, 1.0, 0.0, 1.0);
}

gl_Position = uProjectionMatrix * uViewMatrix * vec4(aPosition + aOffset, 1.0);
}`,
frag: basicFrag,
}),
attributes: {
aPosition: ctx.vertexBuffer(geom.positions),
aNormal: ctx.vertexBuffer(geom.normals),
aTexCoord: ctx.vertexBuffer(geom.uvs),
aOffset: {
buffer: ctx.vertexBuffer(instancePositions),
divisor: 1,
},
},
indices: ctx.indexBuffer(geom.cells),
multiDraw: {
counts,
offsets,
instanceCounts,
baseVertices: baseVertices,
baseInstances: baseInstances,
},
uniforms: {
uProjectionMatrix: camera.projectionMatrix,
uViewMatrix: camera.viewMatrix,
},
};

const onResize = () => {
const W = window.innerWidth;
const H = window.innerHeight;
ctx.set({ width: W, height: H });
camera.set({ aspect: W / H });
};
window.addEventListener("resize", onResize);
onResize();

ctx.frame(() => {
ctx.submit(clearCmd);

ctx.submit(drawCmd, {
uniforms: {
uProjectionMatrix: camera.projectionMatrix,
uViewMatrix: camera.viewMatrix,
},
});

ctx.debug(false);

window.dispatchEvent(new CustomEvent("pex-screenshot"));
});
116 changes: 116 additions & 0 deletions examples/multi-draw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import createContext from "../index.js";

import { perspective as createCamera, orbiter as createOrbiter } from "pex-cam";

import { cube, sphere, torus } from "primitive-geometry";
import merge from "geom-merge";

import basicFrag from "./shaders/basic.frag.js";

const ctx = createContext({ pixelRatio: devicePixelRatio });

const CellsConstructor = Uint16Array;

const sphereGeometry = sphere();
sphereGeometry.positions = sphereGeometry.positions.map((value, i) =>
i % 3 === 0 ? value - 1.25 : value
);
const cubeGeometry = cube();

const torusGeometry = torus();
torusGeometry.positions = torusGeometry.positions.map((value, i) =>
i % 3 === 0 ? value + 1.25 : value
);
const geometries = [sphereGeometry, cubeGeometry, torusGeometry];
const geometry = merge(geometries);

const camera = createCamera({
position: [0, 0, 3],
});
createOrbiter({ camera });

const clearCmd = {
pass: ctx.pass({
clearColor: [0.2, 0.2, 0.2, 1],
clearDepth: 1,
}),
};

const counts = new Int32Array(geometries.length);
const offsets = new Int32Array(geometries.length);

for (let i = 0; i < geometries.length; i++) {
counts[i] = geometries[i].cells.length;

if (i > 0) {
offsets[i] =
offsets[i - 1] +
geometries[i - 1].cells.length * CellsConstructor.BYTES_PER_ELEMENT;
}
}

const drawCmd = {
pipeline: ctx.pipeline({
depthTest: true,
vert: /* glsl */ `
#extension GL_ANGLE_multi_draw: require

attribute vec3 aPosition;

uniform mat4 uProjectionMatrix;
uniform mat4 uViewMatrix;

varying vec4 vColor;

void main () {
if (gl_DrawID == 0) {
vColor = vec4(1.0, 0.0, 0.0, 1.0);
} else if (gl_DrawID == 1) {
vColor = vec4(0.0, 1.0, 0.0, 1.0);
} else if (gl_DrawID == 2) {
vColor = vec4(0.0, 0.0, 1.0, 1.0);
} else {
vColor = vec4(1.0, 1.0, 0.0, 1.0);
}

gl_Position = uProjectionMatrix * uViewMatrix * vec4(aPosition, 1.0);
}`,
frag: basicFrag,
}),
attributes: {
aPosition: ctx.vertexBuffer(geometry.positions),
},
indices: ctx.indexBuffer(geometry.cells),
multiDraw: {
counts,
offsets,
},
uniforms: {
uProjectionMatrix: camera.projectionMatrix,
uViewMatrix: camera.viewMatrix,
},
};

const onResize = () => {
const W = window.innerWidth;
const H = window.innerHeight;
ctx.set({ width: W, height: H });
camera.set({ aspect: W / H });
};
window.addEventListener("resize", onResize);
onResize();

ctx.frame(() => {
ctx.submit(clearCmd);

ctx.submit(drawCmd, {
uniforms: {
uProjectionMatrix: camera.projectionMatrix,
uViewMatrix: camera.viewMatrix,
},
});

ctx.debug(false);

window.dispatchEvent(new CustomEvent("pex-screenshot"));
});
102 changes: 92 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ function createContext(options = {}) {
? !!gl.getExtension("EXT_color_buffer_float")
: !!gl.getExtension("WEBGL_color_buffer_float"),
colorBufferHalfFloat: !!gl.getExtension("EXT_color_buffer_half_float"),
multiDraw: !!gl.getExtension("WEBGL_multi_draw"),
},
/**
* Getter for `gl.drawingBufferWidth`
Expand Down Expand Up @@ -1091,22 +1092,103 @@ function createContext(options = {}) {
this.state.indexBuffer.type;

if (instanced) {
gl.drawElementsInstanced(
primitive,
count,
type,
offset,
cmd.instances
);
if (cmd.multiDraw && ctx.capabilities.multiDraw) {
const ext = gl.getExtension("WEBGL_multi_draw");

if (cmd.multiDraw.baseVertices && cmd.multiDraw.baseInstances) {
const baseVertexBaseInstanceExt = gl.getExtension(
"WEBGL_multi_draw_instanced_base_vertex_base_instance"
);
if (!baseVertexBaseInstanceExt) {
throw new Error(
"WEBGL_multi_draw_instanced_base_vertex_base_instance not supported"
);
}
baseVertexBaseInstanceExt.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(
primitive,
cmd.multiDraw.counts,
cmd.multiDraw.countsOffset || 0,
type,
cmd.multiDraw.offsets,
cmd.multiDraw.offsetsOffset || 0,
cmd.multiDraw.instanceCounts,
cmd.multiDraw.instanceCountsOffset || 0,
cmd.multiDraw.baseVertices,
cmd.multiDraw.baseVerticesOffset || 0,
cmd.multiDraw.baseInstances,
cmd.multiDraw.baseInstancesOffset || 0,
cmd.multiDraw.counts.length
);
} else {
ext.multiDrawElementsInstancedWEBGL(
primitive,
cmd.multiDraw.counts,
cmd.multiDraw.countsOffset || 0,
type,
cmd.multiDraw.offsets,
cmd.multiDraw.offsetsOffset || 0,
cmd.multiDraw.instanceCounts,
cmd.multiDraw.instanceCountsOffset || 0,
cmd.multiDraw.counts.length
);
}
} else {
gl.drawElementsInstanced(
primitive,
count,
type,
offset,
cmd.instances
);
}
} else {
gl.drawElements(primitive, count, type, offset);
if (cmd.multiDraw && ctx.capabilities.multiDraw) {
const ext = gl.getExtension("WEBGL_multi_draw");
ext.multiDrawElementsWEBGL(
primitive,
cmd.multiDraw.counts,
cmd.multiDraw.countsOffset || 0,
type,
cmd.multiDraw.offsets,
cmd.multiDraw.offsetsOffset || 0,
cmd.multiDraw.counts.length
);
} else {
gl.drawElements(primitive, count, type, offset);
}
}
} else if (cmd.count) {
const first = 0;
if (instanced) {
gl.drawArraysInstanced(primitive, first, cmd.count, cmd.instances);
if (cmd.multiDraw && ctx.capabilities.multiDraw) {
const ext = gl.getExtension("WEBGL_multi_draw");
ext.multiDrawArraysInstancedWEBGL(
primitive,
cmd.multiDraw.firsts,
cmd.multiDraw.firstsOffset || 0,
cmd.multiDraw.counts,
cmd.multiDraw.countsOffset || 0,
cmd.multiDraw.instanceCounts,
cmd.multiDraw.instanceCountsOffset || 0,
cmd.multiDraw.firsts.length
);
} else {
gl.drawArraysInstanced(primitive, first, cmd.count, cmd.instances);
}
} else {
gl.drawArrays(primitive, first, cmd.count);
if (cmd.multiDraw && ctx.capabilities.multiDraw) {
const ext = gl.getExtension("WEBGL_multi_draw");
ext.multiDrawArraysWEBGL(
primitive,
cmd.multiDraw.firsts,
cmd.multiDraw.firstsOffset || 0,
cmd.multiDraw.counts,
cmd.multiDraw.countsOffset || 0,
cmd.multiDraw.firsts.length
);
} else {
gl.drawArrays(primitive, first, cmd.count);
}
}
} else {
throw new Error("Vertex arrays requires elements or count to draw");
Expand Down
Loading