Improve WebGL leather material

This commit is contained in:
2026-06-06 03:00:07 +02:00
parent f634500121
commit 13f8b60e20
+53 -26
View File
@@ -175,40 +175,44 @@ const materials = {
color: 0x25130b, color: 0x25130b,
map: leatherTextures.color, map: leatherTextures.color,
normalMap: leatherTextures.normal, normalMap: leatherTextures.normal,
normalScale: new THREE.Vector2(0.055, 0.055), normalScale: new THREE.Vector2(0.07, 0.07),
roughness: 0.72, roughnessMap: leatherTextures.roughness,
roughness: 0.78,
metalness: 0.02, metalness: 0.02,
envMapIntensity: 0.08, envMapIntensity: 0.1,
side: THREE.DoubleSide side: THREE.DoubleSide
}), }),
hingeLeather: new THREE.MeshStandardMaterial({ hingeLeather: new THREE.MeshStandardMaterial({
color: 0x32180c, color: 0x32180c,
map: leatherTextures.color, map: leatherTextures.color,
normalMap: leatherTextures.normal, normalMap: leatherTextures.normal,
normalScale: new THREE.Vector2(0.048, 0.048), normalScale: new THREE.Vector2(0.062, 0.062),
roughness: 0.78, roughnessMap: leatherTextures.roughness,
roughness: 0.82,
metalness: 0.015, metalness: 0.015,
envMapIntensity: 0.06, envMapIntensity: 0.08,
side: THREE.DoubleSide side: THREE.DoubleSide
}), }),
spineBaseLeather: new THREE.MeshStandardMaterial({ spineBaseLeather: new THREE.MeshStandardMaterial({
color: 0x2a1209, color: 0x2a1209,
map: leatherTextures.color, map: leatherTextures.color,
normalMap: leatherTextures.normal, normalMap: leatherTextures.normal,
normalScale: new THREE.Vector2(0.042, 0.042), normalScale: new THREE.Vector2(0.055, 0.055),
roughness: 0.82, roughnessMap: leatherTextures.roughness,
roughness: 0.86,
metalness: 0.01, metalness: 0.01,
envMapIntensity: 0.04, envMapIntensity: 0.06,
side: THREE.DoubleSide side: THREE.DoubleSide
}), }),
coverEdge: new THREE.MeshStandardMaterial({ coverEdge: new THREE.MeshStandardMaterial({
color: 0x5b351b, color: 0x5b351b,
map: leatherTextures.color, map: leatherTextures.color,
normalMap: leatherTextures.normal, normalMap: leatherTextures.normal,
normalScale: new THREE.Vector2(0.052, 0.052), normalScale: new THREE.Vector2(0.068, 0.068),
roughness: 0.76, roughnessMap: leatherTextures.roughness,
roughness: 0.8,
metalness: 0.015, metalness: 0.015,
envMapIntensity: 0.05, envMapIntensity: 0.07,
side: THREE.DoubleSide side: THREE.DoubleSide
}), }),
pageBlock: new THREE.MeshStandardMaterial({ pageBlock: new THREE.MeshStandardMaterial({
@@ -252,6 +256,9 @@ const materials = {
}), }),
spineCloth: new THREE.MeshStandardMaterial({ spineCloth: new THREE.MeshStandardMaterial({
color: 0x8e1d18, color: 0x8e1d18,
normalMap: leatherTextures.normal,
normalScale: new THREE.Vector2(0.04, 0.04),
roughnessMap: leatherTextures.roughness,
roughness: 0.82, roughness: 0.82,
metalness: 0, metalness: 0,
envMapIntensity: 0.08, envMapIntensity: 0.08,
@@ -1840,22 +1847,31 @@ function createLeatherTextures() {
const size = 1024; const size = 1024;
const colorCanvas = document.createElement('canvas'); const colorCanvas = document.createElement('canvas');
const normalCanvas = document.createElement('canvas'); const normalCanvas = document.createElement('canvas');
const roughnessCanvas = document.createElement('canvas');
colorCanvas.width = size; colorCanvas.width = size;
colorCanvas.height = size; colorCanvas.height = size;
normalCanvas.width = size; normalCanvas.width = size;
normalCanvas.height = size; normalCanvas.height = size;
roughnessCanvas.width = size;
roughnessCanvas.height = size;
const colorContext = colorCanvas.getContext('2d'); const colorContext = colorCanvas.getContext('2d');
const normalContext = normalCanvas.getContext('2d'); const normalContext = normalCanvas.getContext('2d');
const roughnessContext = roughnessCanvas.getContext('2d');
const colorImage = colorContext.createImageData(size, size); const colorImage = colorContext.createImageData(size, size);
const normalImage = normalContext.createImageData(size, size); const normalImage = normalContext.createImageData(size, size);
const roughnessImage = roughnessContext.createImageData(size, size);
const heightAt = (x, y) => { const heightAt = (x, y) => {
const nx = x / size; const nx = x / size;
const ny = y / size; const ny = y / size;
const longGrain = Math.sin((nx * 18 + Math.sin(ny * 18.8495559215) * 0.24) * 6.28318530718); const longGrain = Math.sin((nx * 24 + Math.sin(ny * 31.4159265359) * 0.18) * 6.28318530718);
const crossGrain = Math.sin((ny * 42 + Math.sin(nx * 12.5663706144) * 0.16) * 6.28318530718); const secondaryGrain = Math.sin((nx * 63 + ny * 9 + Math.sin(ny * 50.2654824574) * 0.1) * 6.28318530718);
const poreA = Math.sin((nx * 91 + ny * 37) * 6.28318530718); const crossGrain = Math.sin((ny * 39 + Math.sin(nx * 18.8495559215) * 0.12) * 6.28318530718);
const poreB = Math.sin((nx * 47 - ny * 83) * 6.28318530718); const poreA = Math.sin((nx * 137 + ny * 71) * 6.28318530718);
return longGrain * 0.34 + crossGrain * 0.22 + poreA * poreB * 0.16; const poreB = Math.sin((nx * 97 - ny * 113) * 6.28318530718);
const pebble = Math.sin((nx * 181 + Math.sin(ny * 25.1327412287) * 0.22) * 6.28318530718) *
Math.sin((ny * 167 + Math.sin(nx * 37.6991118431) * 0.18) * 6.28318530718);
const pit = Math.max(0, 0.58 - Math.abs(poreA * poreB));
return longGrain * 0.22 + secondaryGrain * 0.16 + crossGrain * 0.1 + pebble * 0.18 - pit * 0.24;
}; };
for (let y = 0; y < size; y += 1) { for (let y = 0; y < size; y += 1) {
@@ -1863,34 +1879,44 @@ function createLeatherTextures() {
const wrappedX = (x + size) % size; const wrappedX = (x + size) % size;
const wrappedY = (y + size) % size; const wrappedY = (y + size) % size;
const height = heightAt(wrappedX, wrappedY); const height = heightAt(wrappedX, wrappedY);
const grain = THREE.MathUtils.clamp(0.54 + height * 0.22, 0, 1); const grain = THREE.MathUtils.clamp(0.58 + height * 0.24, 0, 1);
const warm = 0.82 + 0.18 * Math.sin((x * 0.07 + y * 0.013)); const warm = 0.86 + 0.1 * Math.sin((x * 0.045 + y * 0.011)) + 0.04 * Math.sin((x * 0.009 - y * 0.031));
const index = (y * size + x) * 4; const index = (y * size + x) * 4;
colorImage.data[index] = Math.round(132 * grain * warm); colorImage.data[index] = Math.round(118 * grain * warm);
colorImage.data[index + 1] = Math.round(66 * grain * warm); colorImage.data[index + 1] = Math.round(54 * grain * warm);
colorImage.data[index + 2] = Math.round(29 * grain); colorImage.data[index + 2] = Math.round(22 * grain);
colorImage.data[index + 3] = 255; colorImage.data[index + 3] = 255;
const hLeft = heightAt((x - 1 + size) % size, wrappedY); const hLeft = heightAt((x - 1 + size) % size, wrappedY);
const hRight = heightAt((x + 1) % size, wrappedY); const hRight = heightAt((x + 1) % size, wrappedY);
const hDown = heightAt(wrappedX, (y - 1 + size) % size); const hDown = heightAt(wrappedX, (y - 1 + size) % size);
const hUp = heightAt(wrappedX, (y + 1) % size); const hUp = heightAt(wrappedX, (y + 1) % size);
const normal = new THREE.Vector3((hLeft - hRight) * 2.8, (hDown - hUp) * 2.8, 1).normalize(); const normal = new THREE.Vector3((hLeft - hRight) * 4.1, (hDown - hUp) * 4.1, 1).normalize();
normalImage.data[index] = Math.round((normal.x * 0.5 + 0.5) * 255); normalImage.data[index] = Math.round((normal.x * 0.5 + 0.5) * 255);
normalImage.data[index + 1] = Math.round((normal.y * 0.5 + 0.5) * 255); normalImage.data[index + 1] = Math.round((normal.y * 0.5 + 0.5) * 255);
normalImage.data[index + 2] = Math.round((normal.z * 0.5 + 0.5) * 255); normalImage.data[index + 2] = Math.round((normal.z * 0.5 + 0.5) * 255);
normalImage.data[index + 3] = 255; normalImage.data[index + 3] = 255;
const fiberContrast = Math.abs(hLeft - hRight) + Math.abs(hDown - hUp);
const roughness = THREE.MathUtils.clamp(0.76 + height * 0.1 + fiberContrast * 1.4, 0.5, 0.96);
const roughnessByte = Math.round(roughness * 255);
roughnessImage.data[index] = roughnessByte;
roughnessImage.data[index + 1] = roughnessByte;
roughnessImage.data[index + 2] = roughnessByte;
roughnessImage.data[index + 3] = 255;
} }
} }
colorContext.putImageData(colorImage, 0, 0); colorContext.putImageData(colorImage, 0, 0);
normalContext.putImageData(normalImage, 0, 0); normalContext.putImageData(normalImage, 0, 0);
roughnessContext.putImageData(roughnessImage, 0, 0);
const colorTexture = new THREE.CanvasTexture(colorCanvas); const colorTexture = new THREE.CanvasTexture(colorCanvas);
const normalTexture = new THREE.CanvasTexture(normalCanvas); const normalTexture = new THREE.CanvasTexture(normalCanvas);
[colorTexture, normalTexture].forEach((texture) => { const roughnessTexture = new THREE.CanvasTexture(roughnessCanvas);
[colorTexture, normalTexture, roughnessTexture].forEach((texture) => {
texture.wrapS = THREE.RepeatWrapping; texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2.5, 1.6); texture.repeat.set(3.6, 2.2);
texture.anisotropy = maxTextureAnisotropy; texture.anisotropy = maxTextureAnisotropy;
texture.minFilter = THREE.LinearMipmapLinearFilter; texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.magFilter = THREE.LinearFilter; texture.magFilter = THREE.LinearFilter;
@@ -1898,7 +1924,8 @@ function createLeatherTextures() {
}); });
colorTexture.colorSpace = THREE.SRGBColorSpace; colorTexture.colorSpace = THREE.SRGBColorSpace;
normalTexture.colorSpace = THREE.NoColorSpace; normalTexture.colorSpace = THREE.NoColorSpace;
return { color: colorTexture, normal: normalTexture }; roughnessTexture.colorSpace = THREE.NoColorSpace;
return { color: colorTexture, normal: normalTexture, roughness: roughnessTexture };
} }
function createRoomReflectionTexture() { function createRoomReflectionTexture() {