Implement patterned glass and maximum-depth distance controls

Add gobang surface refraction and shared photo/depth distance adjustment. Compare V2 Small and Base at input 770 with maximum-depth masks and pixel diagnostics. Record browser QA and update runtime dependencies and model notices.
This commit is contained in:
2026-10-06 14:33:47 +09:00
parent 619d980098
commit 71c8d3b7ff
82 changed files with 1602 additions and 1103 deletions
+25 -21
View File
@@ -1,10 +1,12 @@
import assert from 'node:assert/strict';
import * as THREE from 'three';
import { WindowReveal } from '../src/objects/WindowReveal.ts';
import { PHOTO_GLASS_DISTANCE } from '../src/constants/glass.ts';
const glassZ = 0.6;
const cameraZ = 3;
const depthScales = [0, 0.25, 0.4];
const glassGaps = [PHOTO_GLASS_DISTANCE.minGap, PHOTO_GLASS_DISTANCE.maxGap];
const aspects = [0.7, 1.6];
const offsets = [
[0, 0],
@@ -58,29 +60,31 @@ for (const aspect of aspects) {
const frontW = baseW * (3 - glassZ) / 3;
const frontH = baseH * (3 - glassZ) / 3;
for (const depthScale of depthScales) {
const backZ = glassZ - 0.04 - depthScale;
const backW = baseW * (3 - backZ) / 3;
const backH = baseH * (3 - backZ) / 3;
const photo = makePhoto(backW, backH, backZ);
const priorFrame = makePriorFrame(frontW, frontH);
const material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide });
const reveal = new WindowReveal(material);
reveal.update(frontW, frontH, backW, backH, glassZ, backZ);
reveal.meshes.forEach((mesh) => mesh.updateMatrixWorld(true));
for (const [offsetX, offsetY] of offsets) {
const camera = new THREE.PerspectiveCamera(40, 1, 0.1, 100);
camera.position.set(offsetX, offsetY, cameraZ);
camera.lookAt(0, 0, 0);
for (const xRatio of samples) {
for (const yRatio of samples) {
const x = xRatio * frontW / 2;
const y = yRatio * frontH / 2;
if (!coveredBy([photo, ...priorFrame.meshes], camera, x, y, glassZ)) baselineMisses += 1;
assert.equal(coveredBy([photo, ...reveal.meshes], camera, x, y, glassZ), true, `uncovered aperture ray aspect=${aspect} depth=${depthScale} offset=${offsetX},${offsetY} sample=${xRatio},${yRatio}`);
for (const glassGap of glassGaps) {
const backZ = glassZ - glassGap - depthScale;
const backW = baseW * (3 - backZ) / 3;
const backH = baseH * (3 - backZ) / 3;
const photo = makePhoto(backW, backH, backZ);
const priorFrame = makePriorFrame(frontW, frontH);
const material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide });
const reveal = new WindowReveal(material);
reveal.update(frontW, frontH, backW, backH, glassZ, backZ);
reveal.meshes.forEach((mesh) => mesh.updateMatrixWorld(true));
for (const [offsetX, offsetY] of offsets) {
const camera = new THREE.PerspectiveCamera(40, 1, 0.1, 100);
camera.position.set(offsetX, offsetY, cameraZ);
camera.lookAt(0, 0, 0);
for (const xRatio of samples) {
for (const yRatio of samples) {
const x = xRatio * frontW / 2;
const y = yRatio * frontH / 2;
if (!coveredBy([photo, ...priorFrame.meshes], camera, x, y, glassZ)) baselineMisses += 1;
assert.equal(coveredBy([photo, ...reveal.meshes], camera, x, y, glassZ), true, `uncovered aperture ray aspect=${aspect} depth=${depthScale} gap=${glassGap} offset=${offsetX},${offsetY} sample=${xRatio},${yRatio}`);
}
}
}
photo.geometry.dispose(); photo.material.dispose(); priorFrame.geometry.dispose(); priorFrame.material.dispose(); reveal.dispose(); material.dispose();
}
photo.geometry.dispose(); photo.material.dispose(); priorFrame.geometry.dispose(); priorFrame.material.dispose(); reveal.dispose(); material.dispose();
}
}
@@ -103,4 +107,4 @@ reusableReveal.dispose();
assert.deepEqual(disposeCounts.map((getCount) => getCount()), [1, 1, 1, 1], 'disposal emits exactly once per reveal geometry');
reuseMaterial.dispose();
console.log(`Window reveal coverage passed: ${baselineMisses} previous-frame misses sealed across ${aspects.length} aspects, ${depthScales.length} depth scales, ${offsets.length} view offsets (including neutral) and ${samples.length ** 2} aperture samples; geometry reuse/disposal verified.`);
console.log(`Window reveal coverage passed: ${baselineMisses} previous-frame misses sealed across ${aspects.length} aspects, ${depthScales.length} depth scales, ${glassGaps.length} glass gaps, ${offsets.length} view offsets (including neutral) and ${samples.length ** 2} aperture samples; geometry reuse/disposal verified.`);