Skip to content

Commit

Permalink
adds an example for 3D texture use
Browse files Browse the repository at this point in the history
  • Loading branch information
clindsey committed Feb 1, 2023
1 parent 6991cde commit 45e3d41
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ More info about using gpu-io with Threejs can be found in the [Threejs Example](

### Limitations

- gpu-io currently only supports GPULayers with 1D or 2D arrays of dense data. 3D textures are not officially supported by the library. You can still compute 3D simulations in gpu-io, you will just need to pass in your 3D position data as a 1D list to a GPULayer and then access it in the fragment shader using .xyz. TODO: make example for this.
- gpu-io currently only supports GPULayers with 1D or 2D arrays of dense data. 3D textures are not officially supported by the library. You can still compute 3D simulations in gpu-io, you will just need to pass in your 3D position data as a 1D list to a GPULayer and then access it in the fragment shader using .xyz. See [this example](https://github.com/amandaghassaei/gpu-io/blob/main/examples/texture-3d/index.js) for more details.
- gpu-io does not currently allow you to pass in your own vertex shaders. Currently all computation is happening in user-specified fragment shaders; vertex shaders are managed internally.
- In order for the WRAP/FILTER polyfilling to work correctly, any calls to texture() must contain a direct reference to the sampler2D that it should operate on. For example:

Expand Down
19 changes: 19 additions & 0 deletions examples/texture-3d/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<html>
<head>
<title>GPU-IO - 3D Texture</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta charset="utf-8"/>
<script src="../../dist/gpu-io.min.js"></script>
<style>
body, html, canvas {
width: 32px; /* assuming texture cube will be 32x32x32 */
height: calc(32px * 32);
padding: 0;
margin: 0 auto;
}
</style>
</head>
<body>
</body>
<script src="index.js"></script>
</html>
126 changes: 126 additions & 0 deletions examples/texture-3d/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
const {
GPUComposer,
GPULayer,
GPUProgram,
renderRGBProgram,
FLOAT,
INT,
REPEAT,
NEAREST,
} = GPUIO;

// Init a canvas element.
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);

// Init a composer.
const composer = new GPUComposer({ canvas });

// Create a texture of size [width, height * depth], this will be the 3D texture cube.
// Draw a sphere inside the texture cube, sliced through the z-axis and vertically stacked
const width = height = depth = 32
const data = new Float32Array(width * height * depth * 3) // 1D array to store 3D texture data
const sphereSDF = (x, y, z, radius) => Math.sqrt(x ** 2 + y ** 2 + z ** 2) - radius
const step = (edge, x) => edge > x ? 0 : 1
for (let z = 0; z < depth; z++) {
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const index = (y * width + x + (z * height * width)) * 3 // 3D texture coordinates to 1D array index
const xPos = x / width - 0.5 // put the sphere in the center
const yPos = y / height - 0.5
const zPos = z / depth - 0.5
const pct = step(sphereSDF(xPos, yPos, zPos, 0.5), 0) // 1 = inside sphere, 0 = outside sphere
data[index + 0] = pct * x / width // color the sphere slices
data[index + 1] = pct * y / height
data[index + 2] = pct * z / depth
}
}
}

const state = new GPULayer(composer, {
name: 'state',
dimensions: [width, height * depth],
numComponents: 3, // state data has three components.
type: FLOAT,
filter: NEAREST,
numBuffers: 2, // Use 2 buffers so we can toggle read/write from one to the other.
wrapX: REPEAT,
wrapY: REPEAT,
array: data,
});

// Init a program to move the sphere through the texture.
// Demonstrates how to access texture cube using x, y, z coordinates.
const textureCubeProgram = new GPUProgram(composer, {
name: 'render',
fragmentShader: `
in vec2 v_uv;
uniform sampler2D u_state;
uniform vec3 u_size;
out vec3 out_result;
// assumes cube texture is size [width, height * depth]
vec3 uvToCube (vec2 uv, vec3 cubeSize) {
return vec3(
uv.x,
mod(uv.y, 1.0 / cubeSize.y) * cubeSize.y,
floor(uv.y / (1.0 / cubeSize.y)) / cubeSize.z
);
}
// cubeCoord components are [0..1]
vec2 cubeToUv (vec3 cubeCoord, vec3 cubeSize) {
return vec2(
cubeCoord.x,
cubeCoord.z + cubeCoord.y / cubeSize.z
);
}
void main() {
vec3 cubeCoord = uvToCube(v_uv, u_size);
cubeCoord.z += 1.0 / u_size.z; // sphere rolls through
vec2 uv = cubeToUv(cubeCoord, u_size);
out_result = texture(u_state, uv).xyz;
}
`,
uniforms: [
{ // Index of sampler2D uniform to assign to value "u_state".
name: 'u_state',
value: 0,
type: INT,
},
{ // Need dimensions for projecting coordinates between cube and plane.
name: 'u_size',
value: [width, height ,depth],
type: FLOAT,
},
],
});

// Init a program to render state to canvas.
// See https://github.com/amandaghassaei/gpu-io/tree/main/docs#gpuprogram-helper-functions for more built-in GPUPrograms to use.
const renderProgram = renderRGBProgram(composer, {
name: 'render',
type: state.type,
});

// Simulation/render loop.
function loop() {
window.requestAnimationFrame(loop);

// Compute state and write result to state.
composer.step({
program: textureCubeProgram,
input: state,
output: state,
});

// If no "output", will draw to canvas.
composer.step({
program: renderProgram,
input: state,
});
}
loop(); // Start animation loop.

0 comments on commit 45e3d41

Please sign in to comment.