[object Object]

← back to Quadrille Showroom

Clean+clear pass: bright even gallery lighting, true-colour crisp swatches (low env wash), hi-res brass CHINA SEAS plaque + hi-res placards

15f8c33c43860f61d99f0a80d887f54fab550dee · 2026-06-27 00:42:38 -0700 · Steve

Files touched

Diff

commit 15f8c33c43860f61d99f0a80d887f54fab550dee
Author: Steve <steve@designerwallcoverings.com>
Date:   Sat Jun 27 00:42:38 2026 -0700

    Clean+clear pass: bright even gallery lighting, true-colour crisp swatches (low env wash), hi-res brass CHINA SEAS plaque + hi-res placards
---
 public/js/showroom.js | 60 ++++++++++++++++++++++++++++-----------------------
 1 file changed, 33 insertions(+), 27 deletions(-)

diff --git a/public/js/showroom.js b/public/js/showroom.js
index 6c9b92e..1b0a428 100644
--- a/public/js/showroom.js
+++ b/public/js/showroom.js
@@ -75,7 +75,7 @@ function init() {
   renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 1.5));
   if (THREE.sRGBEncoding) renderer.outputEncoding = THREE.sRGBEncoding; // correct gamma → no washed-out look
   renderer.toneMapping = THREE.ACESFilmicToneMapping;
-  renderer.toneMappingExposure = 1.06; // slight lift — opens shadows without washing highlights
+  renderer.toneMappingExposure = 1.12; // brighter clean gallery exposure
   renderer.shadowMap.enabled = true;             // contact shadows = the #1 realism lever
   // Soft PCF — penumbra blur on the wing/furniture shadows reads far more believable than
   // hard PCF. Cost is modest at one shadow-casting light; the adaptive guard protects FPS.
