Add WebGL book headbands and bounce lighting

This commit is contained in:
2026-06-06 10:29:18 +02:00
parent 925caa57bb
commit 0956d2ef1f
2 changed files with 180 additions and 9 deletions
+38 -4
View File
@@ -68,6 +68,12 @@ function createBookContext(options) {
envMapIntensity: 0.08,
side: THREE.DoubleSide
}),
headband: options.materials?.headband ?? new THREE.MeshStandardMaterial({
color: 0xf4dcc0,
roughness: 0.82,
metalness: 0,
envMapIntensity: 0.06
}),
pageTop: options.materials?.pageTop ?? new THREE.MeshStandardMaterial({
color: 0xf1dfba,
roughness: 0.82,
@@ -324,26 +330,53 @@ function addClothSpine(group, context, model) {
mesh.userData.bookPart = 'spine';
configurePartMaterial(context, mesh.material, 'spine');
group.add(mesh);
createHeadbandMeshes(context, model).forEach((headband) => group.add(headband));
}
function createHeadbandMeshes(context, model) {
const radius = 0.0046;
const centerOffset = radius * 0.62;
const spineProfile = [];
for (let i = 2; i <= 30; i += 1) {
const point = spineCurvePoint(i / 32, model.spineWidth);
spineProfile.push(new THREE.Vector3(point.x, point.y + 0.0012, 0));
}
const meshes = [];
[-1, 1].forEach((zSide) => {
const z = zSide * (model.pageDepth * 0.5 + centerOffset);
const curve = new THREE.CatmullRomCurve3(spineProfile.map((point) => point.clone().setZ(z)));
const geometry = new THREE.TubeGeometry(curve, 56, radius, 10, false);
const mesh = new THREE.Mesh(geometry, context.materials.headband);
mesh.userData.bookPart = 'headband';
configurePartMaterial(context, mesh.material, 'headband');
meshes.push(mesh);
});
return meshes;
}
function createClothSpineGeometry(depth, spineWidth) {
const endOverrun = 0.0012;
const profile = [];
for (let i = 0; i <= 32; i += 1) {
profile.push(spineCurvePoint(i / 32, spineWidth));
}
const positions = [];
const uvs = [];
const indices = [];
const front = [];
const back = [];
const push = (point, z) => {
const push = (point, z, uv) => {
const index = positions.length / 3;
positions.push(point.x, point.y, z);
uvs.push(uv.u, uv.v);
return index;
};
profile.forEach((point) => {
front.push(push(point, depth * 0.5 + 0.024));
back.push(push(point, -depth * 0.5 - 0.024));
profile.forEach((point, index) => {
const u = profile.length <= 1 ? 0.5 : index / (profile.length - 1);
front.push(push(point, depth * 0.5 + endOverrun, { u, v: 1 }));
back.push(push(point, -depth * 0.5 - endOverrun, { u, v: 0 }));
});
for (let i = 0; i < profile.length - 1; i += 1) {
indices.push(front[i], back[i], front[i + 1]);
@@ -353,6 +386,7 @@ function createClothSpineGeometry(depth, spineWidth) {
const geometry = new THREE.BufferGeometry();
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
geometry.computeVertexNormals();
return geometry;
}