Skip to content

Commit

Permalink
feat: Geometry.drawRange
Browse files Browse the repository at this point in the history
  • Loading branch information
CodyJasonBennett committed Jul 13, 2023
1 parent 376d806 commit 0379c81
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 182 deletions.
152 changes: 7 additions & 145 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,151 +155,6 @@ renderer.render(mesh, camera)

</details>

<hr />

In four, a camera is optional and not needed for fullscreen effects. The following renders a red fullscreen triangle:

<details>

<summary>Show WebGL example</summary>

```ts
import { WebGLRenderer, Geometry, Material, Mesh } from 'four'

const renderer = new WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.canvas)

const geometry = new Geometry({
position: { size: 2, data: new Float32Array([-1, -1, 3, -1, -1, 3]) },
})
const material = new Material({
vertex: /* glsl */ `#version 300 es
in vec3 position;
void main() {
gl_Position = vec4(position, 1);
}
`,
fragment: /* glsl */ `#version 300 es
out lowp vec4 color;
void main() {
color = vec4(1, 0, 0, 1);
}
`,
})
const mesh = new Mesh(geometry, material)

renderer.render(mesh)
```

</details>

<details>

<summary>Show WebGPU example</summary>

```ts
import { WebGPURenderer, Geometry, Material, Mesh } from 'four'

const renderer = new WebGPURenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.canvas)

const geometry = new Geometry({
position: { size: 2, data: new Float32Array([-1, -1, 3, -1, -1, 3]) },
})
const material = new Material({
vertex: /* wgsl */ `
@vertex
fn main(@location(0) position: vec3<f32>) -> @builtin(position) vec4<f32> {
return vec4(position, 1);
}
`,
fragment: /* wgsl */ `
@fragment
fn main() -> @location(0) vec4<f32> {
return vec4(1, 0, 0, 1);
}
`,
})
const mesh = new Mesh(geometry, material)

renderer.render(mesh)
```

</details>

<hr />

Vertex data or geometry are also optional for drawing. The following renders 3 vertices and computes everything in shaders:

<details>

<summary>Show WebGL example</summary>

```ts
import { WebGLRenderer, Material, Mesh } from 'four'

const renderer = new WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.canvas)

const material = new Material({
vertex: /* glsl */ `#version 300 es
void main() {
const vec2 position[3] = vec2[](vec2(-1), vec2(3, -1), vec2(-1, 3));
gl_Position = vec4(position[gl_VertexID], 0, 1);
}
`,
fragment: /* glsl */ `#version 300 es
out lowp vec4 color;
void main() {
color = vec4(1, 0, 0, 1);
}
`,
})
const mesh = new Mesh()
mesh.material = material

renderer.render(mesh)
```

</details>

<details>

<summary>Show WebGPU example</summary>

```ts
import { WebGPURenderer, Material, Mesh } from 'four'

const renderer = new WebGPURenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.canvas)

const material = new Material({
vertex: /* wgsl */ `
@vertex
fn main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
const position = array<vec2<f32>, 3>(vec2(-1), vec2(3, -1), vec2(-1, 3));
return vec4(position[i], 0, 1);
}
`,
fragment: /* wgsl */ `
@fragment
fn main() -> @location(0) vec4<f32> {
return vec4(1, 0, 0, 1);
}
`,
})
const mesh = new Mesh()
mesh.material = material

renderer.render(mesh)
```

</details>

## Object3D