@@ -450,13 +450,13 @@ function revertWalls() {
 // LIGHTING — minimal: ambient + hemi + 4 point
 // ============================================================
 function buildLighting() {
-  // Lower fills — the environment map + key light now carry the scene, so heavy
-  // ambient would just wash out the new contrast/shadows.
-  scene.add(new THREE.AmbientLight(0xfff5e6, 0.32));
-  scene.add(new THREE.HemisphereLight(0xfff8f0, 0x2a2a32, 0.32));
+  // Bright, EVEN gallery light — Phillip-Jeffries white-box: high ambient + hemi so the
+  // wallcovering swatches read crisp and true-colour, not greyed by moody low-key shadow.
+  scene.add(new THREE.AmbientLight(0xffffff, 0.62));
+  scene.add(new THREE.HemisphereLight(0xfffaf2, 0xded6c8, 0.5)); // sky/ground bounce — warm, even
 
-  // KEY light — warm, casts soft shadows (the wings drop real shadows on wall + floor)
-  const key = new THREE.DirectionalLight(0xfff3e2, 0.95);
+  // KEY light — soft, gentle shadow (clean contact, not dramatic)
+  const key = new THREE.DirectionalLight(0xfff6ea, 0.7);
   key.position.set(2.4, CONFIG.room.height + 1.4, 2.2);
   key.target.position.set(0, 1.0, -CONFIG.room.depth / 2);
   key.castShadow = true;
@@ -824,18 +824,20 @@ function buildWingWall() {
     // Gallery placard — engraved-look brass-on-dark plate under the board.
     const pName = (product.pattern_name || product.name || 'Pattern').toString();
     const pColor = (product.color_name || product.color || '').toString();
-    const plc = document.createElement('canvas'); plc.width = 256; plc.height = 64;
+    const plc = document.createElement('canvas'); plc.width = 1024; plc.height = 256; // 4× — crisp
     const pcx = plc.getContext('2d');
-    pcx.fillStyle = '#1c1c22'; pcx.fillRect(0, 0, 256, 64);
-    pcx.strokeStyle = 'rgba(201,169,110,0.5)'; pcx.lineWidth = 2; pcx.strokeRect(3, 3, 250, 58);
-    pcx.fillStyle = '#c9a96e'; pcx.textAlign = 'center';
-    pcx.font = '600 22px Georgia, serif';
-    pcx.fillText(pName.length > 22 ? pName.slice(0, 21) + '…' : pName, 128, 28);
+    pcx.fillStyle = '#15110a'; pcx.fillRect(0, 0, 1024, 256);
+    pcx.strokeStyle = 'rgba(185,147,63,0.6)'; pcx.lineWidth = 6; pcx.strokeRect(10, 10, 1004, 236);
+    pcx.fillStyle = '#cbb073'; pcx.textAlign = 'center'; pcx.textBaseline = 'middle';
+    pcx.font = '600 76px Georgia, serif';
+    pcx.fillText(pName.length > 26 ? pName.slice(0, 25) + '…' : pName, 512, 100);
     if (pColor) {
-      pcx.fillStyle = '#9a8c6e'; pcx.font = '12px sans-serif';
-      pcx.fillText(pColor.toUpperCase().slice(0, 30), 128, 48);
+      pcx.fillStyle = '#9a8c6e'; pcx.font = '500 44px sans-serif';
+      pcx.fillText(pColor.toUpperCase().slice(0, 34), 512, 172);
     }
     const plTex = new THREE.CanvasTexture(plc);
+    if (renderer.capabilities) plTex.anisotropy = renderer.capabilities.getMaxAnisotropy();
+    plTex.minFilter = THREE.LinearMipmapLinearFilter; plTex.generateMipmaps = true;
     const placard = new THREE.Mesh(
       new THREE.PlaneGeometry(Math.min(wingW * 0.9, 0.32), 0.075),
       new THREE.MeshBasicMaterial({ map: plTex, transparent: true })
@@ -907,19 +909,22 @@ function buildWingWall() {
     const centerX = (startX + endX) / 2;
     const sectionW = (section.endIdx - section.startIdx + 1) * slotW;
 
-    // Vendor label canvas
-    const lc = document.createElement('canvas'); lc.width = 512; lc.height = 48;
+    // Vendor label — HIGH-RES engraved-brass gallery plaque (crisp, clean, PJ-style).
+    const lc = document.createElement('canvas'); lc.width = 2048; lc.height = 160;
     const lx = lc.getContext('2d');
-    lx.fillStyle = vColor; lx.fillRect(0, 0, 512, 48);
-    const grad = lx.createLinearGradient(0, 0, 0, 48);
-    grad.addColorStop(0, 'rgba(255,255,255,0.15)'); grad.addColorStop(1, 'rgba(0,0,0,0.2)');
-    lx.fillStyle = grad; lx.fillRect(0, 0, 512, 48);
-    lx.fillStyle = '#ffffff'; lx.font = 'bold 26px sans-serif'; lx.textAlign = 'center'; lx.textBaseline = 'middle';
-    lx.fillText(section.vendor.toUpperCase(), 256, 26);
+    lx.clearRect(0, 0, 2048, 160);                                  // transparent ground (no cheap colour bar)
+    lx.fillStyle = '#1a1206'; lx.font = '600 84px Georgia, "Times New Roman", serif';
+    lx.textAlign = 'center'; lx.textBaseline = 'middle';
+    // letter-spaced uppercase for an engraved-plaque feel
+    const txt = section.vendor.toUpperCase().split('').join('  ');
+    lx.fillStyle = '#b9933f'; lx.fillText(txt, 1024, 86);           // brass
     const lt = new THREE.CanvasTexture(lc);
-    const labelW = Math.max(sectionW * 0.8, 0.3);
-    const label = new THREE.Mesh(new THREE.PlaneGeometry(labelW, 0.07), new THREE.MeshBasicMaterial({ map: lt }));
-    label.position.set(centerX, WC.height + 0.15, 0.01);
+    if (renderer.capabilities) lt.anisotropy = renderer.capabilities.getMaxAnisotropy();
+    lt.minFilter = THREE.LinearMipmapLinearFilter; lt.generateMipmaps = true;
+    const labelW = Math.max(sectionW * 0.9, 0.4);
+    const label = new THREE.Mesh(new THREE.PlaneGeometry(labelW, labelW * 160 / 2048),
+      new THREE.MeshBasicMaterial({ map: lt, transparent: true }));
+    label.position.set(centerX, WC.height + 0.18, 0.01);
     wallGroup.add(label);
 
     // Thin vertical divider at section start (except first)
@@ -1057,7 +1062,8 @@ function loadWingBook(pivot, onDone) {
   const src = imageUrl.charAt(0) === '/' ? imageUrl : ('/api/proxy/image?url=' + encodeURIComponent(imageUrl));
   const maxAniso = (renderer.capabilities && renderer.capabilities.getMaxAnisotropy) ? renderer.capabilities.getMaxAnisotropy() : 1;
 
-  const leafMat = (tex) => new THREE.MeshStandardMaterial({ map: tex, roughness: 0.82, metalness: 0.0, envMapIntensity: 0.45 });
+  // Low env wash + matte paper sheen so the pattern reads crisp and true-colour, not greyed.
+  const leafMat = (tex) => new THREE.MeshStandardMaterial({ map: tex, roughness: 0.7, metalness: 0.0, envMapIntensity: 0.12 });
   const apply = (cached) => {
     ud.faceR.material = leafMat(cached.texture);
     ud.faceL.material = leafMat(cached.mirror);

← 575ee37 Scene luxury pass 1: limewash plaster left wall (was brick),  ·  back to Quadrille Showroom  ·  Add 30s captioned how-to-use demo recorder 80afbd8 →