Skip to content

Commit

Permalink
Added normal mapping example (#305)
Browse files Browse the repository at this point in the history
* Added normal mapping example
  • Loading branch information
cmhhelgeson authored Oct 27, 2023
1 parent 302bfef commit 7349a25
Show file tree
Hide file tree
Showing 15 changed files with 1,311 additions and 0 deletions.
Binary file added public/img/brickwall_diffuse.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/brickwall_height.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/brickwall_normal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/spiral_height.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/spiral_normal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/toybox_height.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/toybox_normal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/wood_diffuse.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/components/SampleLayout.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ nav.sourceFileNav li {
display: inline-block;
margin: 0;
padding: 0;
transition: 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

nav.sourceFileNav li a {
Expand Down
347 changes: 347 additions & 0 deletions src/meshes/box.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
import { vec3 } from 'wgpu-matrix';
import { getMeshPosAtIndex, getMeshUVAtIndex, Mesh } from './mesh';

export interface BoxMesh extends Mesh {
vertices: Float32Array;
indices: Uint16Array | Uint32Array;
vertexStride: number;
}

//// Borrowed and simplified from https://github.com/mrdoob/three.js/blob/master/src/geometries/BoxGeometry.js
//// Presumes vertex buffer alignment of verts, normals, and uvs
const createBoxGeometry = (
width = 1.0,
height = 1.0,
depth = 1.0,
widthSegments = 1.0,
heightSegments = 1.0,
depthSegments = 1.0
) => {
widthSegments = Math.floor(widthSegments);
heightSegments = Math.floor(heightSegments);
depthSegments = Math.floor(depthSegments);

const indices = [];
const vertNormalUVBuffer = [];

let numVertices = 0;

const buildPlane = (
u: 0 | 1 | 2,
v: 0 | 1 | 2,
w: 0 | 1 | 2,
udir: -1 | 1,
vdir: -1 | 1,
planeWidth: number,
planeHeight: number,
planeDepth: number,
xSections: number,
ySections: number
) => {
const segmentWidth = planeWidth / xSections;
const segmentHeight = planeHeight / ySections;

const widthHalf = planeWidth / 2;
const heightHalf = planeHeight / 2;
const depthHalf = planeDepth / 2;

const gridX1 = xSections + 1;
const gridY1 = ySections + 1;

let vertexCounter = 0;

const vertex = vec3.create();
const normal = vec3.create();
for (let iy = 0; iy < gridY1; iy++) {
const y = iy * segmentHeight - heightHalf;

for (let ix = 0; ix < gridX1; ix++) {
const x = ix * segmentWidth - widthHalf;

//Calculate plane vertices
vertex[u] = x * udir;
vertex[v] = y * vdir;
vertex[w] = depthHalf;
vertNormalUVBuffer.push(...vertex);

//Caclulate normal
normal[u] = 0;
normal[v] = 0;
normal[w] = planeDepth > 0 ? 1.0 : -1.0;
vertNormalUVBuffer.push(...normal);

//Calculate uvs
vertNormalUVBuffer.push(ix / xSections);
vertNormalUVBuffer.push(1 - iy / ySections);

vertexCounter += 1;
}
}

for (let iy = 0; iy < ySections; iy++) {
for (let ix = 0; ix < xSections; ix++) {
const a = numVertices + ix + gridX1 * iy;
const b = numVertices + ix + gridX1 * (iy + 1);
const c = numVertices + (ix + 1) + gridX1 * (iy + 1);
const d = numVertices + (ix + 1) + gridX1 * iy;

//Push vertex indices
//6 indices for each face
indices.push(a, b, d);
indices.push(b, c, d);

numVertices += vertexCounter;
}
}
};

//Side face
buildPlane(
2, //z
1, //y
0, //x
-1,
-1,
depth,
height,
width,
depthSegments,
heightSegments
);

//Side face
buildPlane(
2, //z
1, //y
0, //x
1,
-1,
depth,
height,
-width,
depthSegments,
heightSegments
);

//Bottom face
buildPlane(
0, //x
2, //z
1, //y
1,
1,
width,
depth,
height,
widthSegments,
depthSegments
);

//Top face
buildPlane(
0, //x
2, //z
1, //y
1,
-1,
width,
depth,
-height,
widthSegments,
depthSegments
);

//Side faces
buildPlane(
0, //x
1, //y
2, //z
1,
-1,
width,
height,
depth,
widthSegments,
heightSegments
);

//Side face
buildPlane(
0, //x
1, //y
2, //z
-1,
-1,
width,
height,
-depth,
widthSegments,
heightSegments
);

return {
vertices: vertNormalUVBuffer,
indices: indices,
};
};

type IndexFormat = 'uint16' | 'uint32';

// Box mesh code ported from threejs, with addition of indexFormat specifier for vertex pulling
export const createBoxMesh = (
width = 1.0,
height = 1.0,
depth = 1.0,
widthSegments = 1.0,
heightSegments = 1.0,
depthSegments = 1.0,
indexFormat: IndexFormat = 'uint16'
): Mesh => {
const { vertices, indices } = createBoxGeometry(
width,
height,
depth,
widthSegments,
heightSegments,
depthSegments
);

const vertexStride = 8 * Float32Array.BYTES_PER_ELEMENT; //calculateVertexStride(vertexProperties);

const indicesArray =
indexFormat === 'uint16'
? new Uint16Array(indices)
: new Uint32Array(indices);

return {
vertices: new Float32Array(vertices),
indices: indicesArray,
vertexStride: vertexStride,
};
};

export const createBoxMeshWithTangents = (
width = 1.0,
height = 1.0,
depth = 1.0,
widthSegments = 1.0,
heightSegments = 1.0,
depthSegments = 1.0
): Mesh => {
const mesh = createBoxMesh(
width,
height,
depth,
widthSegments,
heightSegments,
depthSegments
);

const originalStrideElements =
mesh.vertexStride / Float32Array.BYTES_PER_ELEMENT;

const vertexCount = mesh.vertices.length / originalStrideElements;

const tangents = new Array(vertexCount);
const bitangents = new Array(vertexCount);
const counts = new Array(vertexCount);
for (let i = 0; i < vertexCount; i++) {
tangents[i] = [0, 0, 0];
bitangents[i] = [0, 0, 0];
counts[i] = 0;
}

for (let i = 0; i < mesh.indices.length; i += 3) {
const [idx1, idx2, idx3] = [
mesh.indices[i],
mesh.indices[i + 1],
mesh.indices[i + 2],
];

const [pos1, pos2, pos3] = [
getMeshPosAtIndex(mesh, idx1),
getMeshPosAtIndex(mesh, idx2),
getMeshPosAtIndex(mesh, idx3),
];

const [uv1, uv2, uv3] = [
getMeshUVAtIndex(mesh, idx1),
getMeshUVAtIndex(mesh, idx2),
getMeshUVAtIndex(mesh, idx3),
];

const edge1 = vec3.sub(pos2, pos1);
const edge2 = vec3.sub(pos3, pos1);
const deltaUV1 = vec3.sub(uv2, uv1);
const deltaUV2 = vec3.sub(uv3, uv1);

// Edge of a triangle moves in both u and v direction (2d)
// deltaU * tangent vector + deltav * bitangent
// Manipulating the data into matrices, we get an equation

const constantVal =
1.0 / (deltaUV1[0] * deltaUV2[1] - deltaUV1[1] * deltaUV2[0]);

const tangent = [
constantVal * (deltaUV2[1] * edge1[0] - deltaUV1[1] * edge2[0]),
constantVal * (deltaUV2[1] * edge1[1] - deltaUV1[1] * edge2[1]),
constantVal * (deltaUV2[1] * edge1[2] - deltaUV1[1] * edge2[2]),
];

const bitangent = [
constantVal * (-deltaUV2[0] * edge1[0] + deltaUV1[0] * edge2[0]),
constantVal * (-deltaUV2[0] * edge1[1] + deltaUV1[0] * edge2[1]),
constantVal * (-deltaUV2[0] * edge1[2] + deltaUV1[0] * edge2[2]),
];

//Accumulate tangents and bitangents
tangents[idx1] = vec3.add(tangents[idx1], tangent);
bitangents[idx1] = vec3.add(bitangents[idx1], bitangent);
tangents[idx2] = vec3.add(tangents[idx2], tangent);
bitangents[idx2] = vec3.add(bitangents[idx2], bitangent);
tangents[idx3] = vec3.add(tangents[idx3], tangent);
bitangents[idx3] = vec3.add(bitangents[idx3], bitangent);

//Increment index count
counts[idx1]++;
counts[idx2]++;
counts[idx3]++;
}

for (let i = 0; i < tangents.length; i++) {
tangents[i] = vec3.divScalar(tangents[i], counts[i]);
bitangents[i] = vec3.divScalar(bitangents[i], counts[i]);
}

const newStrideElements = 14;
const wTangentArray = new Float32Array(vertexCount * newStrideElements);

for (let i = 0; i < vertexCount; i++) {
//Copy original vertex data (pos, normal uv)
wTangentArray.set(
//Get the original vertex [8 elements] (3 ele pos, 3 ele normal, 2 ele uv)
mesh.vertices.subarray(
i * originalStrideElements,
(i + 1) * originalStrideElements
),
//And put it at the proper location in the new array [14 bytes = 8 og + 6 empty]
i * newStrideElements
);
//For each vertex, place tangent after originalStride
wTangentArray.set(
tangents[i],
i * newStrideElements + originalStrideElements
);
//Place bitangent after 3 elements of tangent
wTangentArray.set(
bitangents[i],
i * newStrideElements + originalStrideElements + 3
);
}

return {
vertices: wTangentArray,
indices: mesh.indices,
vertexStride: mesh.vertexStride + Float32Array.BYTES_PER_ELEMENT * 3 * 2,
};
};
Loading

0 comments on commit 7349a25

Please sign in to comment.