An `Object3D` represents a basic 3D object and its transforms. Objects are linked via their `parent` and `children` properties, constructing a rooted scene-graph.
Expand Down Expand Up @@ -367,6 +222,13 @@ const geometry = new Geometry({
})
```

A `DrawRange` can also be configured to control rendering without submitting vertex data. This is useful for GPU-computed geometry or vertex pulling, as demonstrated in the fullscreen demos.

```ts
const geometry = new Geometry()
geometry.drawRange = { start: 0, count: 3 } // renders 3 vertices at starting index 0
```

### Attribute

An `Attribute` defines a data view, its per-vertex size, and an optional per-instance divisor (see [instancing](#instancing)).
Expand Down
2 changes: 1 addition & 1 deletion examples/webgl-cube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const material = new Material({
}
`,
fragment: /* glsl */ `#version 300 es
precision highp float;
precision lowp float;
uniform vec3 color;
in vec3 vNormal;
Expand Down
22 changes: 9 additions & 13 deletions examples/webgl-fullscreen.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,26 @@
import { WebGLRenderer, Geometry, Material, Mesh, Texture } from 'four'
import { WebGLRenderer, Material, Mesh, Texture } from 'four'

const renderer = new WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.canvas)

const geometry = new Geometry({
position: { size: 2, data: new Float32Array([-1, -1, 3, -1, -1, 3]) },
uv: { size: 2, data: new Float32Array([0, 0, 2, 0, 0, 2]) },
})

const material = new Material({
uniforms: {
time: 0,
color: new Texture(await createImageBitmap(new ImageData(new Uint8ClampedArray([76, 51, 128, 255]), 1, 1))),
},
vertex: /* glsl */ `#version 300 es
in vec2 uv;
in vec3 position;
out vec2 vUv;
const vec2 triangle[3] = vec2[](vec2(-1), vec2(3, -1), vec2(-1, 3));
void main() {
vUv = uv;
gl_Position = vec4(position, 1);
gl_Position = vec4(triangle[gl_VertexID], 0, 1);
vUv = abs(gl_Position.xy) - 1.0;
}
`,
fragment: /* glsl */ `#version 300 es
precision highp float;
precision lowp float;
uniform float time;
uniform sampler2D color;
Expand All @@ -40,7 +34,9 @@ const material = new Material({
`,
})

const mesh = new Mesh(geometry, material)
const mesh = new Mesh()
mesh.material = material
mesh.geometry.drawRange.count = 3

window.addEventListener('resize', () => {
renderer.setSize(window.innerWidth, window.innerHeight)
Expand Down
4 changes: 2 additions & 2 deletions examples/webgl-mrt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const compute = new Mesh(
}
`,
fragment: /* glsl */ `#version 300 es
precision highp float;
precision lowp float;
layout(location = 0) out vec4 color0;
layout(location = 1) out vec4 color1;
Expand Down Expand Up @@ -64,7 +64,7 @@ const composite = new Mesh(
}
`,
fragment: /* glsl */ `#version 300 es
precision highp float;
precision lowp float;
uniform sampler2D texture0;
uniform sampler2D texture1;
Expand Down
2 changes: 1 addition & 1 deletion examples/webgl-transparency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const material = new Material({
}
`,
fragment: /* glsl */ `#version 300 es
precision highp float;
precision lowp float;
in vec3 vColor;
out vec4 pc_fragColor;
Expand Down
24 changes: 9 additions & 15 deletions examples/webgpu-fullscreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ const renderer = new WebGPURenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.canvas)

const geometry = new Geometry({
position: { size: 2, data: new Float32Array([-1, -1, 3, -1, -1, 3]) },
uv: { size: 2, data: new Float32Array([0, 0, 2, 0, 0, 2]) },
})

const material = new Material({
uniforms: {
time: 0,
Expand All @@ -20,23 +15,20 @@ const material = new Material({
};
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
struct VertexIn {
@location(0) position: vec3<f32>,
@location(1) uv: vec2<f32>,
};
struct VertexOut {
@builtin(position) position: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) uv: vec2<f32>,
};
const triangle = array<vec2<f32>, 3>(vec2(-1), vec2(3, -1), vec2(-1, 3));
@vertex
fn main(input: VertexIn) -> VertexOut {
fn main(@builtin(vertex_index) i: u32) -> VertexOut {
var out: VertexOut;
out.position = vec4(input.position, 1.0);
out.color = vec4(0.5 + 0.3 * cos(vec3(input.uv, 0.0) + uniforms.time), 0.0);
out.uv = input.uv;
out.position = vec4(triangle[i], 0, 1);
out.uv = abs(out.position.xy) - 1.0;
out.color = vec4(0.5 + 0.3 * cos(vec3(out.uv, 0.0) + uniforms.time), 0.0);
return out;
}
`,
Expand All @@ -62,7 +54,9 @@ const material = new Material({
`,
})

const mesh = new Mesh(geometry, material)
const mesh = new Mesh()
mesh.material = material
mesh.geometry.drawRange.count = 3

window.addEventListener('resize', () => {
renderer.setSize(window.innerWidth, window.innerHeight)
Expand Down
13 changes: 13 additions & 0 deletions src/Geometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,23 @@ export interface Attribute {
needsUpdate?: boolean
}

/**
* Specifies the visible range of vertices or indices to draw when rendering.
*/
export interface DrawRange {
start: number
count: number
}

/**
* Constructs a geometry object. Used to store program attributes.
*/
export class Geometry {
/**
* Configures the geometry's {@link DrawRange}.
*/
drawRange: DrawRange = { start: 0, count: Infinity }

constructor(
/**
* Geometry program attributes.
Expand Down
12 changes: 10 additions & 2 deletions src/WebGLRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,9 +605,17 @@ export class WebGLRenderer {

const mode = this.gl[node.mode.toUpperCase() as Uppercase<Mode>]
const { index, position } = node.geometry.attributes
const { start, count } = node.geometry.drawRange
if (index)
this.gl.drawElementsInstanced(mode, index.data.length / index.size, getDataType(index.data)!, 0, node.instances)
else if (position) this.gl.drawArraysInstanced(mode, 0, position.data.length / position.size, node.instances)
this.gl.drawElementsInstanced(
mode,
min(count, index.data.length / index.size),
getDataType(index.data)!,
start,
node.instances,
)
else if (position)
this.gl.drawArraysInstanced(mode, start, min(count, position.data.length / position.size), node.instances)
else this.gl.drawArraysInstanced(mode, 0, 3, node.instances)
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/WebGPURenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { Camera } from './Camera'
import { Mesh, type Mode } from './Mesh'
import type { Object3D } from './Object3D'
import { type RenderTarget } from './RenderTarget'
import { Compiled } from './_utils'
import { Compiled, min } from './_utils'
import type { Attribute, AttributeData, Geometry } from './Geometry'
import type { Material, Side, Uniform } from './Material'
import { Texture } from './Texture'
Expand Down Expand Up @@ -623,8 +623,9 @@ export class WebGPURenderer {

// Alternate drawing for indexed and non-indexed children
const { index, position } = node.geometry.attributes
if (index) this._passEncoder.drawIndexed(index.data.length / index.size, node.instances)
else if (position) this._passEncoder.draw(position.data.length / position.size, node.instances)
const { start, count } = node.geometry.drawRange
if (index) this._passEncoder.drawIndexed(min(count, index.data.length / index.size), node.instances, start)
else if (position) this._passEncoder.draw(min(count, position.data.length / position.size), node.instances, start)
else this._passEncoder.draw(3, node.instances)
}

Expand Down

0 comments on commit 0379c81

Please sign in to comment.