layershift 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +14 -0
  2. package/dist/components/layershift.js +921 -647
  3. package/dist/npm/layershift.es.js +3354 -2266
  4. package/dist/types/components/layershift/global.d.ts +2 -2
  5. package/dist/types/components/layershift/index.d.ts +5 -1
  6. package/dist/types/components/layershift/index.d.ts.map +1 -1
  7. package/dist/types/components/layershift/layershift-element.d.ts +41 -10
  8. package/dist/types/components/layershift/layershift-element.d.ts.map +1 -1
  9. package/dist/types/components/layershift/lifecycle.d.ts +6 -0
  10. package/dist/types/components/layershift/lifecycle.d.ts.map +1 -1
  11. package/dist/types/components/layershift/portal-element.d.ts +13 -4
  12. package/dist/types/components/layershift/portal-element.d.ts.map +1 -1
  13. package/dist/types/components/layershift/types.d.ts +30 -9
  14. package/dist/types/components/layershift/types.d.ts.map +1 -1
  15. package/dist/types/depth-analysis.d.ts +10 -6
  16. package/dist/types/depth-analysis.d.ts.map +1 -1
  17. package/dist/types/depth-effect-renderer.d.ts +186 -0
  18. package/dist/types/depth-effect-renderer.d.ts.map +1 -0
  19. package/dist/types/depth-estimator.d.ts +96 -0
  20. package/dist/types/depth-estimator.d.ts.map +1 -0
  21. package/dist/types/input-handler.d.ts +8 -2
  22. package/dist/types/input-handler.d.ts.map +1 -1
  23. package/dist/types/jfa-distance-field.d.ts +78 -0
  24. package/dist/types/jfa-distance-field.d.ts.map +1 -0
  25. package/dist/types/media-source.d.ts +76 -0
  26. package/dist/types/media-source.d.ts.map +1 -0
  27. package/dist/types/portal-renderer.d.ts +65 -66
  28. package/dist/types/portal-renderer.d.ts.map +1 -1
  29. package/dist/types/precomputed-depth.d.ts +14 -51
  30. package/dist/types/precomputed-depth.d.ts.map +1 -1
  31. package/dist/types/quality.d.ts +95 -0
  32. package/dist/types/quality.d.ts.map +1 -0
  33. package/dist/types/render-pass.d.ts +171 -0
  34. package/dist/types/render-pass.d.ts.map +1 -0
  35. package/dist/types/renderer-base.d.ts +129 -0
  36. package/dist/types/renderer-base.d.ts.map +1 -0
  37. package/dist/types/shared/channel-to-renderer.d.ts +72 -0
  38. package/dist/types/shared/channel-to-renderer.d.ts.map +1 -0
  39. package/dist/types/shared/filter-config.d.ts +184 -0
  40. package/dist/types/shared/filter-config.d.ts.map +1 -0
  41. package/dist/types/video-source.d.ts +0 -1
  42. package/dist/types/video-source.d.ts.map +1 -1
  43. package/dist/types/webgl-utils.d.ts +29 -0
  44. package/dist/types/webgl-utils.d.ts.map +1 -0
  45. package/package.json +31 -5
  46. package/dist/types/parallax-renderer.d.ts +0 -175
  47. package/dist/types/parallax-renderer.d.ts.map +0 -1
@@ -1,196 +1,472 @@
1
- var Layershift=(function(et){"use strict";var ut=typeof document<"u"?document.currentScript:null;class it{worker;currentBuffer;pendingTimeSec=null;workerBusy=!1;disposed=!1;constructor(t,e){this.worker=t,this.currentBuffer=new Uint8Array(e)}static async create(t,e,i){const o=new Worker(new URL("/assets/depth-worker-CMcEa805.js",ut&&ut.tagName.toUpperCase()==="SCRIPT"&&ut.src||new URL("layershift.js",document.baseURI).href),{type:"module"}),n=e*i,s=new it(o,n),h=t.frames.map(a=>{const l=new Uint8Array(a.length);return l.set(a),l.buffer});return await new Promise((a,l)=>{const u=setTimeout(()=>l(new Error("Worker init timeout")),1e4);o.onmessage=f=>{f.data.type==="ready"&&(clearTimeout(u),a())},o.onerror=f=>{clearTimeout(u),l(f)},o.postMessage({type:"init",frames:h,meta:{frameCount:t.meta.frameCount,fps:t.meta.fps,width:t.meta.width,height:t.meta.height},targetWidth:e,targetHeight:i},h)}),o.onmessage=a=>{if(a.data.type==="result"&&(s.currentBuffer=a.data.data,s.workerBusy=!1,s.pendingTimeSec!==null)){const l=s.pendingTimeSec;s.pendingTimeSec=null,s.requestSample(l)}},s}sample(t){return this.requestSample(t),this.currentBuffer}requestSample(t){if(!this.disposed){if(this.workerBusy){this.pendingTimeSec=t;return}this.workerBusy=!0,this.worker.postMessage({type:"sample",timeSec:t})}}dispose(){this.disposed=!0,this.worker.terminate()}}class gt{constructor(t,e,i){this.depthData=t,this.targetWidth=e,this.targetHeight=i;const o=t.meta.width*t.meta.height,n=e*i;this.interpolatedDepth=new Float32Array(o),this.bilateralOutput=new Float32Array(o),this.resizedDepth=new Float32Array(n),this.uint8Output=new Uint8Array(n)}interpolatedDepth;resizedDepth;bilateralOutput;uint8Output;lastFrameIndex=-1;lastNextFrameIndex=-1;lastLerpFactor=-1;sample(t){const e=j(t*this.depthData.meta.fps,0,this.depthData.meta.frameCount-1),i=Math.floor(e),o=Math.min(i+1,this.depthData.meta.frameCount-1),n=e-i,s=i!==this.lastFrameIndex||o!==this.lastNextFrameIndex,h=Math.abs(n-this.lastLerpFactor)>.001;if(!s&&!h)return this.uint8Output;this.lastFrameIndex=i,this.lastNextFrameIndex=o,this.lastLerpFactor=n;const a=1-n,l=this.depthData.frames[i],u=this.depthData.frames[o];for(let g=0;g<this.interpolatedDepth.length;g+=1)this.interpolatedDepth[g]=(l[g]*a+u[g]*n)/255;kt(this.interpolatedDepth,this.depthData.meta.width,this.depthData.meta.height,this.bilateralOutput);const f=this.targetWidth!==this.depthData.meta.width||this.targetHeight!==this.depthData.meta.height;f&&Ot(this.bilateralOutput,this.depthData.meta.width,this.depthData.meta.height,this.targetWidth,this.targetHeight,this.resizedDepth);const c=f?this.resizedDepth:this.bilateralOutput;for(let g=0;g<this.uint8Output.length;g+=1)this.uint8Output[g]=c[g]*255+.5|0;return this.uint8Output}}async function vt(r,t,e){const[i,o]=await Promise.all([Mt(t),_t(r)]);return It(o,i)}async function Mt(r){const t=await fetch(r);if(!t.ok)throw new Error(`Failed to fetch depth metadata (${t.status} ${t.statusText}).`);const e=await t.json();return Vt(e),{frameCount:e.frameCount,fps:e.fps,width:e.width,height:e.height,sourceFps:e.sourceFps}}async function _t(r,t){const e=await fetch(r);if(!e.ok)throw new Error(`Failed to fetch depth data (${e.status} ${e.statusText}).`);e.headers.get("content-length");const i=e.body;if(!i)return new Uint8Array(await e.arrayBuffer());const o=[];let n=0;const s=i.getReader();for(;;){const{done:l,value:u}=await s.read();if(l)break;u&&(o.push(u),n+=u.byteLength)}const h=new Uint8Array(n);let a=0;for(const l of o)h.set(l,a),a+=l.byteLength;return h}function It(r,t){if(r.byteLength<4)throw new Error("Depth data binary is missing the frame-count header.");const i=new DataView(r.buffer,r.byteOffset,r.byteLength).getUint32(0,!0),o=t.width*t.height,n=4+i*o;if(r.byteLength!==n)throw new Error(`Depth data byte length mismatch. Expected ${n} bytes, received ${r.byteLength}.`);if(i!==t.frameCount)throw new Error(`Depth frame count mismatch between metadata (${t.frameCount}) and binary header (${i}).`);const s=r.subarray(4),h=new Array(i);for(let a=0;a<i;a+=1){const l=a*o;h[a]=s.subarray(l,l+o)}return{meta:t,frames:h}}function Vt(r){if(!r||typeof r.frameCount!="number"||typeof r.fps!="number"||typeof r.width!="number"||typeof r.height!="number"||typeof r.sourceFps!="number")throw new Error("Depth metadata is malformed.");if(!Number.isFinite(r.frameCount)||!Number.isFinite(r.fps)||!Number.isFinite(r.width)||!Number.isFinite(r.height)||!Number.isFinite(r.sourceFps)||r.frameCount<=0||r.fps<=0||r.width<=0||r.height<=0||r.sourceFps<=0)throw new Error("Depth metadata contains invalid numeric values.")}function Ot(r,t,e,i,o,n){const s=t/i,h=e/o;for(let a=0;a<o;a+=1){const l=(a+.5)*h-.5,u=j(Math.floor(l),0,e-1),f=j(u+1,0,e-1),c=l-u;for(let g=0;g<i;g+=1){const d=(g+.5)*s-.5,m=j(Math.floor(d),0,t-1),U=j(m+1,0,t-1),y=d-m,E=r[u*t+m],p=r[u*t+U],v=r[f*t+m],T=r[f*t+U],x=E+(p-E)*y,A=v+(T-v)*y;n[a*i+g]=x+(A-x)*c}}}function kt(r,t,e,i){for(let s=0;s<e;s+=1)for(let h=0;h<t;h+=1){const a=s*t+h,l=r[a];let u=1,f=l;for(let c=-2;c<=2;c+=1){const g=s+c;if(!(g<0||g>=e))for(let d=-2;d<=2;d+=1){if(d===0&&c===0)continue;const m=h+d;if(m<0||m>=t)continue;const U=r[g*t+m],y=d*d+c*c,E=U-l,p=Math.exp(-y/2.25-E*E/.01);u+=p,f+=U*p}}i[a]=f/u}}function j(r,t,e){return Math.min(e,Math.max(t,r))}const Bt={parallaxStrength:.05,contrastLow:.05,contrastHigh:.95,verticalReduction:.5,dofStart:.6,dofStrength:.4,pomSteps:16,overscanPadding:.08};function Ht(r,t,e){const i=new Float32Array(256);if(r.length===0||t<=0||e<=0)return xt(i);const o=Nt(r.length),n=t*e;let s=0;const h=new Uint32Array(256);for(const T of o){const x=r[T],A=Math.min(x.length,n);for(let S=0;S<A;S+=1)h[x[S]]+=1;s+=A}if(s===0)return xt(i);const a=1/s;for(let T=0;T<256;T+=1)i[T]=h[T]*a;const l=new Float32Array(256);l[0]=i[0];for(let T=1;T<256;T+=1)l[T]=l[T-1]+i[T];const u=Y(l,.05),f=Y(l,.25),c=Y(l,.5),g=Y(l,.75),d=Y(l,.95);let m=0;for(let T=0;T<256;T+=1)m+=T/255*i[T];let U=0;for(let T=0;T<256;T+=1){const x=T/255-m;U+=i[T]*x*x}const y=Math.sqrt(U),E=d-u,p=g-f,v=zt(i);return{mean:m,stdDev:y,p5:u,p25:f,median:c,p75:g,p95:d,effectiveRange:E,iqr:p,bimodality:v,histogram:i}}function Xt(r){if(r.effectiveRange<.05||r.stdDev<.02)return{...Bt};const t=r.effectiveRange-.5,e=r.bimodality-.4,i=V(.05-t*.03+e*.01,.035,.065),o=V(r.p5-.03,0,.25),n=V(r.p95+.03,.75,1),s=V((i-.03)/.05,0,1),h=V(.6-s*.25,.35,.6),a=V(.6-t*.2,.5,.7),l=V(.4+t*.2,.25,.5),u=16,f=V(i+.03,.06,.1);return{parallaxStrength:i,contrastLow:o,contrastHigh:n,verticalReduction:h,dofStart:a,dofStrength:l,pomSteps:u,overscanPadding:f}}function Nt(r){if(r<=0)return[];if(r===1)return[0];const t=r-1,e=[0,Math.floor(r/4),Math.floor(r/2),Math.floor(3*r/4),t],i=new Set,o=[];for(const n of e)i.has(n)||(i.add(n),o.push(n));return o}function Y(r,t){for(let e=0;e<256;e+=1)if(r[e]>=t)return e/255;return 1}function zt(r){const t=new Float32Array(256);for(let c=0;c<256;c+=1){let g=0,d=0;for(let m=c-2;m<=c+2;m+=1)m>=0&&m<256&&(g+=r[m],d+=1);t[c]=g/d}let e=0;for(let c=0;c<256;c+=1)e+=t[c];e/=256;const i=e*2,o=25,n=[];for(let c=1;c<255;c+=1)t[c]>t[c-1]&&t[c]>t[c+1]&&t[c]>=i&&n.push({bin:c,height:t[c]});if(t[0]>t[1]&&t[0]>=i&&n.push({bin:0,height:t[0]}),t[255]>t[254]&&t[255]>=i&&n.push({bin:255,height:t[255]}),n.sort((c,g)=>g.height-c.height),n.length<2)return 0;const s=n[0];let h=null;for(let c=1;c<n.length;c+=1)if(Math.abs(n[c].bin-s.bin)>=o){h=n[c];break}if(!h)return 0;const a=Math.min(s.bin,h.bin),l=Math.max(s.bin,h.bin);let u=1/0;for(let c=a;c<=l;c+=1)t[c]<u&&(u=t[c]);const f=Math.min(s.height,h.height);return f<=0?0:V(1-u/f,0,1)}function xt(r){return{mean:0,stdDev:0,p5:0,p25:0,median:0,p75:0,p95:0,effectiveRange:0,iqr:0,bimodality:0,histogram:r}}function V(r,t,e){return Math.min(e,Math.max(t,r))}const Wt=`#version 300 es
2
- in vec2 aPosition;
3
-
4
- // UV coordinates for cover-fit + overscan.
5
- // Computed on the CPU and passed as a uniform to avoid
6
- // recreating geometry on every resize.
7
- uniform vec2 uUvOffset;
8
- uniform vec2 uUvScale;
9
-
10
- out vec2 vUv;
11
- out vec2 vScreenUv;
12
-
13
- void main() {
14
- // Map from clip space [-1,1] to [0,1], then apply cover-fit transform
15
- vec2 baseUv = aPosition * 0.5 + 0.5;
16
- vUv = baseUv * uUvScale + uUvOffset;
17
- // Screen-space UV always [0,1] used for vignette and edge fade
18
- // which should operate on screen position, not texture coordinates.
19
- vScreenUv = baseUv;
20
- gl_Position = vec4(aPosition, 0.0, 1.0);
1
+ var Layershift=(function(st){"use strict";class Ct{constructor(t){this.depthData=t;const e=t.meta.width*t.meta.height;this.uint8Output=new Uint8Array(e)}uint8Output;lastFrameIndex=-1;lastNextFrameIndex=-1;lastLerpFactor=-1;sample(t){const e=re(t*this.depthData.meta.fps,0,this.depthData.meta.frameCount-1),n=Math.floor(e),i=Math.min(n+1,this.depthData.meta.frameCount-1),r=e-n,s=n!==this.lastFrameIndex||i!==this.lastNextFrameIndex,a=Math.abs(r-this.lastLerpFactor)>.001;if(!s&&!a)return this.uint8Output;this.lastFrameIndex=n,this.lastNextFrameIndex=i,this.lastLerpFactor=r;const l=1-r,u=this.depthData.frames[n],h=this.depthData.frames[i];for(let f=0;f<this.uint8Output.length;f+=1)this.uint8Output[f]=u[f]*l+h[f]*r+.5|0;return this.uint8Output}}async function Lt(o,t,e){const[n,i]=await Promise.all([te(t),ee(o)]);return ne(i,n)}async function te(o){const t=await fetch(o);if(!t.ok)throw new Error(`Failed to fetch depth metadata (${t.status} ${t.statusText}).`);const e=await t.json();return ie(e),{frameCount:e.frameCount,fps:e.fps,width:e.width,height:e.height,sourceFps:e.sourceFps}}async function ee(o,t){const e=await fetch(o);if(!e.ok)throw new Error(`Failed to fetch depth data (${e.status} ${e.statusText}).`);e.headers.get("content-length");const n=e.body;if(!n)return new Uint8Array(await e.arrayBuffer());const i=[];let r=0;const s=n.getReader();for(;;){const{done:u,value:h}=await s.read();if(u)break;h&&(i.push(h),r+=h.byteLength)}const a=new Uint8Array(r);let l=0;for(const u of i)a.set(u,l),l+=u.byteLength;return a}function ne(o,t){if(o.byteLength<4)throw new Error("Depth data binary is missing the frame-count header.");const n=new DataView(o.buffer,o.byteOffset,o.byteLength).getUint32(0,!0),i=t.width*t.height,r=4+n*i;if(o.byteLength!==r)throw new Error(`Depth data byte length mismatch. Expected ${r} bytes, received ${o.byteLength}.`);if(n!==t.frameCount)throw new Error(`Depth frame count mismatch between metadata (${t.frameCount}) and binary header (${n}).`);const s=o.subarray(4),a=new Array(n);for(let l=0;l<n;l+=1){const u=l*i;a[l]=s.subarray(u,u+i)}return{meta:t,frames:a}}function ie(o){if(!o||typeof o.frameCount!="number"||typeof o.fps!="number"||typeof o.width!="number"||typeof o.height!="number"||typeof o.sourceFps!="number")throw new Error("Depth metadata is malformed.");if(!Number.isFinite(o.frameCount)||!Number.isFinite(o.fps)||!Number.isFinite(o.width)||!Number.isFinite(o.height)||!Number.isFinite(o.sourceFps)||o.frameCount<=0||o.fps<=0||o.width<=0||o.height<=0||o.sourceFps<=0)throw new Error("Depth metadata contains invalid numeric values.")}function N(o,t){const e=new Uint8Array(o*t);return e.fill(128),{meta:{frameCount:1,fps:1,width:o,height:t,sourceFps:1},frames:[e]}}function re(o,t,e){return Math.min(e,Math.max(t,o))}const oe={parallaxStrength:.05,contrastLow:.05,contrastHigh:.95,verticalReduction:.5,dofStart:.6,dofStrength:.4,pomSteps:16,overscanPadding:.08};function se(o,t,e){const n=new Float32Array(256);if(o.length===0||t<=0||e<=0)return Ut(n);const i=le(o.length),r=t*e;let s=0;const a=new Uint32Array(256);for(const b of i){const T=o[b],m=Math.min(T.length,r);for(let S=0;S<m;S+=1)a[T[S]]+=1;s+=m}if(s===0)return Ut(n);const l=1/s;for(let b=0;b<256;b+=1)n[b]=a[b]*l;const u=new Float32Array(256);u[0]=n[0];for(let b=1;b<256;b+=1)u[b]=u[b-1]+n[b];const h=tt(u,.05),f=tt(u,.25),c=tt(u,.5),E=tt(u,.75),v=tt(u,.95);let p=0;for(let b=0;b<256;b+=1)p+=b/255*n[b];let P=0;for(let b=0;b<256;b+=1){const T=b/255-p;P+=n[b]*T*T}const x=Math.sqrt(P),y=v-h,d=E-f,g=he(n);return{mean:p,stdDev:x,p5:h,p25:f,median:c,p75:E,p95:v,effectiveRange:y,iqr:d,bimodality:g,histogram:n}}function ae(o){if(o.effectiveRange<.05||o.stdDev<.02)return{...oe};const t=o.effectiveRange-.5,e=o.bimodality-.4,n=O(.05-t*.03+e*.01,.035,.065),i=O(o.p5-.03,0,.25),r=O(o.p95+.03,.75,1),s=O((n-.03)/.05,0,1),a=O(.6-s*.25,.35,.6),l=O(.6-t*.2,.5,.7),u=O(.4+t*.2,.25,.5),h=16,f=O(n+.03,.06,.1);return{parallaxStrength:n,contrastLow:i,contrastHigh:r,verticalReduction:a,dofStart:l,dofStrength:u,pomSteps:h,overscanPadding:f}}function le(o){if(o<=0)return[];if(o===1)return[0];const t=o-1,e=[0,Math.floor(o/4),Math.floor(o/2),Math.floor(3*o/4),t],n=new Set,i=[];for(const r of e)n.has(r)||(n.add(r),i.push(r));return i}function tt(o,t){for(let e=0;e<256;e+=1)if(o[e]>=t)return e/255;return 1}function he(o){const t=new Float32Array(256);for(let c=0;c<256;c+=1){let E=0,v=0;for(let p=c-2;p<=c+2;p+=1)p>=0&&p<256&&(E+=o[p],v+=1);t[c]=E/v}let e=0;for(let c=0;c<256;c+=1)e+=t[c];e/=256;const n=e*2,i=25,r=[];for(let c=1;c<255;c+=1)t[c]>t[c-1]&&t[c]>t[c+1]&&t[c]>=n&&r.push({bin:c,height:t[c]});if(t[0]>t[1]&&t[0]>=n&&r.push({bin:0,height:t[0]}),t[255]>t[254]&&t[255]>=n&&r.push({bin:255,height:t[255]}),r.sort((c,E)=>E.height-c.height),r.length<2)return 0;const s=r[0];let a=null;for(let c=1;c<r.length;c+=1)if(Math.abs(r[c].bin-s.bin)>=i){a=r[c];break}if(!a)return 0;const l=Math.min(s.bin,a.bin),u=Math.max(s.bin,a.bin);let h=1/0;for(let c=l;c<=u;c+=1)t[c]<h&&(h=t[c]);const f=Math.min(s.height,a.height);return f<=0?0:O(1-h/f,0,1)}function Ut(o){return{mean:0,stdDev:0,p5:0,p25:0,median:0,p75:0,p95:0,effectiveRange:0,iqr:0,bimodality:0,histogram:o}}function O(o,t,e){return Math.min(e,Math.max(t,o))}function z(o,t,e){const n=o.createShader(t);if(!n)throw new Error("Failed to create shader.");if(o.shaderSource(n,e),o.compileShader(n),!o.getShaderParameter(n,o.COMPILE_STATUS)){const i=o.getShaderInfoLog(n)??"";throw o.deleteShader(n),new Error(`Shader compilation failed:
2
+ ${i}`)}return n}function Et(o,t,e){const n=o.createProgram();if(!n)throw new Error("Failed to create program.");if(o.attachShader(n,t),o.attachShader(n,e),o.linkProgram(n),!o.getProgramParameter(n,o.LINK_STATUS)){const i=o.getProgramInfoLog(n)??"";throw o.deleteProgram(n),new Error(`Program linking failed:
3
+ ${i}`)}return o.detachShader(n,t),o.detachShader(n,e),o.deleteShader(t),o.deleteShader(e),n}function bt(o,t,e){const n={};for(const i of e)n[i]=o.getUniformLocation(t,i);return n}const ue=new Float32Array([-1,-1,1,-1,-1,1,1,1]);function _t(o,t){const e=o.createVertexArray();if(!e)throw new Error("Failed to create VAO.");o.bindVertexArray(e);const n=o.createBuffer();o.bindBuffer(o.ARRAY_BUFFER,n),o.bufferData(o.ARRAY_BUFFER,ue,o.STATIC_DRAW);const i=o.getAttribLocation(t,"aPosition");return o.enableVertexAttribArray(i),o.vertexAttribPointer(i,2,o.FLOAT,!1,0,0),o.bindVertexArray(null),e}class Mt{slots=new Map;nextUnit=0;register(t){if(this.slots.has(t))throw new Error(`TextureRegistry: slot '${t}' already registered.`);const e={name:t,unit:this.nextUnit++,texture:null};return this.slots.set(t,e),e}get(t){const e=this.slots.get(t);if(!e)throw new Error(`TextureRegistry: slot '${t}' not found.`);return e}disposeAll(t){for(const e of this.slots.values())e.texture&&(t.deleteTexture(e.texture),e.texture=null)}get size(){return this.slots.size}}function k(o,t,e,n,i){const r=z(o,o.VERTEX_SHADER,e),s=z(o,o.FRAGMENT_SHADER,n),a=Et(o,r,s),l=bt(o,a,i);return{name:t,program:a,uniforms:l,dispose(u){u.deleteProgram(a)}}}const ce={high:{dprCap:2,depthMaxDim:512,pomSteps:16,bilateralRadius:2,jfaDivisor:2},medium:{dprCap:1.5,depthMaxDim:512,pomSteps:16,bilateralRadius:2,jfaDivisor:2},low:{dprCap:1,depthMaxDim:256,pomSteps:8,bilateralRadius:1,jfaDivisor:4}};function fe(o){let t="unknown";const e=o.getExtension("WEBGL_debug_renderer_info");e&&(t=o.getParameter(e.UNMASKED_RENDERER_WEBGL)||"unknown");const n=o.getParameter(o.MAX_TEXTURE_SIZE),i=typeof navigator<"u"&&navigator.hardwareConcurrency||0,r=typeof navigator<"u"&&navigator.deviceMemory||0,s=typeof window<"u"&&window.devicePixelRatio||1,a=typeof screen<"u"?(screen.width||0)*(screen.height||0):0,l=typeof navigator<"u"&&("ontouchstart"in window||navigator.maxTouchPoints>0),u=a>0&&a<1920*1080;return{gpuRenderer:t,maxTextureSize:n,hardwareConcurrency:i,deviceMemory:r,devicePixelRatio:s,screenPixels:a,isMobile:l&&u}}const de=["mali-4","mali-t","adreno 3","adreno 4","adreno 5","powervr sgx","intel hd graphics","intel uhd graphics","intel iris","llvmpipe","swiftshader","software"],me=["nvidia","geforce","radeon rx","radeon pro","apple m","apple gpu","adreno 7","adreno 6","mali-g7","mali-g6"];function pe(o){let t=0;const e=o.gpuRenderer.toLowerCase(),n=de.some(r=>e.includes(r)),i=me.some(r=>e.includes(r));return n&&(t-=30),i&&(t+=20),o.maxTextureSize>=16384?t+=10:o.maxTextureSize>=8192?t+=5:o.maxTextureSize<=4096&&(t-=15),o.hardwareConcurrency>=8?t+=5:o.hardwareConcurrency>=4?t+=0:o.hardwareConcurrency>0&&o.hardwareConcurrency<4&&(t-=10),o.deviceMemory>=8?t+=5:o.deviceMemory>=4?t+=0:o.deviceMemory>0&&o.deviceMemory<4&&(t-=15),o.isMobile&&(t-=10),t>=0?"high":t>=-25?"medium":"low"}function It(o,t){const e=t&&t!=="auto"?t:pe(fe(o));return{tier:e,...ce[e]}}class at{static RESIZE_DEBOUNCE_MS=100;canvas;container;depthWidth=0;depthHeight=0;sourceDepthWidth=0;sourceDepthHeight=0;depthSubsampleBuffer=null;videoAspect=1.7777777777777777;isCameraSource=!1;uvOffset=[0,0];uvScale=[1,1];readDepth=null;readInput=null;mediaSource=null;onVideoFrame=null;animationFrameHandle=0;rvfcHandle=0;rvfcSupported=!1;resizeObserver=null;resizeTimer=null;qualityParams;constructor(t){this.container=t,this.canvas=document.createElement("canvas"),this.container.appendChild(this.canvas),this.canvas.addEventListener("webglcontextlost",this._handleContextLost),this.canvas.addEventListener("webglcontextrestored",this._handleContextRestored)}get canvasElement(){return this.canvas}start(t,e,n,i){this.stop(),this.mediaSource=t,this.readDepth=e,this.readInput=n,this.onVideoFrame=i??null,this.rvfcSupported=t.isLive&&typeof t.requestVideoFrameCallback=="function",this.rvfcSupported?this.rvfcHandle=t.requestVideoFrameCallback(this._videoFrameLoop):t.isLive||this.onDepthUpdate(t.currentTime),this.animationFrameHandle=window.requestAnimationFrame(this._rafLoop)}stop(){this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0),this.rvfcHandle&&this.mediaSource?.cancelVideoFrameCallback&&(this.mediaSource.cancelVideoFrameCallback(this.rvfcHandle),this.rvfcHandle=0),this.mediaSource=null,this.readDepth=null,this.readInput=null,this.onVideoFrame=null,this.rvfcSupported=!1}dispose(){this.stop(),this.disposeRenderer(),this.canvas.removeEventListener("webglcontextlost",this._handleContextLost),this.canvas.removeEventListener("webglcontextrestored",this._handleContextRestored),this.canvas.remove(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),window.removeEventListener("resize",this.scheduleResizeRecalculate),this.resizeTimer!==null&&(window.clearTimeout(this.resizeTimer),this.resizeTimer=null)}static isRVFCSupported(){return"requestVideoFrameCallback"in HTMLVideoElement.prototype}_rafLoop=()=>{this.animationFrameHandle=window.requestAnimationFrame(this._rafLoop),this.onRenderFrame()};_videoFrameLoop=(t,e)=>{const n=this.mediaSource;if(!n||!n.requestVideoFrameCallback)return;this.rvfcHandle=n.requestVideoFrameCallback(this._videoFrameLoop);const i=e.mediaTime??n.currentTime;this.onDepthUpdate(i),this.onVideoFrame&&this.onVideoFrame(i,e.presentedFrames??0)};_handleContextLost=t=>{t.preventDefault(),this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0)};_handleContextRestored=()=>{this.onContextRestored()};setupResizeHandling(){typeof ResizeObserver<"u"&&(this.resizeObserver=new ResizeObserver(()=>{this.scheduleResizeRecalculate()}),this.resizeObserver.observe(this.container)),window.addEventListener("resize",this.scheduleResizeRecalculate),this.recalculateViewportLayout()}scheduleResizeRecalculate=()=>{this.resizeTimer!==null&&window.clearTimeout(this.resizeTimer),this.resizeTimer=window.setTimeout(()=>{this.resizeTimer=null,this.recalculateViewportLayout()},at.RESIZE_DEBOUNCE_MS)};getViewportSize(){const t=Math.max(1,Math.round(this.container.clientWidth||window.innerWidth)),e=Math.max(1,Math.round(this.container.clientHeight||window.innerHeight));return{width:t,height:e}}clampDepthDimensions(t,e,n){this.sourceDepthWidth=t,this.sourceDepthHeight=e;let i=t,r=e;if(i>n||r>n){const s=n/Math.max(i,r);i=Math.max(1,Math.round(i*s)),r=Math.max(1,Math.round(r*s))}this.depthWidth=i,this.depthHeight=r,i!==t||r!==e?this.depthSubsampleBuffer=new Uint8Array(i*r):this.depthSubsampleBuffer=null}subsampleDepth(t){if(!this.depthSubsampleBuffer)return t;const e=this.depthSubsampleBuffer,n=this.sourceDepthWidth,i=this.depthWidth,r=this.depthHeight;for(let s=0;s<r;s++){const l=Math.min(Math.round(s*n/i),this.sourceDepthHeight-1)*n,u=s*i;for(let h=0;h<i;h++){const f=Math.min(Math.round(h*n/i),n-1);e[u+h]=t[l+f]}}return e}computeCoverFitUV(t,e){const{width:n,height:i}=this.getViewportSize(),r=n/i,s=t+e;let a=1,l=1;r>this.videoAspect?l=this.videoAspect/r:a=r/this.videoAspect;const u=1+s*2;a/=u,l/=u,this.uvOffset=[(1-a)/2,(1-l)/2],this.uvScale=[a,l],this.isCameraSource&&(this.uvOffset[0]+=this.uvScale[0],this.uvScale[0]=-this.uvScale[0])}}const ge=`#version 300 es
4
+ in vec2 aPosition;
5
+
6
+ // UV coordinates for cover-fit + overscan.
7
+ // Computed on the CPU and passed as a uniform to avoid
8
+ // recreating geometry on every resize.
9
+ uniform vec2 uUvOffset;
10
+ uniform vec2 uUvScale;
11
+
12
+ out vec2 vUv;
13
+ out vec2 vScreenUv;
14
+
15
+ void main() {
16
+ // Map from clip space [-1,1] to [0,1], then apply cover-fit transform
17
+ vec2 baseUv = aPosition * 0.5 + 0.5;
18
+ vUv = baseUv * uUvScale + uUvOffset;
19
+ // Screen-space UV always [0,1] -- used for tilted focal plane and edge fade
20
+ // which should operate on screen position, not texture coordinates.
21
+ vScreenUv = baseUv;
22
+ gl_Position = vec4(aPosition, 0.0, 1.0);
23
+ }
24
+ `,ve=`#version 300 es
25
+ in vec2 aPosition;
26
+ out vec2 vUv;
27
+
28
+ void main() {
29
+ vUv = aPosition * 0.5 + 0.5;
30
+ gl_Position = vec4(aPosition, 0.0, 1.0);
31
+ }
32
+ `,xe=`#version 300 es
33
+ precision highp float;
34
+
35
+ // BILATERAL_RADIUS is injected as a #define at compile time.
36
+ // Radius 2 -> 5x5 kernel (high/medium), radius 1 -> 3x3 kernel (low).
37
+
38
+ uniform sampler2D uRawDepth;
39
+ uniform vec2 uTexelSize;
40
+ uniform float uSpatialSigma2;
41
+
42
+ in vec2 vUv;
43
+ out vec4 fragColor;
44
+
45
+ void main() {
46
+ const float depthSigma2 = 0.01; // 0.1^2
47
+
48
+ float center = texture(uRawDepth, vUv).r;
49
+ float totalWeight = 1.0;
50
+ float totalDepth = center;
51
+
52
+ for (int dy = -BILATERAL_RADIUS; dy <= BILATERAL_RADIUS; dy++) {
53
+ for (int dx = -BILATERAL_RADIUS; dx <= BILATERAL_RADIUS; dx++) {
54
+ if (dx == 0 && dy == 0) continue;
55
+
56
+ vec2 offset = vec2(float(dx), float(dy)) * uTexelSize;
57
+ float neighbor = texture(uRawDepth, vUv + offset).r;
58
+
59
+ float spatialDist2 = float(dx * dx + dy * dy);
60
+ float depthDiff = neighbor - center;
61
+ float w = exp(-spatialDist2 / uSpatialSigma2 - (depthDiff * depthDiff) / depthSigma2);
62
+
63
+ totalWeight += w;
64
+ totalDepth += neighbor * w;
65
+ }
21
66
  }
22
- `,Gt=`#version 300 es
23
- precision highp float;
24
67
 
25
- // ---- Uniforms ----
68
+ fragColor = vec4(totalDepth / totalWeight, 0.0, 0.0, 1.0);
69
+ }
70
+ `,Te=`#version 300 es
71
+ precision highp float;
26
72
 
27
- /** Color video frame, uploaded from HTMLVideoElement. */
28
- uniform sampler2D uImage;
73
+ // ---- Uniforms ----
29
74
 
30
- /**
31
- * Single-channel depth map (R channel, 0=near, 1=far).
32
- * Pre-filtered with a bilateral filter on the CPU before upload,
33
- * so a single texture() read gives smooth, edge-preserving depth.
34
- * Uploaded as R8 format (auto-normalized to [0,1]).
35
- */
36
- uniform sampler2D uDepth;
75
+ /** Color video frame, uploaded from HTMLVideoElement. */
76
+ uniform sampler2D uImage;
37
77
 
38
- /**
39
- * Current parallax input from mouse or gyroscope.
40
- * Range [-1, 1] for both x (horizontal) and y (vertical).
41
- */
42
- uniform vec2 uOffset;
78
+ /**
79
+ * Single-channel depth map (R channel, 0=far, 1=near).
80
+ * Depth Anything v2 outputs higher=closer; no inversion is applied.
81
+ * Bilateral-filtered on the GPU via a dedicated render pass,
82
+ * so a single texture() read gives smooth, edge-preserving depth.
83
+ *
84
+ * DEPTH CONVENTION: 0.0 = farthest, 1.0 = nearest.
85
+ * See DEPTH_CONVENTION.md for the canonical reference.
86
+ */
87
+ uniform sampler2D uDepth;
43
88
 
44
- /** Parallax displacement magnitude in UV space (e.g. 0.05 = 5%). */
45
- uniform float uStrength;
89
+ /**
90
+ * Current parallax input from mouse or gyroscope.
91
+ * Range [-1, 1] for both x (horizontal) and y (vertical).
92
+ */
93
+ uniform vec2 uOffset;
46
94
 
47
- /** Whether to use POM ray-marching instead of basic displacement. */
48
- uniform bool uPomEnabled;
95
+ /** Parallax displacement magnitude in UV space (e.g. 0.05 = 5%). */
96
+ uniform float uStrength;
49
97
 
50
- /** Number of ray-march steps for POM (runtime-adjustable). */
51
- uniform int uPomSteps;
98
+ /** Whether to use POM ray-marching instead of basic displacement. */
99
+ uniform bool uPomEnabled;
52
100
 
53
- /** Smoothstep lower bound for depth contrast curve (depth-adaptive). */
54
- uniform float uContrastLow;
101
+ /** Number of ray-march steps for POM (runtime-adjustable). */
102
+ uniform int uPomSteps;
55
103
 
56
- /** Smoothstep upper bound for depth contrast curve (depth-adaptive). */
57
- uniform float uContrastHigh;
104
+ /** Smoothstep lower bound for depth contrast curve (depth-adaptive). */
105
+ uniform float uContrastLow;
58
106
 
59
- /** Y-axis displacement multiplier (depth-adaptive). */
60
- uniform float uVerticalReduction;
107
+ /** Smoothstep upper bound for depth contrast curve (depth-adaptive). */
108
+ uniform float uContrastHigh;
61
109
 
62
- /** Depth threshold where DOF blur ramp begins (depth-adaptive). */
63
- uniform float uDofStart;
110
+ /** Y-axis displacement multiplier (depth-adaptive). */
111
+ uniform float uVerticalReduction;
64
112
 
65
- /** Maximum DOF blur blend factor (depth-adaptive). */
66
- uniform float uDofStrength;
113
+ /** Depth threshold where DOF blur ramp begins (depth-adaptive). */
114
+ uniform float uDofStart;
67
115
 
68
- /**
69
- * Texel size for video/image texture (1.0 / videoResolution).
70
- * Used by the depth-of-field effect to sample neighboring pixels.
71
- */
72
- uniform vec2 uImageTexelSize;
116
+ /** Maximum DOF blur blend factor (depth-adaptive). */
117
+ uniform float uDofStrength;
73
118
 
74
- // ---- Varyings ----
119
+ /**
120
+ * Texel size for video/image texture (1.0 / videoResolution).
121
+ * Used by the depth-of-field effect to sample neighboring pixels.
122
+ */
123
+ uniform vec2 uImageTexelSize;
75
124
 
76
- /** Interpolated texture coordinates from vertex shader (cover-fit transformed). */
77
- in vec2 vUv;
125
+ /**
126
+ * Displacement curve LUT — 256×1 R8 texture.
127
+ * Maps depth (0=far, 1=near) → displacement intensity [0,1].
128
+ * Replaces the hardcoded (1.0 - depth) linear ramp.
129
+ * When not provided, falls back to the legacy linear ramp.
130
+ */
131
+ uniform sampler2D uDisplacementCurve;
78
132
 
79
- /** Screen-space UV [0,1] — always covers the full viewport. */
80
- in vec2 vScreenUv;
133
+ /**
134
+ * Blur curve LUT — 256×1 R8 texture.
135
+ * Maps depth → blur intensity [0,1].
136
+ * Replaces the hardcoded smoothstep(dofStart, 1.0, depth) * dofStrength ramp.
137
+ */
138
+ uniform sampler2D uBlurCurve;
81
139
 
82
- /** Fragment output color. */
83
- out vec4 fragColor;
140
+ /** Whether curve LUT textures are bound (0 = use legacy scalar math). */
141
+ uniform bool uCurvesEnabled;
84
142
 
85
- // ---- Helper functions ----
143
+ /** Maximum blur radius in UV space, from blur channel params. */
144
+ uniform float uBlurRadius;
86
145
 
87
- /**
88
- * Compute an edge fade factor that reduces displacement near UV
89
- * boundaries.
90
- */
91
- float edgeFade(vec2 uv) {
92
- float margin = uStrength * 1.5;
93
- float fadeX = smoothstep(0.0, margin, uv.x) * smoothstep(0.0, margin, 1.0 - uv.x);
94
- float fadeY = smoothstep(0.0, margin, uv.y) * smoothstep(0.0, margin, 1.0 - uv.y);
95
- return fadeX * fadeY;
96
- }
146
+ /** Per-frame depth offset for focal band shifting (0 = no shift). */
147
+ uniform float uFocalBandOffset;
97
148
 
98
- /**
99
- * Compute a subtle vignette darkening factor.
100
- */
101
- float vignette(vec2 uv) {
102
- float dist = length(uv - 0.5) * 1.4;
103
- return 1.0 - pow(dist, 2.5);
104
- }
149
+ /** Whether tilted focal plane blur is enabled (Scheimpflug simulation). */
150
+ uniform bool uTiltEnabled;
105
151
 
106
- // ---- Displacement functions ----
107
-
108
- /**
109
- * Basic UV displacement with edge fade.
110
- */
111
- vec2 basicDisplace(vec2 uv) {
112
- float depth = texture(uDepth, uv).r;
113
- depth = smoothstep(uContrastLow, uContrastHigh, depth);
114
- float displacement = (1.0 - depth) * uStrength;
115
- displacement *= edgeFade(uv);
116
- vec2 offset = uOffset * displacement;
117
- offset.y *= uVerticalReduction;
118
- return uv + offset;
152
+ /** Precomputed tan(fov/2) for pseudo-world reconstruction. */
153
+ uniform float uTiltHalfTanFov;
154
+
155
+ /** Blur ramp width in pseudo-world-space units. */
156
+ uniform float uTiltTransitionWidth;
157
+
158
+ /** Maximum blur intensity for tilted plane DOF (0-1). */
159
+ uniform float uTiltPeakIntensity;
160
+
161
+ /** Tilted focal plane normal vector (computed from pitch/yaw on CPU). */
162
+ uniform vec3 uTiltPlaneNormal;
163
+
164
+ /** Tilted focal plane distance constant: dot(normal, focalPoint). */
165
+ uniform float uTiltPlaneD;
166
+
167
+ /**
168
+ * Glow curve LUT — 256×1 R8 texture.
169
+ * Maps depth → glow intensity [0,1].
170
+ * Near-camera objects typically have high glow intensity.
171
+ */
172
+ uniform sampler2D uGlowCurve;
173
+
174
+ /** Whether glow is enabled (has a glow channel with a non-zero curve). */
175
+ uniform bool uGlowEnabled;
176
+
177
+ /** Glow tint color [r, g, b]. */
178
+ uniform vec3 uGlowColor;
179
+
180
+ /** Glow spread radius in UV space. */
181
+ uniform float uGlowRadius;
182
+
183
+ /** Glow edge softness — controls blending of the glow mask. */
184
+ uniform float uGlowSoftness;
185
+
186
+ /**
187
+ * Color-shift curve LUT — 256×1 R8 texture.
188
+ * Maps depth → color-shift intensity [0,1].
189
+ * At 0: no color adjustment. At 1: full hue/sat/brightness/tint shift.
190
+ */
191
+ uniform sampler2D uColorShiftCurve;
192
+
193
+ /** Whether color-shift is enabled (has a color-shift channel). */
194
+ uniform bool uColorShiftEnabled;
195
+
196
+ /** Hue rotation in radians (converted from degrees on CPU). */
197
+ uniform float uColorShiftHue;
198
+
199
+ /** Saturation multiplier at full intensity. */
200
+ uniform float uColorShiftSaturation;
201
+
202
+ /** Brightness multiplier at full intensity. */
203
+ uniform float uColorShiftBrightness;
204
+
205
+ /** Tint blend strength at full intensity (0-1). */
206
+ uniform float uColorShiftTintStrength;
207
+
208
+ /** Tint target color [r, g, b]. */
209
+ uniform vec3 uColorShiftTintColor;
210
+
211
+ // ---- Varyings ----
212
+
213
+ /** Interpolated texture coordinates from vertex shader (cover-fit transformed). */
214
+ in vec2 vUv;
215
+
216
+ /** Screen-space UV [0,1] -- always covers the full viewport. */
217
+ in vec2 vScreenUv;
218
+
219
+ /** Fragment output color. */
220
+ out vec4 fragColor;
221
+
222
+ // ---- Helper functions ----
223
+
224
+ /**
225
+ * Compute an edge fade factor that reduces displacement near UV
226
+ * boundaries.
227
+ */
228
+ float edgeFade(vec2 uv) {
229
+ float margin = uStrength * 1.5;
230
+ float fadeX = smoothstep(0.0, margin, uv.x) * smoothstep(0.0, margin, 1.0 - uv.x);
231
+ float fadeY = smoothstep(0.0, margin, uv.y) * smoothstep(0.0, margin, 1.0 - uv.y);
232
+ return fadeX * fadeY;
233
+ }
234
+
235
+ // ---- Color space helpers ----
236
+
237
+ /**
238
+ * RGB to HSV conversion.
239
+ * Returns vec3(hue [0-1], saturation [0-1], value [0-1]).
240
+ */
241
+ vec3 rgb2hsv(vec3 c) {
242
+ vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);
243
+ vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
244
+ vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
245
+ float d = q.x - min(q.w, q.y);
246
+ float e = 1.0e-10;
247
+ return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
248
+ }
249
+
250
+ /**
251
+ * HSV to RGB conversion.
252
+ * Input: vec3(hue [0-1], saturation [0-1], value [0-1]).
253
+ */
254
+ vec3 hsv2rgb(vec3 c) {
255
+ vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
256
+ vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
257
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
258
+ }
259
+
260
+ // ---- Displacement functions ----
261
+
262
+ /**
263
+ * Basic UV displacement with edge fade.
264
+ */
265
+ vec2 basicDisplace(vec2 uv) {
266
+ float depth = texture(uDepth, uv).r;
267
+ depth = smoothstep(uContrastLow, uContrastHigh, depth);
268
+
269
+ float displacement;
270
+ if (uCurvesEnabled) {
271
+ displacement = texture(uDisplacementCurve, vec2(depth, 0.5)).r * uStrength;
272
+ } else {
273
+ displacement = (1.0 - depth) * uStrength;
119
274
  }
120
275
 
121
- /**
122
- * Parallax Occlusion Mapping (POM) ray-marching displacement.
123
- */
124
- vec2 pomDisplace(vec2 uv) {
125
- float layerDepth = 1.0 / float(uPomSteps);
276
+ displacement *= edgeFade(uv);
277
+ vec2 offset = uOffset * displacement;
278
+ offset.y *= uVerticalReduction;
279
+ return uv + offset;
280
+ }
126
281
 
127
- vec2 scaledOffset = uOffset;
128
- scaledOffset.y *= uVerticalReduction;
282
+ /**
283
+ * Parallax Occlusion Mapping (POM) ray-marching displacement.
284
+ */
285
+ vec2 pomDisplace(vec2 uv) {
286
+ float layerDepth = 1.0 / float(uPomSteps);
129
287
 
130
- vec2 deltaUV = scaledOffset * uStrength / float(uPomSteps);
288
+ vec2 scaledOffset = uOffset;
289
+ scaledOffset.y *= uVerticalReduction;
131
290
 
132
- float currentLayerDepth = 0.0;
133
- vec2 currentUV = uv;
291
+ vec2 deltaUV = scaledOffset * uStrength / float(uPomSteps);
134
292
 
135
- float fade = edgeFade(uv);
293
+ float currentLayerDepth = 0.0;
294
+ vec2 currentUV = uv;
136
295
 
137
- for (int i = 0; i < MAX_POM_STEPS; i++) {
138
- if (i >= uPomSteps) break;
296
+ float fade = edgeFade(uv);
139
297
 
140
- float rawDepth = texture(uDepth, currentUV).r;
141
- rawDepth = smoothstep(uContrastLow, uContrastHigh, rawDepth);
142
- float depthAtUV = 1.0 - rawDepth;
298
+ for (int i = 0; i < MAX_POM_STEPS; i++) {
299
+ if (i >= uPomSteps) break;
143
300
 
144
- if (currentLayerDepth > depthAtUV) {
145
- vec2 prevUV = currentUV - deltaUV;
146
- float prevLayerDepth = currentLayerDepth - layerDepth;
147
- float prevRaw = texture(uDepth, prevUV).r;
148
- prevRaw = smoothstep(uContrastLow, uContrastHigh, prevRaw);
149
- float prevDepthAtUV = 1.0 - prevRaw;
150
-
151
- float afterDepth = depthAtUV - currentLayerDepth;
152
- float beforeDepth = prevDepthAtUV - prevLayerDepth;
153
- float t = afterDepth / (afterDepth - beforeDepth);
301
+ float rawDepth = texture(uDepth, currentUV).r;
302
+ rawDepth = smoothstep(uContrastLow, uContrastHigh, rawDepth);
303
+ float depthAtUV;
304
+ if (uCurvesEnabled) {
305
+ // Curve outputs displacement intensity (far=high, near=low with descending curve).
306
+ // POM needs a height value where 0=surface, 1=deep. The displacement intensity
307
+ // IS the height: far objects are "deep" (high displacement), near are "surface".
308
+ depthAtUV = texture(uDisplacementCurve, vec2(rawDepth, 0.5)).r;
309
+ } else {
310
+ depthAtUV = 1.0 - rawDepth;
311
+ }
154
312
 
155
- vec2 hitUV = mix(currentUV, prevUV, t);
156
- return mix(uv, hitUV, fade);
313
+ if (currentLayerDepth > depthAtUV) {
314
+ vec2 prevUV = currentUV - deltaUV;
315
+ float prevLayerDepth = currentLayerDepth - layerDepth;
316
+
317
+ float prevRaw = texture(uDepth, prevUV).r;
318
+ prevRaw = smoothstep(uContrastLow, uContrastHigh, prevRaw);
319
+ float prevDepthAtUV;
320
+ if (uCurvesEnabled) {
321
+ prevDepthAtUV = texture(uDisplacementCurve, vec2(prevRaw, 0.5)).r;
322
+ } else {
323
+ prevDepthAtUV = 1.0 - prevRaw;
157
324
  }
158
325
 
159
- currentUV += deltaUV;
160
- currentLayerDepth += layerDepth;
326
+ float afterDepth = depthAtUV - currentLayerDepth;
327
+ float beforeDepth = prevDepthAtUV - prevLayerDepth;
328
+ float t = afterDepth / (afterDepth - beforeDepth);
329
+
330
+ vec2 hitUV = mix(currentUV, prevUV, t);
331
+ return mix(uv, hitUV, fade);
161
332
  }
162
333
 
163
- return mix(uv, currentUV, fade);
334
+ currentUV += deltaUV;
335
+ currentLayerDepth += layerDepth;
164
336
  }
165
337
 
166
- // ---- Main ----
338
+ return mix(uv, currentUV, fade);
339
+ }
340
+
341
+ // ---- Main ----
342
+
343
+ void main() {
344
+ vec2 displaced = uPomEnabled ? pomDisplace(vUv) : basicDisplace(vUv);
345
+ displaced = clamp(displaced, vec2(0.0), vec2(1.0));
346
+
347
+ vec4 color = texture(uImage, displaced);
348
+
349
+ // Depth-of-field blur — three modes:
350
+ // 1. Tilted focal plane (Scheimpflug): 3D plane distance → CoC
351
+ // 2. Curve LUT: depth → LUT lookup → blur intensity
352
+ // 3. Legacy scalar: smoothstep ramp from dofStart
353
+ float dofDepth = texture(uDepth, displaced).r;
354
+ float blurIntensity;
355
+ if (uTiltEnabled) {
356
+ // Reconstruct pseudo-world position from depth + screen UV.
357
+ const float TILT_NEAR_Z = 0.5;
358
+ const float TILT_FAR_Z = 5.0;
359
+ float linearZ = mix(TILT_NEAR_Z, TILT_FAR_Z, dofDepth);
360
+ float aspect = uImageTexelSize.y / uImageTexelSize.x;
361
+ vec3 worldPos = vec3(
362
+ (vScreenUv.x - 0.5) * 2.0 * uTiltHalfTanFov * aspect * linearZ,
363
+ (vScreenUv.y - 0.5) * 2.0 * uTiltHalfTanFov * linearZ,
364
+ linearZ
365
+ );
366
+ float signedDist = dot(uTiltPlaneNormal, worldPos) - uTiltPlaneD;
367
+ blurIntensity = smoothstep(0.0, uTiltTransitionWidth, abs(signedDist)) * uTiltPeakIntensity;
368
+ } else if (uCurvesEnabled) {
369
+ float shiftedDepth = clamp(dofDepth + uFocalBandOffset, 0.0, 1.0);
370
+ blurIntensity = texture(uBlurCurve, vec2(shiftedDepth, 0.5)).r;
371
+ } else {
372
+ blurIntensity = smoothstep(uDofStart, 1.0, dofDepth) * uDofStrength;
373
+ }
167
374
 
168
- void main() {
169
- vec2 displaced = uPomEnabled ? pomDisplace(vUv) : basicDisplace(vUv);
170
- displaced = clamp(displaced, vec2(0.0), vec2(1.0));
375
+ if (blurIntensity > 0.001) {
376
+ float radius = blurIntensity * uBlurRadius;
377
+
378
+ // 13-tap Poisson disk — perceptually uniform distribution.
379
+ // Offsets are pre-normalized to unit disk; scaled by radius at runtime.
380
+ const vec2 poissonDisk[12] = vec2[12](
381
+ vec2(-0.326, -0.406),
382
+ vec2(-0.840, -0.074),
383
+ vec2(-0.696, 0.457),
384
+ vec2(-0.203, 0.621),
385
+ vec2( 0.962, -0.195),
386
+ vec2( 0.473, -0.480),
387
+ vec2( 0.519, 0.767),
388
+ vec2( 0.185, -0.893),
389
+ vec2( 0.507, 0.064),
390
+ vec2( 0.896, 0.412),
391
+ vec2(-0.322, -0.933),
392
+ vec2(-0.792, -0.598)
393
+ );
171
394
 
172
- vec4 color = texture(uImage, displaced);
395
+ vec4 blurAccum = color; // center tap (weight 1)
396
+ for (int i = 0; i < 12; i++) {
397
+ vec2 sampleUV = displaced + poissonDisk[i] * radius;
398
+ sampleUV = clamp(sampleUV, vec2(0.0), vec2(1.0));
399
+ blurAccum += texture(uImage, sampleUV);
400
+ }
401
+ blurAccum /= 13.0;
173
402
 
174
- // Depth-of-field hint
175
- float dofDepth = texture(uDepth, displaced).r;
176
- float dof = smoothstep(uDofStart, 1.0, dofDepth) * uDofStrength;
177
- vec4 blurred = (
178
- texture(uImage, displaced + vec2( uImageTexelSize.x, 0.0)) +
179
- texture(uImage, displaced + vec2(-uImageTexelSize.x, 0.0)) +
180
- texture(uImage, displaced + vec2( 0.0, uImageTexelSize.y)) +
181
- texture(uImage, displaced + vec2( 0.0, -uImageTexelSize.y))
182
- ) * 0.25;
183
- color = mix(color, blurred, dof);
403
+ color = mix(color, blurAccum, blurIntensity);
404
+ }
405
+
406
+ // ---- Glow pass ----
407
+ // Samples a glow curve LUT to determine per-pixel glow intensity,
408
+ // then spreads the glow to neighboring pixels via a small blur kernel.
409
+ // The glow is additive: it brightens pixels without replacing color.
410
+ if (uGlowEnabled) {
411
+ float glowDepth = texture(uDepth, displaced).r;
412
+ float glowMask = texture(uGlowCurve, vec2(glowDepth, 0.5)).r;
413
+
414
+ // Spread glow to neighbors for a soft halo effect
415
+ float spreadMask = 0.0;
416
+ float spreadSamples = 0.0;
417
+ for (int x = -3; x <= 3; x++) {
418
+ for (int y = -3; y <= 3; y++) {
419
+ vec2 sampleOffset = vec2(float(x), float(y)) * uImageTexelSize * uGlowRadius * 100.0;
420
+ float d = texture(uDepth, displaced + sampleOffset).r;
421
+ spreadMask += texture(uGlowCurve, vec2(d, 0.5)).r;
422
+ spreadSamples += 1.0;
423
+ }
424
+ }
425
+ spreadMask /= spreadSamples;
426
+
427
+ // Combine direct glow mask with spread, apply softness
428
+ float finalGlow = mix(spreadMask * 0.5, glowMask, uGlowSoftness);
429
+ color.rgb += uGlowColor * finalGlow;
430
+ }
431
+
432
+ // ---- Color-shift pass ----
433
+ // Applies depth-driven hue rotation, saturation/brightness adjustment,
434
+ // and optional tint blending. Intensity is driven by the color-shift
435
+ // curve LUT: 0 = no effect, 1 = full effect at that depth.
436
+ if (uColorShiftEnabled) {
437
+ float csDepth = texture(uDepth, displaced).r;
438
+ float csIntensity = texture(uColorShiftCurve, vec2(csDepth, 0.5)).r;
439
+
440
+ if (csIntensity > 0.001) {
441
+ vec3 hsv = rgb2hsv(color.rgb);
442
+
443
+ // Hue rotation (uColorShiftHue is in radians, convert to 0-1 hue space)
444
+ float hueOffset = uColorShiftHue / 6.283185; // 2*PI
445
+ hsv.x = fract(hsv.x + hueOffset * csIntensity);
446
+
447
+ // Saturation: lerp multiplier from 1.0 toward target
448
+ float satMul = mix(1.0, uColorShiftSaturation, csIntensity);
449
+ hsv.y = clamp(hsv.y * satMul, 0.0, 1.0);
450
+
451
+ // Brightness: lerp multiplier from 1.0 toward target
452
+ float brightMul = mix(1.0, uColorShiftBrightness, csIntensity);
453
+ hsv.z = clamp(hsv.z * brightMul, 0.0, 1.0);
454
+
455
+ vec3 shifted = hsv2rgb(hsv);
184
456
 
185
- // Vignette (screen-space, not texture-space)
186
- color.rgb *= vignette(vScreenUv);
457
+ // Tint: blend toward tint color
458
+ float tintAmount = uColorShiftTintStrength * csIntensity;
459
+ shifted = mix(shifted, uColorShiftTintColor, tintAmount);
187
460
 
188
- fragColor = color;
461
+ color.rgb = shifted;
462
+ }
189
463
  }
190
- `,q={contrastLow:.05,contrastHigh:.95,verticalReduction:.5,dofStart:.6,dofStrength:.4};function Tt(r,t,e){const i=r.createShader(t);if(!i)throw new Error("Failed to create shader.");if(r.shaderSource(i,e),r.compileShader(i),!r.getShaderParameter(i,r.COMPILE_STATUS)){const o=r.getShaderInfoLog(i)??"";throw r.deleteShader(i),new Error(`Shader compilation failed:
191
- ${o}`)}return i}function jt(r,t,e){const i=r.createProgram();if(!i)throw new Error("Failed to create program.");if(r.attachShader(i,t),r.attachShader(i,e),r.linkProgram(i),!r.getProgramParameter(i,r.LINK_STATUS)){const o=r.getProgramInfoLog(i)??"";throw r.deleteProgram(i),new Error(`Program linking failed:
192
- ${o}`)}return r.detachShader(i,t),r.detachShader(i,e),r.deleteShader(t),r.deleteShader(e),i}function Yt(r,t){return{uImage:r.getUniformLocation(t,"uImage"),uDepth:r.getUniformLocation(t,"uDepth"),uOffset:r.getUniformLocation(t,"uOffset"),uStrength:r.getUniformLocation(t,"uStrength"),uPomEnabled:r.getUniformLocation(t,"uPomEnabled"),uPomSteps:r.getUniformLocation(t,"uPomSteps"),uContrastLow:r.getUniformLocation(t,"uContrastLow"),uContrastHigh:r.getUniformLocation(t,"uContrastHigh"),uVerticalReduction:r.getUniformLocation(t,"uVerticalReduction"),uDofStart:r.getUniformLocation(t,"uDofStart"),uDofStrength:r.getUniformLocation(t,"uDofStrength"),uImageTexelSize:r.getUniformLocation(t,"uImageTexelSize"),uUvOffset:r.getUniformLocation(t,"uUvOffset"),uUvScale:r.getUniformLocation(t,"uUvScale")}}class Z{static RESIZE_DEBOUNCE_MS=100;static MAX_POM_STEPS=64;canvas;gl=null;program=null;uniforms=null;vao=null;videoTexture=null;depthTexture=null;container;depthWidth=0;depthHeight=0;videoAspect=1.7777777777777777;readDepth=null;readInput=null;playbackVideo=null;onVideoFrame=null;animationFrameHandle=0;rvfcHandle=0;rvfcSupported=!1;resizeObserver=null;resizeTimer=null;uvOffset=[0,0];uvScale=[1,1];config;constructor(t,e){this.container=t,this.config={parallaxStrength:e.parallaxStrength,pomEnabled:e.pomEnabled,pomSteps:e.pomSteps,overscanPadding:e.overscanPadding,contrastLow:e.contrastLow??q.contrastLow,contrastHigh:e.contrastHigh??q.contrastHigh,verticalReduction:e.verticalReduction??q.verticalReduction,dofStart:e.dofStart??q.dofStart,dofStrength:e.dofStrength??q.dofStrength},this.canvas=document.createElement("canvas");const i=this.canvas.getContext("webgl2",{antialias:!1,alpha:!1,desynchronized:!0,powerPreference:"high-performance"});if(!i)throw new Error("WebGL 2 is not supported.");this.gl=i,"drawingBufferColorSpace"in i&&(i.drawingBufferColorSpace="srgb"),i.clearColor(0,0,0,1),i.pixelStorei(i.UNPACK_FLIP_Y_WEBGL,!0),this.container.appendChild(this.canvas),this.initGPUResources(),this.setupResizeHandling(),this.canvas.addEventListener("webglcontextlost",this.handleContextLost),this.canvas.addEventListener("webglcontextrestored",this.handleContextRestored)}initialize(t,e,i){const o=this.gl;o&&(this.disposeTextures(),this.videoAspect=t.videoWidth/t.videoHeight,this.depthWidth=e,this.depthHeight=i,this.videoTexture=o.createTexture(),o.activeTexture(o.TEXTURE0),o.bindTexture(o.TEXTURE_2D,this.videoTexture),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MIN_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MAG_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_S,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_T,o.CLAMP_TO_EDGE),this.depthTexture=o.createTexture(),o.activeTexture(o.TEXTURE1),o.bindTexture(o.TEXTURE_2D,this.depthTexture),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MIN_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_MAG_FILTER,o.LINEAR),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_S,o.CLAMP_TO_EDGE),o.texParameteri(o.TEXTURE_2D,o.TEXTURE_WRAP_T,o.CLAMP_TO_EDGE),o.texStorage2D(o.TEXTURE_2D,1,o.R8,e,i),this.program&&this.uniforms&&(o.useProgram(this.program),o.uniform1i(this.uniforms.uImage,0),o.uniform1i(this.uniforms.uDepth,1),o.uniform1f(this.uniforms.uStrength,this.config.parallaxStrength),o.uniform1i(this.uniforms.uPomEnabled,this.config.pomEnabled?1:0),o.uniform1i(this.uniforms.uPomSteps,this.config.pomSteps),o.uniform1f(this.uniforms.uContrastLow,this.config.contrastLow),o.uniform1f(this.uniforms.uContrastHigh,this.config.contrastHigh),o.uniform1f(this.uniforms.uVerticalReduction,this.config.verticalReduction),o.uniform1f(this.uniforms.uDofStart,this.config.dofStart),o.uniform1f(this.uniforms.uDofStrength,this.config.dofStrength),o.uniform2f(this.uniforms.uImageTexelSize,1/t.videoWidth,1/t.videoHeight)),this.recalculateViewportLayout())}start(t,e,i,o){this.stop(),this.playbackVideo=t,this.readDepth=e,this.readInput=i,this.onVideoFrame=o??null,this.rvfcSupported=Z.isRVFCSupported(),this.rvfcSupported&&(this.rvfcHandle=t.requestVideoFrameCallback(this.videoFrameLoop)),this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop)}stop(){this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0),this.rvfcHandle&&this.playbackVideo&&(this.playbackVideo.cancelVideoFrameCallback(this.rvfcHandle),this.rvfcHandle=0),this.playbackVideo=null,this.readDepth=null,this.readInput=null,this.onVideoFrame=null,this.rvfcSupported=!1}dispose(){this.stop(),this.disposeTextures(),this.disposeGPUResources(),this.canvas.removeEventListener("webglcontextlost",this.handleContextLost),this.canvas.removeEventListener("webglcontextrestored",this.handleContextRestored),this.gl&&(this.gl.getExtension("WEBGL_lose_context")?.loseContext(),this.gl=null),this.canvas.remove(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),window.removeEventListener("resize",this.scheduleResizeRecalculate),this.resizeTimer!==null&&(window.clearTimeout(this.resizeTimer),this.resizeTimer=null)}initGPUResources(){const t=this.gl;if(!t)return;const e=Gt.replace("#version 300 es",`#version 300 es
193
- #define MAX_POM_STEPS ${Z.MAX_POM_STEPS}`),i=Tt(t,t.VERTEX_SHADER,Wt),o=Tt(t,t.FRAGMENT_SHADER,e);this.program=jt(t,i,o),this.uniforms=Yt(t,this.program);const n=new Float32Array([-1,-1,1,-1,-1,1,1,1]);this.vao=t.createVertexArray(),t.bindVertexArray(this.vao);const s=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,s),t.bufferData(t.ARRAY_BUFFER,n,t.STATIC_DRAW);const h=t.getAttribLocation(this.program,"aPosition");t.enableVertexAttribArray(h),t.vertexAttribPointer(h,2,t.FLOAT,!1,0,0),t.bindVertexArray(null),t.disable(t.DEPTH_TEST)}static isRVFCSupported(){return"requestVideoFrameCallback"in HTMLVideoElement.prototype}videoFrameLoop=(t,e)=>{const i=this.playbackVideo;if(!i)return;this.rvfcHandle=i.requestVideoFrameCallback(this.videoFrameLoop);const o=e.mediaTime??i.currentTime;this.updateDepthTexture(o),this.onVideoFrame&&this.onVideoFrame(o,e.presentedFrames??0)};renderLoop=()=>{this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop);const t=this.gl,e=this.playbackVideo;if(!(!t||!this.program||!this.uniforms||!this.vao)&&!(!e||e.readyState<HTMLMediaElement.HAVE_CURRENT_DATA)){if(t.useProgram(this.program),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.videoTexture),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,e),this.rvfcSupported||this.updateDepthTexture(e.currentTime),this.readInput){const i=this.readInput();t.uniform2f(this.uniforms.uOffset,-i.x,i.y)}t.bindVertexArray(this.vao),t.drawArrays(t.TRIANGLE_STRIP,0,4)}};updateDepthTexture(t){const e=this.gl;if(!e||!this.readDepth||!this.depthTexture)return;const i=this.readDepth(t);e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.depthTexture),e.texSubImage2D(e.TEXTURE_2D,0,0,0,this.depthWidth,this.depthHeight,e.RED,e.UNSIGNED_BYTE,i)}setupResizeHandling(){typeof ResizeObserver<"u"&&(this.resizeObserver=new ResizeObserver(()=>{this.scheduleResizeRecalculate()}),this.resizeObserver.observe(this.container)),window.addEventListener("resize",this.scheduleResizeRecalculate),this.recalculateViewportLayout()}scheduleResizeRecalculate=()=>{this.resizeTimer!==null&&window.clearTimeout(this.resizeTimer),this.resizeTimer=window.setTimeout(()=>{this.resizeTimer=null,this.recalculateViewportLayout()},Z.RESIZE_DEBOUNCE_MS)};recalculateViewportLayout(){const t=this.gl;if(!t)return;const{width:e,height:i}=this.getViewportSize(),o=Math.min(window.devicePixelRatio,2),n=Math.round(e*o),s=Math.round(i*o);(this.canvas.width!==n||this.canvas.height!==s)&&(this.canvas.width=n,this.canvas.height=s,t.viewport(0,0,n,s));const h=e/i,a=this.config.parallaxStrength+this.config.overscanPadding;let l=1,u=1;h>this.videoAspect?u=this.videoAspect/h:l=h/this.videoAspect;const f=1+a*2;l/=f,u/=f,this.uvOffset=[(1-l)/2,(1-u)/2],this.uvScale=[l,u],this.program&&this.uniforms&&(t.useProgram(this.program),t.uniform2f(this.uniforms.uUvOffset,this.uvOffset[0],this.uvOffset[1]),t.uniform2f(this.uniforms.uUvScale,this.uvScale[0],this.uvScale[1]))}getViewportSize(){const t=Math.max(1,Math.round(this.container.clientWidth||window.innerWidth)),e=Math.max(1,Math.round(this.container.clientHeight||window.innerHeight));return{width:t,height:e}}handleContextLost=t=>{t.preventDefault(),this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0)};handleContextRestored=()=>{const t=this.canvas.getContext("webgl2");t&&(this.gl=t,t.clearColor(0,0,0,1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this.initGPUResources(),this.playbackVideo&&this.depthWidth>0&&this.initialize(this.playbackVideo,this.depthWidth,this.depthHeight),this.playbackVideo&&(this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop)))};disposeTextures(){const t=this.gl;t&&(this.videoTexture&&(t.deleteTexture(this.videoTexture),this.videoTexture=null),this.depthTexture&&(t.deleteTexture(this.depthTexture),this.depthTexture=null))}disposeGPUResources(){const t=this.gl;t&&(this.program&&(t.deleteProgram(this.program),this.program=null),this.vao&&(t.deleteVertexArray(this.vao),this.vao=null),this.uniforms=null)}}class Et{abortController=null;initialized=!1;initializing=!1;element;constructor(t){this.element=t}onConnected(){this.element.setupShadowDOM(),this.tryInit()}onDisconnected(){this.cancelInit(),this.element.doDispose(),this.initialized=!1}onAttributeChanged(t,e,i){this.element.reinitAttributes.includes(t)&&e!==i&&(this.initialized?(this.cancelInit(),this.element.doDispose(),this.initialized=!1,this.element.setupShadowDOM(),this.tryInit()):this.initializing||this.tryInit())}get isInitialized(){return this.initialized}markInitialized(){this.initialized=!0,this.initializing=!1}async tryInit(){if(this.initializing)return;const t=this.element;if(!t.isConnected)return;for(const i of t.reinitAttributes)if(!t.getAttribute(i))return;this.cancelInit();const e=new AbortController;this.abortController=e,this.initializing=!0;try{if(await t.doInit(e.signal),e.signal.aborted){this.initializing=!1;return}}catch{this.initializing=!1}}cancelInit(){this.abortController?.abort(),this.abortController=null,this.initializing=!1}}const H={parallaxX:.4,parallaxY:1,parallaxMax:30,overscan:.05,autoplay:!0,loop:!0,muted:!0};let qt=class Ct{constructor(t,e=.08,i=.06){this.host=t,this.lerpFactor=e,this.motionLerpFactor=i,this.host.addEventListener("mousemove",this.handleMouseMove),this.host.addEventListener("mouseleave",this.resetPointerTarget),this.host.addEventListener("touchstart",this.handleTouchStart,{passive:!0}),this.host.addEventListener("touchmove",this.handleTouchMove,{passive:!0}),this.host.addEventListener("touchend",this.handleTouchEnd,{passive:!0}),this.host.addEventListener("touchcancel",this.handleTouchEnd,{passive:!0})}pointerTarget={x:0,y:0};motionTarget={x:0,y:0};smoothedOutput={x:0,y:0};usingMotionInput=!1;motionListenerAttached=!1;motionRequested=!1;touchActive=!1;touchAnchorX=0;touchAnchorY=0;lerpFactor;motionLerpFactor;static TOUCH_DRAG_RANGE=100;update(){const t=this.touchActive?this.pointerTarget:this.usingMotionInput?this.motionTarget:this.pointerTarget,e=this.usingMotionInput&&!this.touchActive?this.motionLerpFactor:this.lerpFactor;return this.smoothedOutput.x=ot(this.smoothedOutput.x,t.x,e),this.smoothedOutput.y=ot(this.smoothedOutput.y,t.y,e),this.smoothedOutput}dispose(){this.host.removeEventListener("mousemove",this.handleMouseMove),this.host.removeEventListener("mouseleave",this.resetPointerTarget),this.host.removeEventListener("touchstart",this.handleTouchStart),this.host.removeEventListener("touchmove",this.handleTouchMove),this.host.removeEventListener("touchend",this.handleTouchEnd),this.host.removeEventListener("touchcancel",this.handleTouchEnd),this.motionListenerAttached&&(window.removeEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!1)}handleMouseMove=t=>{const e=this.host.getBoundingClientRect(),i=(t.clientX-e.left)/e.width*2-1,o=(t.clientY-e.top)/e.height*2-1;this.pointerTarget.x=N(i,-1,1),this.pointerTarget.y=N(o,-1,1)};resetPointerTarget=()=>{this.pointerTarget.x=0,this.pointerTarget.y=0};handleTouchStart=t=>{const e=t.touches[0];e&&(this.touchActive=!0,this.touchAnchorX=e.clientX,this.touchAnchorY=e.clientY,this.pointerTarget.x=0,this.pointerTarget.y=0,this.motionRequested||(this.motionRequested=!0,this.requestMotionPermission()))};handleTouchMove=t=>{const e=t.touches[0];if(!e)return;const i=e.clientX-this.touchAnchorX,o=e.clientY-this.touchAnchorY,n=Ct.TOUCH_DRAG_RANGE;this.pointerTarget.x=N(i/n,-1,1),this.pointerTarget.y=N(o/n,-1,1)};handleTouchEnd=()=>{this.touchActive=!1,this.pointerTarget.x=0,this.pointerTarget.y=0};async requestMotionPermission(){if(typeof DeviceOrientationEvent>"u")return;const t=DeviceOrientationEvent;if(typeof t.requestPermission=="function")try{if(await t.requestPermission()!=="granted")return}catch{return}this.motionListenerAttached||(window.addEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!0),this.usingMotionInput=!0}handleDeviceOrientation=t=>{const e=N((t.gamma??0)/45,-1,1),i=N((t.beta??0)/45,-1,1);this.motionTarget.x=ot(this.motionTarget.x,e,this.motionLerpFactor),this.motionTarget.y=ot(this.motionTarget.y,i,this.motionLerpFactor)}};class rt extends HTMLElement{static TAG_NAME="layershift-parallax";static get observedAttributes(){return["src","depth-src","depth-meta","parallax-x","parallax-y","parallax-max","layers","overscan","autoplay","loop","muted"]}reinitAttributes=["src","depth-src","depth-meta"];shadow;container=null;renderer=null;inputHandler=null;depthWorker=null;video=null;loopCount=0;lifecycle;constructor(){super(),this.shadow=this.attachShadow({mode:"open"}),this.lifecycle=new Et(this)}getAttrFloat(t,e){const i=this.getAttribute(t);if(i===null)return e;const o=parseFloat(i);return Number.isFinite(o)?o:e}getAttrBool(t,e){if(!this.hasAttribute(t))return e;const i=this.getAttribute(t);return!(i==="false"||i==="0")}get parallaxX(){return this.getAttrFloat("parallax-x",H.parallaxX)}get parallaxY(){return this.getAttrFloat("parallax-y",H.parallaxY)}get parallaxMax(){return this.getAttrFloat("parallax-max",H.parallaxMax)}get overscan(){return this.getAttrFloat("overscan",H.overscan)}get shouldAutoplay(){return this.getAttrBool("autoplay",H.autoplay)}get shouldLoop(){return this.getAttrBool("loop",H.loop)}get shouldMute(){return this.getAttrBool("muted",H.muted)}emit(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}attachVideoEventListeners(t){t.addEventListener("play",()=>{this.emit("layershift-parallax:play",{currentTime:t.currentTime})}),t.addEventListener("pause",()=>{this.emit("layershift-parallax:pause",{currentTime:t.currentTime})}),t.addEventListener("ended",()=>{t.loop&&(this.loopCount+=1,this.emit("layershift-parallax:loop",{loopCount:this.loopCount}))})}connectedCallback(){this.lifecycle.onConnected()}disconnectedCallback(){this.lifecycle.onDisconnected()}attributeChangedCallback(t,e,i){this.lifecycle.onAttributeChanged(t,e,i)}setupShadowDOM(){this.shadow.innerHTML="";const t=document.createElement("style");t.textContent=`
464
+
465
+ fragColor = color;
466
+ }
467
+ `,I={contrastLow:.05,contrastHigh:.95,verticalReduction:.5,dofStart:.6,dofStrength:.4,blurRadius:.01,glowColor:[1,.95,.85],glowRadius:.02,glowSoftness:.6,tiltEnabled:!1,tiltHalfTanFov:Math.tan(50*Math.PI/360),tiltTransitionWidth:.3*4.5,tiltPeakIntensity:.8},Ee=["uRawDepth","uTexelSize","uSpatialSigma2"],be={2:2.25,1:.5625};function ye(o,t){const e=xe.replace("#version 300 es",`#version 300 es
468
+ #define BILATERAL_RADIUS ${t}`),n=z(o,o.VERTEX_SHADER,ve),i=z(o,o.FRAGMENT_SHADER,e),r=Et(o,n,i),s=bt(o,r,Ee),a=be[t]??2.25;let l=null;const u={name:"bilateral-filter",program:r,uniforms:s,fbo:null,outputs:[],width:0,height:0,resize(h,f,c){},initFBO(h,f,c,E){l&&h.deleteFramebuffer(l),u.width=c,u.height=E,l=h.createFramebuffer(),u.fbo=l,h.bindFramebuffer(h.FRAMEBUFFER,l),h.framebufferTexture2D(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0,h.TEXTURE_2D,f,0),h.bindFramebuffer(h.FRAMEBUFFER,null),h.useProgram(r),h.uniform1i(s.uRawDepth,2),h.uniform2f(s.uTexelSize,1/c,1/E),h.uniform1f(s.uSpatialSigma2,a)},execute(h,f,c,E,v,p,P,x){l&&(h.activeTexture(h.TEXTURE2),h.bindTexture(h.TEXTURE_2D,c),h.texSubImage2D(h.TEXTURE_2D,0,0,0,v,p,h.RED,h.UNSIGNED_BYTE,E),h.bindFramebuffer(h.FRAMEBUFFER,l),h.viewport(0,0,v,p),h.useProgram(r),h.bindVertexArray(f),h.drawArrays(h.TRIANGLE_STRIP,0,4),h.bindFramebuffer(h.FRAMEBUFFER,null),h.viewport(0,0,P,x))},dispose(h){l&&(h.deleteFramebuffer(l),l=null,u.fbo=null),h.deleteProgram(r)}};return u}const Se=["uImage","uDepth","uOffset","uStrength","uPomEnabled","uPomSteps","uContrastLow","uContrastHigh","uVerticalReduction","uDofStart","uDofStrength","uImageTexelSize","uUvOffset","uUvScale","uDisplacementCurve","uBlurCurve","uCurvesEnabled","uBlurRadius","uFocalBandOffset","uGlowCurve","uGlowEnabled","uGlowColor","uGlowRadius","uGlowSoftness","uColorShiftCurve","uColorShiftEnabled","uColorShiftHue","uColorShiftSaturation","uColorShiftBrightness","uColorShiftTintStrength","uColorShiftTintColor","uTiltEnabled","uTiltHalfTanFov","uTiltTransitionWidth","uTiltPeakIntensity","uTiltPlaneNormal","uTiltPlaneD"],Ae=64;function we(o){const t=Te.replace("#version 300 es",`#version 300 es
469
+ #define MAX_POM_STEPS ${Ae}`),e=z(o,o.VERTEX_SHADER,ge),n=z(o,o.FRAGMENT_SHADER,t),i=Et(o,e,n),r=bt(o,i,Se);return{name:"depth-effect",program:i,uniforms:r,setStaticUniforms(s,a,l,u){s.useProgram(i),s.uniform1i(r.uImage,0),s.uniform1i(r.uDepth,1),s.uniform1f(r.uStrength,a.parallaxStrength),s.uniform1i(r.uPomEnabled,a.pomEnabled?1:0),s.uniform1i(r.uPomSteps,a.pomSteps),s.uniform1f(r.uContrastLow,a.contrastLow),s.uniform1f(r.uContrastHigh,a.contrastHigh),s.uniform1f(r.uVerticalReduction,a.verticalReduction),s.uniform1f(r.uDofStart,a.dofStart),s.uniform1f(r.uDofStrength,a.dofStrength),s.uniform2f(r.uImageTexelSize,1/l,1/u),s.uniform1i(r.uDisplacementCurve,3),s.uniform1i(r.uBlurCurve,4),s.uniform1i(r.uCurvesEnabled,0),s.uniform1f(r.uBlurRadius,a.blurRadius),s.uniform1f(r.uFocalBandOffset,0),s.uniform1i(r.uGlowCurve,5),s.uniform1i(r.uGlowEnabled,0),s.uniform3f(r.uGlowColor,a.glowColor[0],a.glowColor[1],a.glowColor[2]),s.uniform1f(r.uGlowRadius,a.glowRadius),s.uniform1f(r.uGlowSoftness,a.glowSoftness),s.uniform1i(r.uColorShiftCurve,6),s.uniform1i(r.uColorShiftEnabled,0),s.uniform1f(r.uColorShiftHue,0),s.uniform1f(r.uColorShiftSaturation,1),s.uniform1f(r.uColorShiftBrightness,1),s.uniform1f(r.uColorShiftTintStrength,0),s.uniform3f(r.uColorShiftTintColor,.7,.8,.9),s.uniform1i(r.uTiltEnabled,a.tiltEnabled?1:0),s.uniform1f(r.uTiltHalfTanFov,a.tiltHalfTanFov),s.uniform1f(r.uTiltTransitionWidth,a.tiltTransitionWidth),s.uniform1f(r.uTiltPeakIntensity,a.tiltPeakIntensity),s.uniform3f(r.uTiltPlaneNormal,0,0,1),s.uniform1f(r.uTiltPlaneD,2.75)},updateUvTransform(s,a,l){s.useProgram(i),s.uniform2f(r.uUvOffset,a[0],a[1]),s.uniform2f(r.uUvScale,l[0],l[1])},dispose(s){s.deleteProgram(i)}}}class Re extends at{gl=null;quadVao=null;bilateralPass=null;effectPass=null;textures=new Mt;videoSlot;filteredDepthSlot;rawDepthSlot;displacementLutSlot;blurLutSlot;glowLutSlot;colorShiftLutSlot;config;constructor(t,e){super(t),this.videoSlot=this.textures.register("video"),this.filteredDepthSlot=this.textures.register("filteredDepth"),this.rawDepthSlot=this.textures.register("rawDepth"),this.displacementLutSlot=this.textures.register("displacementLut"),this.blurLutSlot=this.textures.register("blurLut"),this.glowLutSlot=this.textures.register("glowLut"),this.colorShiftLutSlot=this.textures.register("colorShiftLut"),this.config={parallaxStrength:e.parallaxStrength,pomEnabled:e.pomEnabled,pomSteps:e.pomSteps,overscanPadding:e.overscanPadding,contrastLow:e.contrastLow??I.contrastLow,contrastHigh:e.contrastHigh??I.contrastHigh,verticalReduction:e.verticalReduction??I.verticalReduction,dofStart:e.dofStart??I.dofStart,dofStrength:e.dofStrength??I.dofStrength,blurRadius:e.blurRadius??I.blurRadius,glowColor:e.glowColor??[...I.glowColor],glowRadius:e.glowRadius??I.glowRadius,glowSoftness:e.glowSoftness??I.glowSoftness,tiltEnabled:e.tiltEnabled??I.tiltEnabled,tiltHalfTanFov:e.tiltHalfTanFov??I.tiltHalfTanFov,tiltTransitionWidth:e.tiltTransitionWidth??I.tiltTransitionWidth,tiltPeakIntensity:e.tiltPeakIntensity??I.tiltPeakIntensity};const n=this.canvas.getContext("webgl2",{antialias:!1,alpha:!1,desynchronized:!0,powerPreference:"high-performance"});if(!n)throw new Error("WebGL 2 is not supported.");this.gl=n,this.qualityParams=It(n,e.quality),"drawingBufferColorSpace"in n&&(n.drawingBufferColorSpace="srgb"),n.clearColor(0,0,0,1),n.pixelStorei(n.UNPACK_FLIP_Y_WEBGL,!0),this.initGPUResources(),this.setupResizeHandling()}initialize(t,e,n){const i=this.gl;i&&(this.disposeTextures(),this.isCameraSource=t.type==="camera",this.videoAspect=t.width/t.height,this.clampDepthDimensions(e,n,this.qualityParams.depthMaxDim),this.videoSlot.texture=i.createTexture(),i.activeTexture(i.TEXTURE0+this.videoSlot.unit),i.bindTexture(i.TEXTURE_2D,this.videoSlot.texture),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),this.rawDepthSlot.texture=i.createTexture(),i.activeTexture(i.TEXTURE0+this.rawDepthSlot.unit),i.bindTexture(i.TEXTURE_2D,this.rawDepthSlot.texture),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.texStorage2D(i.TEXTURE_2D,1,i.R8,e,n),this.filteredDepthSlot.texture=i.createTexture(),i.activeTexture(i.TEXTURE0+this.filteredDepthSlot.unit),i.bindTexture(i.TEXTURE_2D,this.filteredDepthSlot.texture),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.texStorage2D(i.TEXTURE_2D,1,i.R8,e,n),this.bilateralPass&&this.filteredDepthSlot.texture&&this.bilateralPass.initFBO(i,this.filteredDepthSlot.texture,e,n),this.effectPass&&this.effectPass.setStaticUniforms(i,this.config,t.width,t.height),this.recalculateViewportLayout())}updateConfig(t){const e=this.gl;if(!e||!this.effectPass)return;t.parallaxStrength!==void 0&&(this.config.parallaxStrength=t.parallaxStrength),t.pomEnabled!==void 0&&(this.config.pomEnabled=t.pomEnabled),t.pomSteps!==void 0&&(this.config.pomSteps=t.pomSteps),t.overscanPadding!==void 0&&(this.config.overscanPadding=t.overscanPadding),t.contrastLow!==void 0&&(this.config.contrastLow=t.contrastLow),t.contrastHigh!==void 0&&(this.config.contrastHigh=t.contrastHigh),t.verticalReduction!==void 0&&(this.config.verticalReduction=t.verticalReduction),t.dofStart!==void 0&&(this.config.dofStart=t.dofStart),t.dofStrength!==void 0&&(this.config.dofStrength=t.dofStrength),t.blurRadius!==void 0&&(this.config.blurRadius=t.blurRadius),t.glowColor!==void 0&&(this.config.glowColor=t.glowColor),t.glowRadius!==void 0&&(this.config.glowRadius=t.glowRadius),t.glowSoftness!==void 0&&(this.config.glowSoftness=t.glowSoftness),t.tiltEnabled!==void 0&&(this.config.tiltEnabled=t.tiltEnabled),t.tiltHalfTanFov!==void 0&&(this.config.tiltHalfTanFov=t.tiltHalfTanFov),t.tiltTransitionWidth!==void 0&&(this.config.tiltTransitionWidth=t.tiltTransitionWidth),t.tiltPeakIntensity!==void 0&&(this.config.tiltPeakIntensity=t.tiltPeakIntensity);const{uniforms:n,program:i}=this.effectPass;e.useProgram(i),e.uniform1f(n.uStrength,this.config.parallaxStrength),e.uniform1i(n.uPomEnabled,this.config.pomEnabled?1:0),e.uniform1i(n.uPomSteps,this.config.pomSteps),e.uniform1f(n.uContrastLow,this.config.contrastLow),e.uniform1f(n.uContrastHigh,this.config.contrastHigh),e.uniform1f(n.uVerticalReduction,this.config.verticalReduction),e.uniform1f(n.uDofStart,this.config.dofStart),e.uniform1f(n.uDofStrength,this.config.dofStrength),e.uniform1f(n.uBlurRadius,this.config.blurRadius),e.uniform3f(n.uGlowColor,this.config.glowColor[0],this.config.glowColor[1],this.config.glowColor[2]),e.uniform1f(n.uGlowRadius,this.config.glowRadius),e.uniform1f(n.uGlowSoftness,this.config.glowSoftness),e.uniform1i(n.uTiltEnabled,this.config.tiltEnabled?1:0),e.uniform1f(n.uTiltHalfTanFov,this.config.tiltHalfTanFov),e.uniform1f(n.uTiltTransitionWidth,this.config.tiltTransitionWidth),e.uniform1f(n.uTiltPeakIntensity,this.config.tiltPeakIntensity),(t.parallaxStrength!==void 0||t.overscanPadding!==void 0)&&this.recalculateViewportLayout()}updateCurveLUTs(t,e,n,i,r){const s=this.gl;if(!s||!this.effectPass)return;const a=!!(t||e),l=!!n,u=!!i;if(t&&this.uploadLUT(this.displacementLutSlot,t),e&&this.uploadLUT(this.blurLutSlot,e),n&&this.uploadLUT(this.glowLutSlot,n),i&&this.uploadLUT(this.colorShiftLutSlot,i),s.useProgram(this.effectPass.program),s.uniform1i(this.effectPass.uniforms.uCurvesEnabled,a?1:0),s.uniform1i(this.effectPass.uniforms.uGlowEnabled,l?1:0),s.uniform1i(this.effectPass.uniforms.uColorShiftEnabled,u?1:0),u&&r){const h=this.effectPass.uniforms;s.uniform1f(h.uColorShiftHue,r.hueShift*(Math.PI/180)),s.uniform1f(h.uColorShiftSaturation,r.saturation),s.uniform1f(h.uColorShiftBrightness,r.brightness),s.uniform1f(h.uColorShiftTintStrength,r.tintStrength),s.uniform3f(h.uColorShiftTintColor,r.tintColor[0],r.tintColor[1],r.tintColor[2])}}initGPUResources(){const t=this.gl;t&&(this.bilateralPass=ye(t,this.qualityParams.bilateralRadius),this.effectPass=we(t),this.quadVao=_t(t,this.effectPass.program),t.disable(t.DEPTH_TEST))}uploadLUT(t,e){const n=this.gl;n&&(n.activeTexture(n.TEXTURE0+t.unit),t.texture?n.bindTexture(n.TEXTURE_2D,t.texture):(t.texture=n.createTexture(),n.bindTexture(n.TEXTURE_2D,t.texture),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),n.texStorage2D(n.TEXTURE_2D,1,n.R8,256,1)),n.texSubImage2D(n.TEXTURE_2D,0,0,0,256,1,n.RED,n.UNSIGNED_BYTE,e))}onRenderFrame(){const t=this.gl,e=this.mediaSource;if(!t||!this.effectPass||!this.quadVao)return;const n=e?.getImageSource();if(n){if(!this.rvfcSupported&&e.isLive&&this.onDepthUpdate(e.currentTime),t.useProgram(this.effectPass.program),t.activeTexture(t.TEXTURE0+this.videoSlot.unit),t.bindTexture(t.TEXTURE_2D,this.videoSlot.texture),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,n),this.readInput){const i=this.readInput();t.uniform2f(this.effectPass.uniforms.uOffset,-i.x,i.y),i.focalBandOffset!==void 0&&t.uniform1f(this.effectPass.uniforms.uFocalBandOffset,i.focalBandOffset),i.tiltPlaneNormal!==void 0&&t.uniform3f(this.effectPass.uniforms.uTiltPlaneNormal,i.tiltPlaneNormal[0],i.tiltPlaneNormal[1],i.tiltPlaneNormal[2]),i.tiltPlaneD!==void 0&&t.uniform1f(this.effectPass.uniforms.uTiltPlaneD,i.tiltPlaneD)}t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4)}}onDepthUpdate(t){const e=this.gl;if(!e||!this.readDepth||!this.rawDepthSlot.texture||!this.bilateralPass)return;const n=this.subsampleDepth(this.readDepth(t));this.bilateralPass.execute(e,this.quadVao,this.rawDepthSlot.texture,n,this.depthWidth,this.depthHeight,this.canvas.width,this.canvas.height)}recalculateViewportLayout(){const t=this.gl;if(!t)return;const{width:e,height:n}=this.getViewportSize(),i=Math.min(window.devicePixelRatio,this.qualityParams.dprCap),r=Math.round(e*i),s=Math.round(n*i);(this.canvas.width!==r||this.canvas.height!==s)&&(this.canvas.width=r,this.canvas.height=s,t.viewport(0,0,r,s)),this.computeCoverFitUV(this.config.parallaxStrength,this.config.overscanPadding),this.effectPass&&this.effectPass.updateUvTransform(t,this.uvOffset,this.uvScale)}disposeRenderer(){this.disposeTextures(),this.disposeGPUResources(),this.gl&&(this.gl.getExtension("WEBGL_lose_context")?.loseContext(),this.gl=null)}onContextRestored(){const t=this.canvas.getContext("webgl2");t&&(this.gl=t,t.clearColor(0,0,0,1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this.initGPUResources(),this.mediaSource&&this.depthWidth>0&&this.initialize(this.mediaSource,this.depthWidth,this.depthHeight),this.mediaSource&&(this.animationFrameHandle=window.requestAnimationFrame(()=>this.onRenderFrame())))}disposeTextures(){const t=this.gl;t&&this.textures.disposeAll(t)}disposeGPUResources(){const t=this.gl;t&&(this.bilateralPass&&(this.bilateralPass.dispose(t),this.bilateralPass=null),this.effectPass&&(this.effectPass.dispose(t),this.effectPass=null),this.quadVao&&(t.deleteVertexArray(this.quadVao),this.quadVao=null))}}async function lt(o,t={}){const{parent:e=document.body,loop:n=!0,muted:i=!0}=t,r=document.createElement("video");return r.crossOrigin="anonymous",r.setAttribute("crossorigin","anonymous"),r.playsInline=!0,r.setAttribute("playsinline",""),r.setAttribute("webkit-playsinline","true"),r.muted=i,r.defaultMuted=i,i&&r.setAttribute("muted",""),r.loop=n,r.preload="auto",r.style.display="none",r.src=o,e.appendChild(r),await Vt(r),new De(r)}class De{constructor(t){this.video=t}type="video";isLive=!0;get width(){return this.video.videoWidth}get height(){return this.video.videoHeight}get currentTime(){return this.video.currentTime}get duration(){return this.video.duration}get paused(){return this.video.paused}getImageSource(){return this.video.readyState<HTMLMediaElement.HAVE_CURRENT_DATA?null:this.video}requestVideoFrameCallback(t){return this.video.requestVideoFrameCallback(t)}cancelVideoFrameCallback(t){this.video.cancelVideoFrameCallback(t)}play(){return this.video.play()}pause(){this.video.pause()}addEventListener(t,e){this.video.addEventListener(t,e)}removeEventListener(t,e){this.video.removeEventListener(t,e)}dispose(){this.video.pause(),this.video.removeAttribute("src"),this.video.load(),this.video.remove()}}async function ht(o,t={}){const e=new Image;return e.crossOrigin="anonymous",e.src=o,await new Promise((n,i)=>{if(e.complete&&e.naturalWidth>0){n();return}e.addEventListener("load",()=>n(),{once:!0}),e.addEventListener("error",()=>i(new Error(`Failed to load image: ${o}`)),{once:!0})}),new Pe(e)}class Pe{constructor(t){this.img=t}type="image";isLive=!1;currentTime=0;duration=0;paused=!1;get width(){return this.img.naturalWidth}get height(){return this.img.naturalHeight}getImageSource(){return this.img}dispose(){this.img.removeAttribute("src")}}async function Bt(o={video:!0},t={}){const{parent:e=document.body}=t,n=await navigator.mediaDevices.getUserMedia(o),i=document.createElement("video");return i.playsInline=!0,i.setAttribute("playsinline",""),i.muted=!0,i.defaultMuted=!0,i.style.display="none",i.srcObject=n,e.appendChild(i),await Vt(i),await i.play(),new Fe(i,n)}class Fe{constructor(t,e){this.video=t,this.stream=e}type="camera";isLive=!0;duration=1/0;get width(){return this.video.videoWidth}get height(){return this.video.videoHeight}get currentTime(){return this.video.currentTime}get paused(){return this.video.paused}getImageSource(){return this.video.readyState<HTMLMediaElement.HAVE_CURRENT_DATA?null:this.video}requestVideoFrameCallback(t){return this.video.requestVideoFrameCallback(t)}cancelVideoFrameCallback(t){this.video.cancelVideoFrameCallback(t)}play(){return this.video.play()}pause(){this.video.pause()}addEventListener(t,e){this.video.addEventListener(t,e)}removeEventListener(t,e){this.video.removeEventListener(t,e)}dispose(){this.video.pause(),this.video.srcObject=null,this.video.remove();for(const t of this.stream.getTracks())t.stop()}}async function Vt(o){o.readyState>=HTMLMediaElement.HAVE_METADATA||await new Promise((t,e)=>{const n=()=>{r(),t()},i=()=>{r(),e(new Error("Failed to load video metadata."))},r=()=>{o.removeEventListener("loadedmetadata",n),o.removeEventListener("error",i)};o.addEventListener("loadedmetadata",n),o.addEventListener("error",i),o.load()})}const yt=[.485,.456,.406],St=[.229,.224,.225],q=518;async function Ce(){return await import("onnxruntime-web/webgpu")}class Le{constructor(t,e){this.depthWidth=t,this.depthHeight=e;const n=t*e;this.frontBuffer=new Uint8Array(n),this.frontBuffer.fill(128),this.backBuffer=new Uint8Array(n),this.backBuffer.fill(128),this.readyPromise=new Promise(i=>{this.readyResolve=i})}ort=null;session=null;inputName="";outputName="";frontBuffer;backBuffer;inferenceInFlight=!1;readyResolve=null;readyPromise;captureCanvas=null;captureCtx=null;disposed=!1;async init(t,e){const n=await Ce();if(this.ort=n,this.captureCanvas=document.createElement("canvas"),this.captureCanvas.width=q,this.captureCanvas.height=q,this.captureCtx=this.captureCanvas.getContext("2d",{willReadFrequently:!0}),!this.captureCtx)throw new Error("[DepthEstimator] Failed to create 2D canvas context.");e?.({receivedBytes:0,totalBytes:null,fraction:0,label:"Downloading depth model…"});const i=await Ue(t,e);e?.({receivedBytes:i.byteLength,totalBytes:i.byteLength,fraction:1,label:"Initialising depth model…"});let r;try{r=await n.InferenceSession.create(i,{executionProviders:["webgpu"]}),console.log("[DepthEstimator] Using WebGPU execution provider")}catch(s){console.warn("[DepthEstimator] WebGPU EP unavailable, falling back to WASM:",s),n.env.wasm.proxy=!0,r=await n.InferenceSession.create(i,{executionProviders:["wasm"]}),console.log("[DepthEstimator] Using WASM execution provider (proxy worker)")}this.session=r,this.inputName=r.inputNames[0],this.outputName=r.outputNames[0],this.readyResolve?.(),this.readyResolve=null}waitUntilReady(){return this.readyPromise}submitFrame(t){this.inferenceInFlight||!this.session||this.disposed||(this.inferenceInFlight=!0,this.runInference(t))}async submitFrameAndWait(t){return!this.session||this.disposed?this.frontBuffer:(await this.runInference(t),this.frontBuffer)}getLatestDepth(){return this.frontBuffer}dispose(){this.disposed=!0,this.session?.release(),this.session=null,this.ort=null,this.captureCanvas=null,this.captureCtx=null}async runInference(t){try{if(!this.session||!this.captureCtx||!this.ort)return;this.captureCtx.drawImage(t,0,0,q,q);const e=this.captureCtx.getImageData(0,0,q,q),n=this.preprocess(e),r=(await this.session.run({[this.inputName]:n}))[this.outputName],s=r.data,a=r.dims,l=a.length===3?a[1]:a[2],u=a.length===3?a[2]:a[3];this.postProcess(s,u,l);const h=this.frontBuffer;this.frontBuffer=this.backBuffer,this.backBuffer=h}catch(e){console.error("[DepthEstimator] Inference failed:",e)}finally{this.inferenceInFlight=!1}}preprocess(t){const{data:e,width:n,height:i}=t,r=n*i,s=new Float32Array(3*r);for(let a=0;a<r;a++){const l=a*4;s[a]=(e[l]/255-yt[0])/St[0],s[r+a]=(e[l+1]/255-yt[1])/St[1],s[2*r+a]=(e[l+2]/255-yt[2])/St[2]}return new this.ort.Tensor("float32",s,[1,3,i,n])}postProcess(t,e,n){const{depthWidth:i,depthHeight:r}=this;let s=1/0,a=-1/0;for(let f=0;f<t.length;f++){const c=t[f];c<s&&(s=c),c>a&&(a=c)}const l=a-s||1,u=e/i,h=n/r;for(let f=0;f<r;f++)for(let c=0;c<i;c++){const E=c*u,v=f*h,p=Math.floor(E),P=Math.floor(v),x=Math.min(p+1,e-1),y=Math.min(P+1,n-1),d=E-p,g=v-P,b=t[P*e+p],T=t[P*e+x],m=t[y*e+p],S=t[y*e+x],F=(b*(1-d)*(1-g)+T*d*(1-g)+m*(1-d)*g+S*d*g-s)/l;this.backBuffer[f*i+c]=F*255+.5|0}}}async function ut(o,t,e,n){const i=new Le(t,e);return await i.init(o,n),i}async function Ue(o,t){const e=await fetch(o);if(!e.ok)throw new Error(`[DepthEstimator] Failed to fetch model (${e.status} ${e.statusText}).`);const n=e.headers.get("content-length"),i=n?Number(n):null,r=e.body;if(!r){const f=await e.arrayBuffer();return t?.({receivedBytes:f.byteLength,totalBytes:f.byteLength,fraction:1,label:"Downloading depth model…"}),f}const s=[];let a=0;const l=r.getReader();for(;;){const{done:f,value:c}=await l.read();if(f)break;c&&(s.push(c),a+=c.byteLength,t?.({receivedBytes:a,totalBytes:i,fraction:i?Math.min(a/i,1):0,label:"Downloading depth model…"}))}const u=new Uint8Array(a);let h=0;for(const f of s)u.set(f,h),h+=f.byteLength;return u.buffer}const Ot={sensitivityX:.4,sensitivityY:1,lerpFactor:.08};function At(o,t){const e=o.points;if(e.length===0)return 0;if(e.length===1||t<=e[0].x)return e[0].y;if(t>=e[e.length-1].x)return e[e.length-1].y;let n=0;for(;n<e.length-1&&e[n+1].x<t;)n++;const i=e[n],r=e[n+1],s=(t-i.x)/(r.x-i.x);switch(o.interpolation){case"step":return i.y;case"linear":return i.y+(r.y-i.y)*s;case"smooth":{const a=s*s*(3-2*s);return i.y+(r.y-i.y)*a}default:return i.y+(r.y-i.y)*s}}function ct(o,t=256){const e=new Uint8Array(t);for(let n=0;n<t;n++){const i=n/(t-1);e[n]=Math.round(At(o,i)*255)}return e}function X(o,t){return o.find(e=>e.channel===t&&e.enabled)}function kt(o,t){const e=X(o,"displacement"),n=X(o,"blur"),i=X(o,"glow"),r=e?.params;let s=.6,a=0;if(n){const l=n.curve;for(let u=0;u<=1;u+=.01)if(At(l,u)>.01){s=u;break}a=At(l,1)}return{parallaxStrength:r?.strength??.05,pomEnabled:r?.pomEnabled??!0,pomSteps:r?.pomSteps??16,contrastLow:t.contrastLow,contrastHigh:t.contrastHigh,verticalReduction:t.verticalReduction,dofStart:s,dofStrength:a,blurRadius:n?.params?.maxRadius??.01,glowColor:i?.params?.color??[1,.95,.85],glowRadius:i?.params?.radius??.02,glowSoftness:i?.params?.softness??.6,tiltEnabled:n?.params?.tiltEnabled??!1,tiltHalfTanFov:Math.tan((n?.params?.tiltFov??50)*Math.PI/360),tiltTransitionWidth:(n?.params?.focalWidth??.3)*4.5,tiltPeakIntensity:n?.params?.peakIntensity??.8}}function Nt(o){const t=X(o,"displacement"),e=X(o,"blur"),n=X(o,"glow"),i=X(o,"color-shift"),r=i?.params;return{displacementLUT:t?ct(t.curve):null,blurLUT:e?ct(e.curve):null,glowLUT:n?ct(n.curve):null,colorShiftLUT:i?ct(i.curve):null,colorShiftParams:r?{hueShift:r.hueShift??0,saturation:r.saturation??1,brightness:r.brightness??1,tintStrength:r.tintStrength??0,tintColor:r.tintColor??[.7,.8,.9]}:null}}class Ht{abortController=null;initialized=!1;initializing=!1;element;constructor(t){this.element=t}onConnected(){this.element.setupShadowDOM(),this.tryInit()}onDisconnected(){this.cancelInit(),this.element.doDispose(),this.initialized=!1}onAttributeChanged(t,e,n){this.element.reinitAttributes.includes(t)&&e!==n&&(this.initialized?(this.cancelInit(),this.element.doDispose(),this.initialized=!1,this.element.setupShadowDOM(),this.tryInit()):this.initializing||this.tryInit())}get isInitialized(){return this.initialized}markInitialized(){this.initialized=!0,this.initializing=!1}async tryInit(){if(this.initializing)return;const t=this.element;if(!t.isConnected)return;if(t.canInit){if(!t.canInit())return}else for(const n of t.reinitAttributes)if(!t.getAttribute(n))return;this.cancelInit();const e=new AbortController;this.abortController=e,this.initializing=!0;try{if(await t.doInit(e.signal),e.signal.aborted){this.initializing=!1;return}}catch{this.initializing=!1}}cancelInit(){this.abortController?.abort(),this.abortController=null,this.initializing=!1}}const G={parallaxX:.4,parallaxY:1,parallaxMax:30,overscan:.05,autoplay:!0,loop:!0,muted:!0},Y=512,j=512;let _e=class Qt{constructor(t,e=.08,n=.06){this.host=t,this.lerpFactor=e,this.motionLerpFactor=n,this.host.addEventListener("mousemove",this.handleMouseMove),this.host.addEventListener("mouseleave",this.resetPointerTarget),this.host.addEventListener("touchstart",this.handleTouchStart,{passive:!0}),this.host.addEventListener("touchmove",this.handleTouchMove,{passive:!0}),this.host.addEventListener("touchend",this.handleTouchEnd,{passive:!0}),this.host.addEventListener("touchcancel",this.handleTouchEnd,{passive:!0})}pointerTarget={x:0,y:0};motionTarget={x:0,y:0};smoothedOutput={x:0,y:0};usingMotionInput=!1;motionListenerAttached=!1;motionRequested=!1;touchActive=!1;touchAnchorX=0;touchAnchorY=0;lerpFactor;motionLerpFactor;static TOUCH_DRAG_RANGE=100;update(){const t=this.touchActive?this.pointerTarget:this.usingMotionInput?this.motionTarget:this.pointerTarget,e=this.usingMotionInput&&!this.touchActive?this.motionLerpFactor:this.lerpFactor;return this.smoothedOutput.x=dt(this.smoothedOutput.x,t.x,e),this.smoothedOutput.y=dt(this.smoothedOutput.y,t.y,e),this.smoothedOutput}dispose(){this.host.removeEventListener("mousemove",this.handleMouseMove),this.host.removeEventListener("mouseleave",this.resetPointerTarget),this.host.removeEventListener("touchstart",this.handleTouchStart),this.host.removeEventListener("touchmove",this.handleTouchMove),this.host.removeEventListener("touchend",this.handleTouchEnd),this.host.removeEventListener("touchcancel",this.handleTouchEnd),this.motionListenerAttached&&(window.removeEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!1)}handleMouseMove=t=>{const e=this.host.getBoundingClientRect(),n=(t.clientX-e.left)/e.width*2-1,i=(t.clientY-e.top)/e.height*2-1;this.pointerTarget.x=Z(n,-1,1),this.pointerTarget.y=Z(i,-1,1)};resetPointerTarget=()=>{this.pointerTarget.x=0,this.pointerTarget.y=0};handleTouchStart=t=>{const e=t.touches[0];e&&(this.touchActive=!0,this.touchAnchorX=e.clientX,this.touchAnchorY=e.clientY,this.pointerTarget.x=0,this.pointerTarget.y=0,this.motionRequested||(this.motionRequested=!0,this.requestMotionPermission()))};handleTouchMove=t=>{const e=t.touches[0];if(!e)return;const n=e.clientX-this.touchAnchorX,i=e.clientY-this.touchAnchorY,r=Qt.TOUCH_DRAG_RANGE;this.pointerTarget.x=Z(n/r,-1,1),this.pointerTarget.y=Z(i/r,-1,1)};handleTouchEnd=()=>{this.touchActive=!1,this.pointerTarget.x=0,this.pointerTarget.y=0};async requestMotionPermission(){if(typeof DeviceOrientationEvent>"u")return;const t=DeviceOrientationEvent;if(typeof t.requestPermission=="function")try{if(await t.requestPermission()!=="granted")return}catch{return}this.motionListenerAttached||(window.addEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!0),this.usingMotionInput=!0}handleDeviceOrientation=t=>{const e=Z((t.gamma??0)/45,-1,1),n=Z((t.beta??0)/45,-1,1);this.motionTarget.x=dt(this.motionTarget.x,e,this.motionLerpFactor),this.motionTarget.y=dt(this.motionTarget.y,n,this.motionLerpFactor)}};class ft extends HTMLElement{static TAG_NAME="layershift-effect";static get observedAttributes(){return["src","depth-src","depth-meta","depth-model","source-type","config","parallax-x","parallax-y","parallax-max","layers","overscan","quality","gpu-backend","autoplay","loop","muted"]}reinitAttributes=["src","depth-src","depth-meta","depth-model","source-type","config"];canInit(){if(this.sourceType==="camera")return!0;const t=!!this.getAttribute("src"),e=!!this.getAttribute("depth-src")&&!!this.getAttribute("depth-meta"),n=!!this.getAttribute("depth-model");return t&&(e||n)}shadow;container=null;renderer=null;inputHandler=null;source=null;depthEstimator=null;loopCount=0;lifecycle;depthFallback=null;constructor(){super(),this.shadow=this.attachShadow({mode:"open"}),this.lifecycle=new Ht(this)}getAttrFloat(t,e){const n=this.getAttribute(t);if(n===null)return e;const i=parseFloat(n);return Number.isFinite(i)?i:e}getAttrBool(t,e){if(!this.hasAttribute(t))return e;const n=this.getAttribute(t);return!(n==="false"||n==="0")}get parallaxX(){return this.getAttrFloat("parallax-x",G.parallaxX)}get parallaxY(){return this.getAttrFloat("parallax-y",G.parallaxY)}get parallaxMax(){return this.getAttrFloat("parallax-max",G.parallaxMax)}get overscan(){return this.getAttrFloat("overscan",G.overscan)}get quality(){const t=this.getAttribute("quality");if(t==="auto"||t==="high"||t==="medium"||t==="low")return t}get gpuBackend(){return"webgl2"}get sourceType(){const t=this.getAttribute("source-type");return t==="camera"?"camera":t==="image"?"image":"video"}get depthModel(){return this.getAttribute("depth-model")}get shouldAutoplay(){return this.getAttrBool("autoplay",G.autoplay)}get shouldLoop(){return this.getAttrBool("loop",G.loop)}get shouldMute(){return this.getAttrBool("muted",G.muted)}emit(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}attachSourceEventListeners(t){t.addEventListener&&(t.addEventListener("play",(()=>{this.emit("layershift-effect:play",{currentTime:t.currentTime})})),t.addEventListener("pause",(()=>{this.emit("layershift-effect:pause",{currentTime:t.currentTime})})),t.addEventListener("ended",(()=>{this.loopCount+=1,this.emit("layershift-effect:loop",{loopCount:this.loopCount})})))}connectedCallback(){this.lifecycle.onConnected()}disconnectedCallback(){this.lifecycle.onDisconnected()}attributeChangedCallback(t,e,n){this.lifecycle.onAttributeChanged(t,e,n)}setupShadowDOM(){this.shadow.innerHTML="";const t=document.createElement("style");t.textContent=`
194
470
  :host {
195
471
  display: block;
196
472
  width: 100%;
@@ -210,517 +486,515 @@ ${o}`)}return r.detachShader(i,t),r.detachShader(i,e),r.deleteShader(t),r.delete
210
486
  width: 100%;
211
487
  height: 100%;
212
488
  }
213
- `,this.shadow.appendChild(t),this.container=document.createElement("div"),this.container.className="container",this.shadow.appendChild(this.container)}async doInit(t){const e=this.getAttribute("src"),i=this.getAttribute("depth-src"),o=this.getAttribute("depth-meta");if(this.container)try{const[n,s]=await Promise.all([this.createVideoElement(e),vt(i,o)]);if(t.aborted){n.remove();return}this.video=n,this.loopCount=0,this.attachVideoEventListeners(n);const h=Ht(s.frames,s.meta.width,s.meta.height),a=Xt(h),l=this.hasAttribute("parallax-max")?this.parallaxMax/Math.max(n.videoWidth,1):a.parallaxStrength,u=this.hasAttribute("overscan")?this.overscan:a.overscanPadding;let f;try{const d=await it.create(s,s.meta.width,s.meta.height);this.depthWorker=d,f=m=>d.sample(m)}catch{const d=new gt(s,s.meta.width,s.meta.height);f=m=>d.sample(m)}if(t.aborted){n.remove(),this.depthWorker?.dispose(),this.depthWorker=null;return}this.renderer=new Z(this.container,{parallaxStrength:l,pomEnabled:!0,pomSteps:a.pomSteps,overscanPadding:u,contrastLow:a.contrastLow,contrastHigh:a.contrastHigh,verticalReduction:a.verticalReduction,dofStart:a.dofStart,dofStrength:a.dofStrength}),this.renderer.initialize(n,s.meta.width,s.meta.height),this.inputHandler=new qt(this);const c=this.parallaxX,g=this.parallaxY;if(this.renderer.start(n,f,()=>{if(!this.inputHandler)return{x:0,y:0};const d=this.inputHandler.update();return{x:d.x*c,y:d.y*g}},(d,m)=>{this.emit("layershift-parallax:frame",{currentTime:d,frameNumber:m})}),this.shouldAutoplay){n.currentTime=0;try{await n.play()}catch{}}if(t.aborted)return;this.lifecycle.markInitialized(),this.emit("layershift-parallax:ready",{videoWidth:n.videoWidth,videoHeight:n.videoHeight,duration:n.duration,depthProfile:h,derivedParams:a})}catch(n){const s=n instanceof Error?n.message:"Failed to initialize.";console.error("<layershift-parallax>: Failed to initialize.",n),this.emit("layershift-parallax:error",{message:s})}}async createVideoElement(t){const e=document.createElement("video");return e.crossOrigin="anonymous",e.setAttribute("crossorigin","anonymous"),e.playsInline=!0,e.setAttribute("playsinline",""),e.setAttribute("webkit-playsinline","true"),e.muted=this.shouldMute,e.defaultMuted=this.shouldMute,this.shouldMute&&e.setAttribute("muted",""),e.loop=this.shouldLoop,e.preload="auto",e.style.display="none",e.src=t,this.shadow.appendChild(e),await new Promise((i,o)=>{if(e.readyState>=HTMLMediaElement.HAVE_METADATA){i();return}const n=()=>{h(),i()},s=()=>{h(),o(new Error("Failed to load video metadata."))},h=()=>{e.removeEventListener("loadedmetadata",n),e.removeEventListener("error",s)};e.addEventListener("loadedmetadata",n),e.addEventListener("error",s),e.load()}),e}doDispose(){this.renderer?.dispose(),this.renderer=null,this.inputHandler?.dispose(),this.inputHandler=null,this.depthWorker?.dispose(),this.depthWorker=null,this.video&&(this.video.pause(),this.video.removeAttribute("src"),this.video.load(),this.video.remove(),this.video=null),this.loopCount=0,this.container=null}}function N(r,t,e){return Math.min(e,Math.max(t,r))}function ot(r,t,e){return r+(t-r)*e}const Zt=`#version 300 es
214
- in vec2 aPosition;
215
- uniform vec2 uMeshScale;
216
- void main() {
217
- gl_Position = vec4(aPosition * uMeshScale, 0.0, 1.0);
218
- }
219
- `,$t=`#version 300 es
220
- precision lowp float;
221
- out vec4 fragColor;
222
- void main() { fragColor = vec4(0.0); }
223
- `,Jt=`#version 300 es
224
- in vec2 aPosition;
225
- uniform vec2 uMeshScale;
226
- void main() {
227
- gl_Position = vec4(aPosition * uMeshScale, 0.0, 1.0);
228
- }
229
- `,Kt=`#version 300 es
230
- precision lowp float;
231
- out vec4 fragColor;
232
- void main() { fragColor = vec4(1.0); }
233
- `,Qt=`#version 300 es
234
- in vec2 aPosition;
235
- out vec2 vUv;
236
- void main() {
237
- vUv = aPosition * 0.5 + 0.5;
238
- gl_Position = vec4(aPosition, 0.0, 1.0);
239
- }
240
- `,te=`#version 300 es
241
- precision highp float;
242
- uniform sampler2D uMask;
243
- uniform vec2 uTexelSize;
244
- in vec2 vUv;
245
- out vec2 fragSeed;
246
-
247
- void main() {
248
- float center = texture(uMask, vUv).r;
249
- float left = texture(uMask, vUv + vec2(-uTexelSize.x, 0.0)).r;
250
- float right = texture(uMask, vUv + vec2( uTexelSize.x, 0.0)).r;
251
- float up = texture(uMask, vUv + vec2(0.0, uTexelSize.y)).r;
252
- float down = texture(uMask, vUv + vec2(0.0, -uTexelSize.y)).r;
253
-
254
- bool isEdge = (step(0.5, center) != step(0.5, left)) ||
255
- (step(0.5, center) != step(0.5, right)) ||
256
- (step(0.5, center) != step(0.5, up)) ||
257
- (step(0.5, center) != step(0.5, down));
258
-
259
- if (isEdge) {
260
- fragSeed = vUv;
261
- } else {
262
- fragSeed = vec2(-1.0);
263
- }
264
- }
265
- `,ee=`#version 300 es
266
- in vec2 aPosition;
267
- out vec2 vUv;
268
- void main() {
269
- vUv = aPosition * 0.5 + 0.5;
270
- gl_Position = vec4(aPosition, 0.0, 1.0);
489
+ `,this.shadow.appendChild(t),this.container=document.createElement("div"),this.container.className="container",this.shadow.appendChild(this.container)}async fetchFilterConfig(t){const e=this.getAttribute("config");if(!e)return null;try{const n=await fetch(e,{signal:t});if(!n.ok)return console.warn(`<layershift-effect>: Failed to fetch config from "${e}" (${n.status})`),null;const i=await n.json();return{channels:i.channels??[],motion:i.motion??Ot,overscanPadding:i.overscanPadding??.05,quality:i.quality??"auto"}}catch(n){return n.name==="AbortError"||console.warn("<layershift-effect>: Failed to parse config.",n),null}}buildLegacyConfig(t,e){const n=this.container?.clientWidth||e,i=this.hasAttribute("parallax-max")?this.parallaxMax/Math.max(n,1):t.parallaxStrength,r=this.hasAttribute("overscan")?this.overscan:t.overscanPadding;return{parallaxStrength:i,pomEnabled:!0,pomSteps:t.pomSteps,overscanPadding:r,quality:this.quality,contrastLow:t.contrastLow,contrastHigh:t.contrastHigh,verticalReduction:t.verticalReduction,dofStart:t.dofStart,dofStrength:t.dofStrength}}async doInit(t){if(!this.container)return;const e=this.sourceType==="camera",n=this.depthModel;try{let i,r,s=null;const a=m=>{this.emit("layershift-effect:model-progress",m)};if(e){if(i=await Bt({video:{facingMode:"user"}},{parent:this.shadow}),t.aborted){i.dispose();return}if(n){if(s=await ut(n,Y,j,a),t.aborted){s.dispose(),i.dispose();return}r=N(Y,j)}else r=N(i.width,i.height)}else{const m=this.getAttribute("src"),S=this.getAttribute("depth-src"),w=this.getAttribute("depth-meta"),F=!!S&&!!w,U=this.sourceType==="image"||/\.(jpe?g|png|webp|gif|avif|bmp)(\?|$)/i.test(m);if(F){const[_,R]=await Promise.all([U?ht(m):lt(m,{parent:this.shadow,loop:this.shouldLoop,muted:this.shouldMute}),Lt(S,w)]);if(t.aborted){_.dispose();return}i=_,r=R}else if(n){const[_,R]=await Promise.all([U?ht(m):lt(m,{parent:this.shadow,loop:this.shouldLoop,muted:this.shouldMute}),ut(n,Y,j,a)]);if(t.aborted){_.dispose(),R.dispose();return}if(i=_,s=R,U||!i.isLive){const D=i.getImageSource();if(D){const L=await s.submitFrameAndWait(D);r={meta:{frameCount:1,fps:1,width:Y,height:j,sourceFps:1},frames:[L]}}else r=N(Y,j)}else r=N(Y,j)}else throw new Error("Either depth-src/depth-meta or depth-model must be provided.")}this.source=i,this.depthEstimator=s,this.loopCount=0,this.attachSourceEventListeners(i);const l=se(r.frames,r.meta.width,r.meta.height),u=ae(l);this.depthFallback={contrastLow:u.contrastLow,contrastHigh:u.contrastHigh,verticalReduction:u.verticalReduction};let h;if(s)h=()=>s.getLatestDepth();else{const m=new Ct(r);h=S=>m.sample(S)}if(t.aborted)return;let f,c=null,E=Ot;const v=await this.fetchFilterConfig(t);if(t.aborted)return;if(v&&v.channels.length>0){const m=kt(v.channels,{contrastLow:u.contrastLow,contrastHigh:u.contrastHigh,verticalReduction:u.verticalReduction});f={parallaxStrength:m.parallaxStrength,pomEnabled:m.pomEnabled,pomSteps:m.pomSteps,overscanPadding:v.overscanPadding,quality:v.quality,contrastLow:m.contrastLow,contrastHigh:m.contrastHigh,verticalReduction:m.verticalReduction,dofStart:m.dofStart,dofStrength:m.dofStrength,blurRadius:m.blurRadius,glowColor:m.glowColor,glowRadius:m.glowRadius,glowSoftness:m.glowSoftness,tiltEnabled:m.tiltEnabled,tiltHalfTanFov:m.tiltHalfTanFov,tiltTransitionWidth:m.tiltTransitionWidth,tiltPeakIntensity:m.tiltPeakIntensity},c=Nt(v.channels),E=v.motion}else f=this.buildLegacyConfig(u,i.width);this.renderer=new Re(this.container,f),this.renderer.initialize(i,r.meta.width,r.meta.height),c&&this.renderer.updateCurveLUTs(c.displacementLUT,c.blurLUT,c.glowLUT,c.colorShiftLUT,c.colorShiftParams??void 0),this.inputHandler=new _e(this,E.lerpFactor);const p=E.sensitivityX,P=E.sensitivityY,x=E.tiltPlaneInput??!1,y=E.tiltPitchSensitivity??.35,d=E.tiltYawSensitivity??.15,b=v?.channels.find(m=>m.channel==="blur"&&m.enabled)?.params?.focalCenter??.5,T=s;if(this.renderer.start(i,h,()=>{if(!this.inputHandler)return{x:0,y:0};const m=this.inputHandler.update();if(x){const F=m.y*P*y,U=m.x*p*d,_=Math.cos(F),R=Math.sin(F),D=Math.cos(U),M=Math.sin(U)*_,V=-R,B=D*_,H=.5+b*(5-.5);return{x:m.x*p*.3,y:m.y*P*.3,tiltPlaneNormal:[M,V,B],tiltPlaneD:B*H}}return{x:m.x*p,y:m.y*P}},(m,S)=>{if(T){const w=i.getImageSource();w&&T.submitFrame(w)}this.emit("layershift-effect:frame",{currentTime:m,frameNumber:S})}),!e&&i.isLive&&this.shouldAutoplay&&i.play)try{await i.play()}catch{}if(t.aborted)return;this.lifecycle.markInitialized(),this.emit("layershift-effect:ready",{videoWidth:i.width,videoHeight:i.height,duration:i.duration,depthProfile:l,derivedParams:u})}catch(i){const r=i instanceof Error?i.message:"Failed to initialize.";console.error("<layershift-effect>: Failed to initialize.",i),this.emit("layershift-effect:error",{message:r})}}updateConfig(t){if(!this.renderer)return;const e=this.depthFallback??{contrastLow:.05,contrastHigh:.95,verticalReduction:.5},n=kt(t,e);this.renderer.updateConfig(n);const i=Nt(t);this.renderer.updateCurveLUTs(i.displacementLUT,i.blurLUT,i.glowLUT,i.colorShiftLUT,i.colorShiftParams??void 0)}doDispose(){this.renderer?.dispose(),this.renderer=null,this.inputHandler?.dispose(),this.inputHandler=null,this.depthEstimator?.dispose(),this.depthEstimator=null,this.source?.dispose(),this.source=null,this.depthFallback=null,this.loopCount=0,this.container=null}}function Z(o,t,e){return Math.min(e,Math.max(t,o))}function dt(o,t,e){return o+(t-o)*e}class wt{gl;hasColorBufferFloat;maskFbo=null;maskTex=null;pingFbo=null;pingTex=null;pongFbo=null;pongTex=null;distFbo=null;distTex=null;_width=0;_height=0;_dirty=!0;constructor(t,e){this.gl=t,this.hasColorBufferFloat=e}get width(){return this._width}get height(){return this._height}get isDirty(){return this._dirty}get distanceTexture(){return this.distTex}get maskTexture(){return this.maskTex}markDirty(){this._dirty=!0}createResources(t,e,n){const i=this.gl;this.dispose();const r=Math.max(1,Math.round(t/n)),s=Math.max(1,Math.round(e/n));this._width=r,this._height=s;const a=(u,h,f,c)=>{const E=i.createFramebuffer();return i.bindFramebuffer(i.FRAMEBUFFER,E),i.bindTexture(i.TEXTURE_2D,u),i.texStorage2D(i.TEXTURE_2D,1,h,f,c),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.framebufferTexture2D(i.FRAMEBUFFER,i.COLOR_ATTACHMENT0,i.TEXTURE_2D,u,0),i.bindFramebuffer(i.FRAMEBUFFER,null),E};this.maskTex=i.createTexture(),this.maskFbo=a(this.maskTex,i.R8,r,s);const l=this.hasColorBufferFloat?i.RG16F:i.RGBA8;this.pingTex=i.createTexture(),this.pingFbo=a(this.pingTex,l,r,s),this.pongTex=i.createTexture(),this.pongFbo=a(this.pongTex,l,r,s),this.distTex=i.createTexture(),this.distFbo=a(this.distTex,i.RGBA8,r,s),this._dirty=!0}compute(t){const e=this.gl;if(!this.maskFbo||!this.pingFbo||!this.pongFbo||!this.distFbo)return;const n=this._width,i=this._height;if(n===0||i===0)return;e.viewport(0,0,n,i),e.disable(e.STENCIL_TEST),e.disable(e.BLEND),e.bindFramebuffer(e.FRAMEBUFFER,this.maskFbo),e.clearColor(0,0,0,1),e.clear(e.COLOR_BUFFER_BIT),e.useProgram(t.maskPass.program),e.uniform2f(t.maskPass.uniforms.uMeshScale,t.meshScaleX,t.meshScaleY),e.bindVertexArray(t.maskVao),e.drawElements(e.TRIANGLES,t.stencilIndexCount,e.UNSIGNED_SHORT,0),e.bindFramebuffer(e.FRAMEBUFFER,this.pingFbo),e.clearColor(-1,-1,0,0),e.clear(e.COLOR_BUFFER_BIT),e.useProgram(t.seedPass.program),e.activeTexture(e.TEXTURE5),e.bindTexture(e.TEXTURE_2D,this.maskTex),e.uniform1i(t.seedPass.uniforms.uMask,5),e.uniform2f(t.seedPass.uniforms.uTexelSize,1/n,1/i),e.bindVertexArray(t.quadVao),e.drawArrays(e.TRIANGLE_STRIP,0,4);const r=wt.computeFloodIterations(n,i);e.useProgram(t.floodPass.program);let s=this.pingTex,a=this.pongFbo,l=this.pongTex;for(let u=0;u<r.length;u++){const h=r[u]/Math.max(n,i);e.bindFramebuffer(e.FRAMEBUFFER,a),e.activeTexture(e.TEXTURE5),e.bindTexture(e.TEXTURE_2D,s),e.uniform1i(t.floodPass.uniforms.uSeedTex,5),e.uniform1f(t.floodPass.uniforms.uStepSize,h),e.bindVertexArray(t.quadVao),e.drawArrays(e.TRIANGLE_STRIP,0,4);const f=s,c=a;s=l,a=c===this.pongFbo?this.pingFbo:this.pongFbo,l=f}e.bindFramebuffer(e.FRAMEBUFFER,this.distFbo),e.clearColor(0,0,0,1),e.clear(e.COLOR_BUFFER_BIT),e.useProgram(t.distPass.program),e.activeTexture(e.TEXTURE5),e.bindTexture(e.TEXTURE_2D,s),e.uniform1i(t.distPass.uniforms.uSeedTex,5),e.activeTexture(e.TEXTURE6),e.bindTexture(e.TEXTURE_2D,this.maskTex),e.uniform1i(t.distPass.uniforms.uMask,6),e.uniform1f(t.distPass.uniforms.uBevelWidth,t.distRange),e.bindVertexArray(t.quadVao),e.drawArrays(e.TRIANGLE_STRIP,0,4),e.activeTexture(e.TEXTURE4),e.bindTexture(e.TEXTURE_2D,this.distTex),e.bindFramebuffer(e.FRAMEBUFFER,null),this._dirty=!1}static computeFloodIterations(t,e){const n=Math.max(t,e),i=[];let r=Math.ceil(n/2);for(;r>=1;)i.push(r),r=Math.floor(r/2);return i}dispose(){const t=this.gl;this.maskTex&&(t.deleteTexture(this.maskTex),this.maskTex=null),this.maskFbo&&(t.deleteFramebuffer(this.maskFbo),this.maskFbo=null),this.pingTex&&(t.deleteTexture(this.pingTex),this.pingTex=null),this.pingFbo&&(t.deleteFramebuffer(this.pingFbo),this.pingFbo=null),this.pongTex&&(t.deleteTexture(this.pongTex),this.pongTex=null),this.pongFbo&&(t.deleteFramebuffer(this.pongFbo),this.pongFbo=null),this.distTex&&(t.deleteTexture(this.distTex),this.distTex=null),this.distFbo&&(t.deleteFramebuffer(this.distFbo),this.distFbo=null),this._width=0,this._height=0,this._dirty=!0}}const Me=`#version 300 es
490
+ in vec2 aPosition;
491
+ uniform vec2 uMeshScale;
492
+ void main() {
493
+ gl_Position = vec4(aPosition * uMeshScale, 0.0, 1.0);
494
+ }
495
+ `,Ie=`#version 300 es
496
+ precision lowp float;
497
+ out vec4 fragColor;
498
+ void main() { fragColor = vec4(0.0); }
499
+ `,Be=`#version 300 es
500
+ in vec2 aPosition;
501
+ uniform vec2 uMeshScale;
502
+ void main() {
503
+ gl_Position = vec4(aPosition * uMeshScale, 0.0, 1.0);
504
+ }
505
+ `,Ve=`#version 300 es
506
+ precision lowp float;
507
+ out vec4 fragColor;
508
+ void main() { fragColor = vec4(1.0); }
509
+ `,Oe=`#version 300 es
510
+ in vec2 aPosition;
511
+ out vec2 vUv;
512
+ void main() {
513
+ vUv = aPosition * 0.5 + 0.5;
514
+ gl_Position = vec4(aPosition, 0.0, 1.0);
515
+ }
516
+ `,ke=`#version 300 es
517
+ precision highp float;
518
+ uniform sampler2D uMask;
519
+ uniform vec2 uTexelSize;
520
+ in vec2 vUv;
521
+ out vec2 fragSeed;
522
+
523
+ void main() {
524
+ float center = texture(uMask, vUv).r;
525
+ float left = texture(uMask, vUv + vec2(-uTexelSize.x, 0.0)).r;
526
+ float right = texture(uMask, vUv + vec2( uTexelSize.x, 0.0)).r;
527
+ float up = texture(uMask, vUv + vec2(0.0, uTexelSize.y)).r;
528
+ float down = texture(uMask, vUv + vec2(0.0, -uTexelSize.y)).r;
529
+
530
+ bool isEdge = (step(0.5, center) != step(0.5, left)) ||
531
+ (step(0.5, center) != step(0.5, right)) ||
532
+ (step(0.5, center) != step(0.5, up)) ||
533
+ (step(0.5, center) != step(0.5, down));
534
+
535
+ if (isEdge) {
536
+ fragSeed = vUv;
537
+ } else {
538
+ fragSeed = vec2(-1.0);
271
539
  }
272
- `,ie=`#version 300 es
273
- precision highp float;
274
- uniform sampler2D uSeedTex;
275
- uniform float uStepSize;
276
- in vec2 vUv;
277
- out vec2 fragSeed;
278
-
279
- void main() {
280
- vec2 bestSeed = texture(uSeedTex, vUv).rg;
281
- float bestDist = (bestSeed.x < 0.0) ? 1.0e10 : distance(vUv, bestSeed);
282
-
283
- for (int dy = -1; dy <= 1; dy++) {
284
- for (int dx = -1; dx <= 1; dx++) {
285
- if (dx == 0 && dy == 0) continue;
286
- vec2 offset = vec2(float(dx), float(dy)) * uStepSize;
287
- vec2 sampleUv = vUv + offset;
288
- if (sampleUv.x < 0.0 || sampleUv.x > 1.0 || sampleUv.y < 0.0 || sampleUv.y > 1.0) continue;
289
- vec2 neighborSeed = texture(uSeedTex, sampleUv).rg;
290
- if (neighborSeed.x < 0.0) continue;
291
- float d = distance(vUv, neighborSeed);
292
- if (d < bestDist) {
293
- bestDist = d;
294
- bestSeed = neighborSeed;
295
- }
540
+ }
541
+ `,Ne=`#version 300 es
542
+ in vec2 aPosition;
543
+ out vec2 vUv;
544
+ void main() {
545
+ vUv = aPosition * 0.5 + 0.5;
546
+ gl_Position = vec4(aPosition, 0.0, 1.0);
547
+ }
548
+ `,He=`#version 300 es
549
+ precision highp float;
550
+ uniform sampler2D uSeedTex;
551
+ uniform float uStepSize;
552
+ in vec2 vUv;
553
+ out vec2 fragSeed;
554
+
555
+ void main() {
556
+ vec2 bestSeed = texture(uSeedTex, vUv).rg;
557
+ float bestDist = (bestSeed.x < 0.0) ? 1.0e10 : distance(vUv, bestSeed);
558
+
559
+ for (int dy = -1; dy <= 1; dy++) {
560
+ for (int dx = -1; dx <= 1; dx++) {
561
+ if (dx == 0 && dy == 0) continue;
562
+ vec2 offset = vec2(float(dx), float(dy)) * uStepSize;
563
+ vec2 sampleUv = vUv + offset;
564
+ if (sampleUv.x < 0.0 || sampleUv.x > 1.0 || sampleUv.y < 0.0 || sampleUv.y > 1.0) continue;
565
+ vec2 neighborSeed = texture(uSeedTex, sampleUv).rg;
566
+ if (neighborSeed.x < 0.0) continue;
567
+ float d = distance(vUv, neighborSeed);
568
+ if (d < bestDist) {
569
+ bestDist = d;
570
+ bestSeed = neighborSeed;
296
571
  }
297
572
  }
298
-
299
- fragSeed = bestSeed;
300
- }
301
- `,re=`#version 300 es
302
- in vec2 aPosition;
303
- out vec2 vUv;
304
- void main() {
305
- vUv = aPosition * 0.5 + 0.5;
306
- gl_Position = vec4(aPosition, 0.0, 1.0);
307
573
  }
308
- `,oe=`#version 300 es
309
- precision highp float;
310
- uniform sampler2D uSeedTex;
311
- uniform sampler2D uMask;
312
- uniform float uBevelWidth;
313
- in vec2 vUv;
314
- out vec4 fragDist;
315
-
316
- void main() {
317
- float mask = texture(uMask, vUv).r;
318
- if (mask < 0.5) {
319
- fragDist = vec4(0.0);
320
- return;
321
- }
322
-
323
- vec2 seed = texture(uSeedTex, vUv).rg;
324
- if (seed.x < 0.0) {
325
- fragDist = vec4(1.0);
326
- return;
327
- }
328
574
 
329
- float d = distance(vUv, seed);
330
- float normalized = clamp(d / max(uBevelWidth, 0.001), 0.0, 1.0);
331
- fragDist = vec4(normalized, 0.0, 0.0, 1.0);
332
- }
333
- `,ne=`#version 300 es
334
- in vec2 aPosition;
335
- uniform vec2 uUvOffset;
336
- uniform vec2 uUvScale;
337
- out vec2 vUv;
338
- out vec2 vScreenUv;
339
- void main() {
340
- vec2 baseUv = aPosition * 0.5 + 0.5;
341
- vUv = baseUv * uUvScale + uUvOffset;
342
- vScreenUv = baseUv;
343
- gl_Position = vec4(aPosition, 0.0, 1.0);
344
- }
345
- `,se=`#version 300 es
346
- precision highp float;
347
-
348
- #define MAX_POM_STEPS 32
349
-
350
- uniform sampler2D uImage;
351
- uniform sampler2D uDepth;
352
- uniform vec2 uOffset;
353
- uniform float uStrength;
354
- uniform int uPomSteps;
355
-
356
- // Lens transform: remap depth curve for exaggerated/compressed depth feel
357
- uniform float uDepthPower; // >1 = telephoto, <1 = wide-angle
358
- uniform float uDepthScale; // multiplier on depth range
359
- uniform float uDepthBias; // shift depth origin
360
-
361
- // Depth-adaptive contrast
362
- uniform float uContrastLow;
363
- uniform float uContrastHigh;
364
- uniform float uVerticalReduction;
365
-
366
- // DOF
367
- uniform float uDofStart;
368
- uniform float uDofStrength;
369
- uniform vec2 uImageTexelSize;
370
-
371
- // Interior mood
372
- uniform float uFogDensity; // volumetric fog bias (0 = none, 0.3 = subtle)
373
- uniform vec3 uFogColor; // fog tint color
374
- uniform float uColorShift; // warm/cool grading shift
375
- uniform float uBrightnessBias; // overall brightness adjustment
376
-
377
- in vec2 vUv;
378
- in vec2 vScreenUv;
379
-
380
- layout(location = 0) out vec4 fragColor;
381
- layout(location = 1) out vec4 fragDepth;
382
-
383
- // Apply lens transform to raw depth
384
- float lensDepth(float raw) {
385
- float d = smoothstep(uContrastLow, uContrastHigh, raw);
386
- d = pow(d, uDepthPower) * uDepthScale + uDepthBias;
387
- return clamp(d, 0.0, 1.0);
575
+ fragSeed = bestSeed;
576
+ }
577
+ `,Xe=`#version 300 es
578
+ in vec2 aPosition;
579
+ out vec2 vUv;
580
+ void main() {
581
+ vUv = aPosition * 0.5 + 0.5;
582
+ gl_Position = vec4(aPosition, 0.0, 1.0);
583
+ }
584
+ `,Ge=`#version 300 es
585
+ precision highp float;
586
+ uniform sampler2D uSeedTex;
587
+ uniform sampler2D uMask;
588
+ uniform float uBevelWidth;
589
+ in vec2 vUv;
590
+ out vec4 fragDist;
591
+
592
+ void main() {
593
+ float mask = texture(uMask, vUv).r;
594
+ if (mask < 0.5) {
595
+ fragDist = vec4(0.0);
596
+ return;
388
597
  }
389
598
 
390
- float edgeFade(vec2 uv) {
391
- float margin = uStrength * 1.5;
392
- float fadeX = smoothstep(0.0, margin, uv.x) * smoothstep(0.0, margin, 1.0 - uv.x);
393
- float fadeY = smoothstep(0.0, margin, uv.y) * smoothstep(0.0, margin, 1.0 - uv.y);
394
- return fadeX * fadeY;
599
+ vec2 seed = texture(uSeedTex, vUv).rg;
600
+ if (seed.x < 0.0) {
601
+ fragDist = vec4(1.0);
602
+ return;
395
603
  }
396
604
 
397
- // POM ray-march with lens-transformed depth
398
- vec2 pomDisplace(vec2 uv, out float hitDepth) {
399
- float layerD = 1.0 / float(uPomSteps);
400
- vec2 scaledOffset = uOffset;
401
- scaledOffset.y *= uVerticalReduction;
402
- vec2 deltaUV = scaledOffset * uStrength / float(uPomSteps);
403
- float currentLayerDepth = 0.0;
404
- vec2 currentUV = uv;
405
- float fade = edgeFade(uv);
406
-
407
- for (int i = 0; i < MAX_POM_STEPS; i++) {
408
- if (i >= uPomSteps) break;
409
- float raw = texture(uDepth, currentUV).r;
410
- float depthAtUV = 1.0 - lensDepth(raw);
411
- if (currentLayerDepth > depthAtUV) {
412
- vec2 prevUV = currentUV - deltaUV;
413
- float prevLayerD = currentLayerDepth - layerD;
414
- float prevRaw = texture(uDepth, prevUV).r;
415
- float prevDepthAtUV = 1.0 - lensDepth(prevRaw);
416
- float afterD = depthAtUV - currentLayerDepth;
417
- float beforeD = prevDepthAtUV - prevLayerD;
418
- float t = afterD / (afterD - beforeD);
419
- vec2 hitUV = mix(currentUV, prevUV, t);
420
- hitDepth = mix(depthAtUV, prevDepthAtUV, t);
421
- return mix(uv, hitUV, fade);
422
- }
423
- currentUV += deltaUV;
424
- currentLayerDepth += layerD;
605
+ float d = distance(vUv, seed);
606
+ float normalized = clamp(d / max(uBevelWidth, 0.001), 0.0, 1.0);
607
+ fragDist = vec4(normalized, 0.0, 0.0, 1.0);
608
+ }
609
+ `,We=`#version 300 es
610
+ in vec2 aPosition;
611
+ uniform vec2 uUvOffset;
612
+ uniform vec2 uUvScale;
613
+ out vec2 vUv;
614
+ out vec2 vScreenUv;
615
+ void main() {
616
+ vec2 baseUv = aPosition * 0.5 + 0.5;
617
+ vUv = baseUv * uUvScale + uUvOffset;
618
+ vScreenUv = baseUv;
619
+ gl_Position = vec4(aPosition, 0.0, 1.0);
620
+ }
621
+ `,ze=`#version 300 es
622
+ precision highp float;
623
+
624
+ #define MAX_POM_STEPS 32
625
+
626
+ uniform sampler2D uImage;
627
+ uniform sampler2D uDepth;
628
+ uniform vec2 uOffset;
629
+ uniform float uStrength;
630
+ uniform int uPomSteps;
631
+
632
+ // Lens transform: remap depth curve for exaggerated/compressed depth feel
633
+ uniform float uDepthPower; // >1 = telephoto, <1 = wide-angle
634
+ uniform float uDepthScale; // multiplier on depth range
635
+ uniform float uDepthBias; // shift depth origin
636
+
637
+ // Depth-adaptive contrast
638
+ uniform float uContrastLow;
639
+ uniform float uContrastHigh;
640
+ uniform float uVerticalReduction;
641
+
642
+ // DOF
643
+ uniform float uDofStart;
644
+ uniform float uDofStrength;
645
+ uniform vec2 uImageTexelSize;
646
+
647
+ // Interior mood
648
+ uniform float uFogDensity; // volumetric fog bias (0 = none, 0.3 = subtle)
649
+ uniform vec3 uFogColor; // fog tint color
650
+ uniform float uColorShift; // warm/cool grading shift
651
+ uniform float uBrightnessBias; // overall brightness adjustment
652
+
653
+ in vec2 vUv;
654
+ in vec2 vScreenUv;
655
+
656
+ layout(location = 0) out vec4 fragColor;
657
+ layout(location = 1) out vec4 fragDepth;
658
+
659
+ // Apply lens transform to raw depth
660
+ float lensDepth(float raw) {
661
+ float d = smoothstep(uContrastLow, uContrastHigh, raw);
662
+ d = pow(d, uDepthPower) * uDepthScale + uDepthBias;
663
+ return clamp(d, 0.0, 1.0);
664
+ }
665
+
666
+ float edgeFade(vec2 uv) {
667
+ float margin = uStrength * 1.5;
668
+ float fadeX = smoothstep(0.0, margin, uv.x) * smoothstep(0.0, margin, 1.0 - uv.x);
669
+ float fadeY = smoothstep(0.0, margin, uv.y) * smoothstep(0.0, margin, 1.0 - uv.y);
670
+ return fadeX * fadeY;
671
+ }
672
+
673
+ // POM ray-march with lens-transformed depth
674
+ vec2 pomDisplace(vec2 uv, out float hitDepth) {
675
+ float layerD = 1.0 / float(uPomSteps);
676
+ vec2 scaledOffset = uOffset;
677
+ scaledOffset.y *= uVerticalReduction;
678
+ vec2 deltaUV = scaledOffset * uStrength / float(uPomSteps);
679
+ float currentLayerDepth = 0.0;
680
+ vec2 currentUV = uv;
681
+ float fade = edgeFade(uv);
682
+
683
+ for (int i = 0; i < MAX_POM_STEPS; i++) {
684
+ if (i >= uPomSteps) break;
685
+ float raw = texture(uDepth, currentUV).r;
686
+ float depthAtUV = 1.0 - lensDepth(raw);
687
+ if (currentLayerDepth > depthAtUV) {
688
+ vec2 prevUV = currentUV - deltaUV;
689
+ float prevLayerD = currentLayerDepth - layerD;
690
+ float prevRaw = texture(uDepth, prevUV).r;
691
+ float prevDepthAtUV = 1.0 - lensDepth(prevRaw);
692
+ float afterD = depthAtUV - currentLayerDepth;
693
+ float beforeD = prevDepthAtUV - prevLayerD;
694
+ float t = afterD / (afterD - beforeD);
695
+ vec2 hitUV = mix(currentUV, prevUV, t);
696
+ hitDepth = mix(depthAtUV, prevDepthAtUV, t);
697
+ return mix(uv, hitUV, fade);
425
698
  }
426
- hitDepth = 1.0 - lensDepth(texture(uDepth, currentUV).r);
427
- return mix(uv, currentUV, fade);
428
- }
429
-
430
- void main() {
431
- float hitDepth;
432
- vec2 displaced = pomDisplace(vUv, hitDepth);
433
- displaced = clamp(displaced, vec2(0.0), vec2(1.0));
434
-
435
- vec4 color = texture(uImage, displaced);
436
-
437
- // DOF: blur far objects
438
- float rawDepthAtHit = texture(uDepth, displaced).r;
439
- float lensD = lensDepth(rawDepthAtHit);
440
- float dof = smoothstep(uDofStart, 1.0, lensD) * uDofStrength;
441
- if (dof > 0.01) {
442
- vec2 ts = uImageTexelSize;
443
- vec4 blurred = (
444
- texture(uImage, displaced + vec2( ts.x, 0.0)) +
445
- texture(uImage, displaced + vec2(-ts.x, 0.0)) +
446
- texture(uImage, displaced + vec2( 0.0, ts.y)) +
447
- texture(uImage, displaced + vec2( 0.0, -ts.y)) +
448
- texture(uImage, displaced + vec2( ts.x, ts.y)) +
449
- texture(uImage, displaced + vec2(-ts.x, -ts.y)) +
450
- texture(uImage, displaced + vec2( ts.x, -ts.y)) +
451
- texture(uImage, displaced + vec2(-ts.x, ts.y))
452
- ) * 0.125;
453
- color = mix(color, blurred, dof);
454
- }
455
-
456
- // Volumetric fog bias: far objects fade into fog color
457
- float fogFactor = smoothstep(0.3, 1.0, lensD) * uFogDensity;
458
- color.rgb = mix(color.rgb, uFogColor, fogFactor);
459
-
460
- // Color grading shift: warm near, cool far (or vice versa)
461
- float gradeAmount = (lensD - 0.5) * uColorShift;
462
- color.r += gradeAmount * 0.08;
463
- color.b -= gradeAmount * 0.08;
464
-
465
- // Brightness bias
466
- color.rgb *= (1.0 + uBrightnessBias);
467
-
468
- // Subtle vignette inside portal
469
- float dist = length(vScreenUv - 0.5) * 1.4;
470
- color.rgb *= 1.0 - pow(dist, 3.0) * 0.3;
471
-
472
- fragColor = color;
473
- // Write lens-transformed depth to second attachment for boundary effects
474
- fragDepth = vec4(lensD, 0.0, 0.0, 1.0);
475
- }
476
- `,ae=`#version 300 es
477
- in vec2 aPosition;
478
- out vec2 vUv;
479
- void main() {
480
- vUv = aPosition * 0.5 + 0.5;
481
- gl_Position = vec4(aPosition, 0.0, 1.0);
699
+ currentUV += deltaUV;
700
+ currentLayerDepth += layerD;
482
701
  }
483
- `,he=`#version 300 es
484
- precision highp float;
485
- uniform sampler2D uInteriorColor;
486
- uniform sampler2D uDistField;
487
- uniform float uEdgeOcclusionWidth; // how far edge darkening extends
488
- uniform float uEdgeOcclusionStrength; // how strong (0=none, 1=full black)
489
-
490
- in vec2 vUv;
491
- out vec4 fragColor;
492
-
493
- // sRGB ↔ linear conversions for correct lighting math
494
- vec3 toLinear(vec3 s) {
495
- return mix(s / 12.92, pow((s + 0.055) / 1.055, vec3(2.4)), step(0.04045, s));
496
- }
497
- vec3 toSRGB(vec3 l) {
498
- return mix(l * 12.92, 1.055 * pow(l, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, l));
499
- }
500
-
501
- void main() {
502
- vec4 color = texture(uInteriorColor, vUv);
503
- float dist = texture(uDistField, vUv).r; // 0=edge, 1=deep interior
504
-
505
- // Emissive passthrough: preserve original video luminance.
506
- // Only apply a subtle edge occlusion ramp to sell chamfer→interior depth.
507
- vec3 linear = toLinear(color.rgb);
508
- float occ = smoothstep(0.0, uEdgeOcclusionWidth, dist);
509
- linear *= mix(1.0 - uEdgeOcclusionStrength, 1.0, occ);
510
-
511
- fragColor = vec4(toSRGB(linear), color.a);
512
- }
513
- `,le=`#version 300 es
514
- in vec2 aPosition;
515
- in vec2 aNormal;
516
- uniform float uRimWidth;
517
- uniform vec2 uMeshScale;
518
- out vec2 vNormal;
519
- out vec2 vEdgeUv; // screen-space UV for sampling FBO textures
520
- out float vEdgeDist; // 0 at edge, 1 at outer extent
521
-
522
- void main() {
523
- vec2 scaledPos = aPosition * uMeshScale;
524
- vec2 scaledNormal = normalize(aNormal * uMeshScale);
525
- vec2 pos = scaledPos + scaledNormal * uRimWidth;
526
-
527
- // Pass screen-space UV of this fragment for FBO sampling
528
- vEdgeUv = pos * 0.5 + 0.5;
529
- vNormal = scaledNormal;
530
-
531
- // Distance from the actual edge (0) to the outer rim extent (1)
532
- vEdgeDist = length(pos - scaledPos) / max(uRimWidth, 0.001);
533
-
534
- gl_Position = vec4(pos, 0.0, 1.0);
535
- }
536
- `,ce=`#version 300 es
537
- precision highp float;
538
-
539
- uniform sampler2D uInteriorColor;
540
- uniform sampler2D uInteriorDepth;
541
- uniform sampler2D uDistField;
542
- uniform float uRimIntensity;
543
- uniform vec3 uRimColor;
544
- uniform float uRefractionStrength;
545
- uniform float uChromaticStrength;
546
- uniform float uOcclusionIntensity;
547
- uniform vec2 uTexelSize; // 1.0 / viewport resolution
548
-
549
- // Volumetric edge wall
550
- uniform float uEdgeThickness;
551
- uniform float uEdgeSpecular;
552
- uniform vec3 uEdgeColor;
553
- uniform vec2 uLightDir;
554
- uniform float uBevelIntensity;
555
-
556
- in vec2 vNormal;
557
- in vec2 vEdgeUv;
558
- in float vEdgeDist;
559
- out vec4 fragColor;
560
-
561
- void main() {
562
- // Clamp UV to valid range for texture sampling
563
- vec2 sampleUv = clamp(vEdgeUv, vec2(0.001), vec2(0.999));
564
-
565
- // Sample interior depth at this boundary location
566
- float interiorDepth = texture(uInteriorDepth, sampleUv).r;
567
-
568
- // === DEPTH-REACTIVE RIM (structural seam) ===
569
- float depthReactivity = 1.0 - interiorDepth; // 1=near, 0=far
570
- float rimProfile = 1.0 - smoothstep(0.0, 1.0, vEdgeDist);
571
- rimProfile = pow(rimProfile, 1.5); // sharper falloff = more structural
572
-
573
- float depthPressure = mix(0.2, 1.0, depthReactivity * depthReactivity);
574
- float rim = rimProfile * depthPressure * uRimIntensity;
575
-
576
- vec3 rimCol = uRimColor;
577
- rimCol.r += depthReactivity * 0.15;
578
- rimCol.g += depthReactivity * 0.05;
579
-
580
- // === REFRACTION DISTORTION ===
581
- vec2 ts = uTexelSize * 3.0;
582
- float dLeft = texture(uInteriorDepth, sampleUv + vec2(-ts.x, 0.0)).r;
583
- float dRight = texture(uInteriorDepth, sampleUv + vec2( ts.x, 0.0)).r;
584
- float dUp = texture(uInteriorDepth, sampleUv + vec2(0.0, ts.y)).r;
585
- float dDown = texture(uInteriorDepth, sampleUv + vec2(0.0, -ts.y)).r;
586
- vec2 depthGradient = vec2(dRight - dLeft, dUp - dDown);
587
- vec2 refractUv = sampleUv + depthGradient * uRefractionStrength * rimProfile;
588
- refractUv = clamp(refractUv, vec2(0.001), vec2(0.999));
589
-
590
- vec4 refractedColor = texture(uInteriorColor, refractUv);
591
-
592
- // === CHROMATIC FRINGE ===
593
- float chromaticAmount = uChromaticStrength * depthReactivity * rimProfile;
594
- vec2 chromaticDir = vNormal * chromaticAmount;
595
- float cr = texture(uInteriorColor, refractUv + chromaticDir).r;
596
- float cg = refractedColor.g;
597
- float cb = texture(uInteriorColor, refractUv - chromaticDir).b;
598
- vec3 chromaticColor = vec3(cr, cg, cb);
599
-
600
- // === OCCLUSION CONTACT SHADOW ===
601
- float occlusionAmount = smoothstep(0.4, 0.0, interiorDepth) * uOcclusionIntensity * rimProfile;
602
-
603
- // === VOLUMETRIC EDGE WALL ===
604
- // Sample distance field to get the inner-side distance at this boundary location
605
- float edgeDist = texture(uDistField, sampleUv).r;
606
- float wallZone = smoothstep(uEdgeThickness, 0.0, edgeDist) * rimProfile;
607
-
608
- // Wall lighting from distance field gradient
609
- vec2 dtx = vec2(1.0) / vec2(textureSize(uDistField, 0));
610
- float wdL = texture(uDistField, sampleUv + vec2(-dtx.x, 0.0)).r;
611
- float wdR = texture(uDistField, sampleUv + vec2( dtx.x, 0.0)).r;
612
- float wdU = texture(uDistField, sampleUv + vec2(0.0, dtx.y)).r;
613
- float wdD = texture(uDistField, sampleUv + vec2(0.0, -dtx.y)).r;
614
- vec2 wallNormal = vec2(wdR - wdL, wdU - wdD);
615
- float wnLen = length(wallNormal);
616
- if (wnLen > 0.001) wallNormal /= wnLen;
617
-
618
- float wallSpec = pow(max(dot(wallNormal, uLightDir), 0.0), 16.0) * uEdgeSpecular;
619
- vec3 wallColor = mix(refractedColor.rgb * 0.4, uEdgeColor, 0.3);
620
- wallColor += vec3(wallSpec);
621
-
622
- // === COMPOSITE ===
623
- vec3 color = mix(refractedColor.rgb, chromaticColor, min(chromaticAmount * 10.0, 1.0));
624
- color *= (1.0 - occlusionAmount * 0.4);
625
-
626
- // Blend in volumetric wall
627
- color = mix(color, wallColor, wallZone * uBevelIntensity);
628
-
629
- // Add rim energy on top
630
- color += rimCol * rim;
631
-
632
- // Alpha: rim edge fades out
633
- float alpha = rimProfile * max(rim, occlusionAmount + chromaticAmount * 5.0 + wallZone * 0.5);
634
- alpha = clamp(alpha, 0.0, 1.0);
635
-
636
- fragColor = vec4(color * alpha, alpha);
637
- }
638
- `,ue=`#version 300 es
639
- in vec2 aPosition;
640
- in vec3 aNormal3;
641
- in float aLerpT; // 0 = inner (at silhouette), 1 = outer edge
642
- uniform vec2 uMeshScale;
643
- out vec3 vNormal;
644
- out vec2 vScreenUv;
645
- out float vLerpT;
646
-
647
- void main() {
648
- vec2 sp = aPosition * uMeshScale;
649
- vNormal = aNormal3;
650
- vScreenUv = sp * 0.5 + 0.5;
651
- vLerpT = aLerpT;
652
- gl_Position = vec4(sp, 0.0, 1.0);
653
- }
654
- `,fe=`#version 300 es
655
- precision highp float;
656
- uniform vec3 uLightDir3;
657
- uniform vec3 uChamferColor;
658
- uniform float uChamferAmbient;
659
- uniform float uChamferSpecular;
660
- uniform float uChamferShininess;
661
- uniform sampler2D uInteriorColor;
662
- uniform vec2 uTexelSize; // 1 / viewport resolution
663
-
664
- in vec3 vNormal;
665
- in vec2 vScreenUv;
666
- in float vLerpT;
667
- out vec4 fragColor;
668
-
669
- vec3 toLinear(vec3 s) {
670
- return mix(s / 12.92, pow((s + 0.055) / 1.055, vec3(2.4)), step(0.04045, s));
671
- }
672
- vec3 toSRGB(vec3 l) {
673
- return mix(l * 12.92, 1.055 * pow(l, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, l));
674
- }
675
-
676
- // Approximate gaussian blur via 13-tap poisson disc, radius scaled by vLerpT.
677
- vec3 blurSample(vec2 center, float radius) {
678
- // Poisson disc offsets (normalized to unit circle)
679
- const vec2 offsets[12] = vec2[12](
680
- vec2(-0.326, -0.406), vec2(-0.840, -0.074), vec2(-0.696, 0.457),
681
- vec2(-0.203, 0.621), vec2( 0.962, -0.195), vec2( 0.473, -0.480),
682
- vec2( 0.519, 0.767), vec2( 0.185, -0.893), vec2( 0.507, 0.064),
683
- vec2(-0.321, -0.860), vec2(-0.791, 0.557), vec2( 0.330, 0.418)
684
- );
685
- vec3 sum = texture(uInteriorColor, center).rgb;
686
- for (int i = 0; i < 12; i++) {
687
- vec2 uv = center + offsets[i] * radius;
688
- uv = clamp(uv, vec2(0.001), vec2(0.999));
689
- sum += texture(uInteriorColor, uv).rgb;
690
- }
691
- return sum / 13.0;
702
+ hitDepth = 1.0 - lensDepth(texture(uDepth, currentUV).r);
703
+ return mix(uv, currentUV, fade);
704
+ }
705
+
706
+ void main() {
707
+ float hitDepth;
708
+ vec2 displaced = pomDisplace(vUv, hitDepth);
709
+ displaced = clamp(displaced, vec2(0.0), vec2(1.0));
710
+
711
+ vec4 color = texture(uImage, displaced);
712
+
713
+ // DOF: blur far objects
714
+ float rawDepthAtHit = texture(uDepth, displaced).r;
715
+ float lensD = lensDepth(rawDepthAtHit);
716
+ float dof = smoothstep(uDofStart, 1.0, lensD) * uDofStrength;
717
+ if (dof > 0.01) {
718
+ vec2 ts = uImageTexelSize;
719
+ vec4 blurred = (
720
+ texture(uImage, displaced + vec2( ts.x, 0.0)) +
721
+ texture(uImage, displaced + vec2(-ts.x, 0.0)) +
722
+ texture(uImage, displaced + vec2( 0.0, ts.y)) +
723
+ texture(uImage, displaced + vec2( 0.0, -ts.y)) +
724
+ texture(uImage, displaced + vec2( ts.x, ts.y)) +
725
+ texture(uImage, displaced + vec2(-ts.x, -ts.y)) +
726
+ texture(uImage, displaced + vec2( ts.x, -ts.y)) +
727
+ texture(uImage, displaced + vec2(-ts.x, ts.y))
728
+ ) * 0.125;
729
+ color = mix(color, blurred, dof);
692
730
  }
693
731
 
694
- void main() {
695
- vec3 N = normalize(vNormal);
696
- vec3 L = normalize(uLightDir3);
697
- vec3 V = vec3(0.0, 0.0, -1.0); // orthographic view direction
698
-
699
- // Blinn-Phong lighting in linear space
700
- float diff = max(dot(N, L), 0.0);
701
- vec3 H = normalize(L + V);
702
- float spec = pow(max(dot(N, H), 0.0), uChamferShininess) * uChamferSpecular;
703
-
704
- // Sample interior video with progressive blur (sharper at inner edge)
705
- vec2 uv = clamp(vScreenUv, vec2(0.001), vec2(0.999));
706
- float blurRadius = vLerpT * 12.0 * length(uTexelSize);
707
- vec3 videoSample = blurRadius > 0.0001
708
- ? blurSample(uv, blurRadius)
709
- : texture(uInteriorColor, uv).rgb;
710
-
711
- // Base color: video tinted through chamfer color (like frosted glass)
712
- vec3 video = toLinear(videoSample);
713
- vec3 tint = toLinear(uChamferColor);
714
- // Blend: mostly video near inner edge, more tinted at outer edge
715
- vec3 base = mix(video, video * tint * 3.0, vLerpT * 0.5);
716
-
717
- // Apply Blinn-Phong
718
- vec3 lit = base * (uChamferAmbient + (1.0 - uChamferAmbient) * diff) + vec3(spec);
719
- fragColor = vec4(toSRGB(lit), 1.0);
732
+ // Volumetric fog bias: far objects fade into fog color
733
+ float fogFactor = smoothstep(0.3, 1.0, lensD) * uFogDensity;
734
+ color.rgb = mix(color.rgb, uFogColor, fogFactor);
735
+
736
+ // Color grading shift: warm near, cool far (or vice versa)
737
+ float gradeAmount = (lensD - 0.5) * uColorShift;
738
+ color.r += gradeAmount * 0.08;
739
+ color.b -= gradeAmount * 0.08;
740
+
741
+ // Brightness bias
742
+ color.rgb *= (1.0 + uBrightnessBias);
743
+
744
+ // Subtle vignette inside portal
745
+ float dist = length(vScreenUv - 0.5) * 1.4;
746
+ color.rgb *= 1.0 - pow(dist, 3.0) * 0.3;
747
+
748
+ fragColor = color;
749
+ // Write lens-transformed depth to second attachment for boundary effects
750
+ fragDepth = vec4(lensD, 0.0, 0.0, 1.0);
751
+ }
752
+ `,qe=`#version 300 es
753
+ in vec2 aPosition;
754
+ out vec2 vUv;
755
+ void main() {
756
+ vUv = aPosition * 0.5 + 0.5;
757
+ gl_Position = vec4(aPosition, 0.0, 1.0);
758
+ }
759
+ `,Ye=`#version 300 es
760
+ precision highp float;
761
+ uniform sampler2D uInteriorColor;
762
+ uniform sampler2D uDistField;
763
+ uniform float uEdgeOcclusionWidth; // how far edge darkening extends
764
+ uniform float uEdgeOcclusionStrength; // how strong (0=none, 1=full black)
765
+
766
+ in vec2 vUv;
767
+ out vec4 fragColor;
768
+
769
+ // sRGB <-> linear conversions for correct lighting math
770
+ vec3 toLinear(vec3 s) {
771
+ return mix(s / 12.92, pow((s + 0.055) / 1.055, vec3(2.4)), step(0.04045, s));
772
+ }
773
+ vec3 toSRGB(vec3 l) {
774
+ return mix(l * 12.92, 1.055 * pow(l, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, l));
775
+ }
776
+
777
+ void main() {
778
+ vec4 color = texture(uInteriorColor, vUv);
779
+ float dist = texture(uDistField, vUv).r; // 0=edge, 1=deep interior
780
+
781
+ // Emissive passthrough: preserve original video luminance.
782
+ // Only apply a subtle edge occlusion ramp to sell chamfer->interior depth.
783
+ vec3 linear = toLinear(color.rgb);
784
+ float occ = smoothstep(0.0, uEdgeOcclusionWidth, dist);
785
+ linear *= mix(1.0 - uEdgeOcclusionStrength, 1.0, occ);
786
+
787
+ fragColor = vec4(toSRGB(linear), color.a);
788
+ }
789
+ `,je=`#version 300 es
790
+ in vec2 aPosition;
791
+ in vec2 aNormal;
792
+ uniform float uRimWidth;
793
+ uniform vec2 uMeshScale;
794
+ out vec2 vNormal;
795
+ out vec2 vEdgeUv; // screen-space UV for sampling FBO textures
796
+ out float vEdgeDist; // 0 at edge, 1 at outer extent
797
+
798
+ void main() {
799
+ vec2 scaledPos = aPosition * uMeshScale;
800
+ vec2 scaledNormal = normalize(aNormal * uMeshScale);
801
+ vec2 pos = scaledPos + scaledNormal * uRimWidth;
802
+
803
+ // Pass screen-space UV of this fragment for FBO sampling
804
+ vEdgeUv = pos * 0.5 + 0.5;
805
+ vNormal = scaledNormal;
806
+
807
+ // Distance from the actual edge (0) to the outer rim extent (1)
808
+ vEdgeDist = length(pos - scaledPos) / max(uRimWidth, 0.001);
809
+
810
+ gl_Position = vec4(pos, 0.0, 1.0);
811
+ }
812
+ `,Ze=`#version 300 es
813
+ precision highp float;
814
+
815
+ uniform sampler2D uInteriorColor;
816
+ uniform sampler2D uInteriorDepth;
817
+ uniform sampler2D uDistField;
818
+ uniform float uRimIntensity;
819
+ uniform vec3 uRimColor;
820
+ uniform float uRefractionStrength;
821
+ uniform float uChromaticStrength;
822
+ uniform float uOcclusionIntensity;
823
+ uniform vec2 uTexelSize; // 1.0 / viewport resolution
824
+
825
+ // Volumetric edge wall
826
+ uniform float uEdgeThickness;
827
+ uniform float uEdgeSpecular;
828
+ uniform vec3 uEdgeColor;
829
+ uniform vec2 uLightDir;
830
+ uniform float uBevelIntensity;
831
+
832
+ in vec2 vNormal;
833
+ in vec2 vEdgeUv;
834
+ in float vEdgeDist;
835
+ out vec4 fragColor;
836
+
837
+ void main() {
838
+ // Clamp UV to valid range for texture sampling
839
+ vec2 sampleUv = clamp(vEdgeUv, vec2(0.001), vec2(0.999));
840
+
841
+ // Sample interior depth at this boundary location
842
+ float interiorDepth = texture(uInteriorDepth, sampleUv).r;
843
+
844
+ // === DEPTH-REACTIVE RIM (structural seam) ===
845
+ float depthReactivity = 1.0 - interiorDepth; // 1=near, 0=far
846
+ float rimProfile = 1.0 - smoothstep(0.0, 1.0, vEdgeDist);
847
+ rimProfile = pow(rimProfile, 1.5); // sharper falloff = more structural
848
+
849
+ float depthPressure = mix(0.2, 1.0, depthReactivity * depthReactivity);
850
+ float rim = rimProfile * depthPressure * uRimIntensity;
851
+
852
+ vec3 rimCol = uRimColor;
853
+ rimCol.r += depthReactivity * 0.15;
854
+ rimCol.g += depthReactivity * 0.05;
855
+
856
+ // === REFRACTION DISTORTION ===
857
+ vec2 ts = uTexelSize * 3.0;
858
+ float dLeft = texture(uInteriorDepth, sampleUv + vec2(-ts.x, 0.0)).r;
859
+ float dRight = texture(uInteriorDepth, sampleUv + vec2( ts.x, 0.0)).r;
860
+ float dUp = texture(uInteriorDepth, sampleUv + vec2(0.0, ts.y)).r;
861
+ float dDown = texture(uInteriorDepth, sampleUv + vec2(0.0, -ts.y)).r;
862
+ vec2 depthGradient = vec2(dRight - dLeft, dUp - dDown);
863
+ vec2 refractUv = sampleUv + depthGradient * uRefractionStrength * rimProfile;
864
+ refractUv = clamp(refractUv, vec2(0.001), vec2(0.999));
865
+
866
+ vec4 refractedColor = texture(uInteriorColor, refractUv);
867
+
868
+ // === CHROMATIC FRINGE ===
869
+ float chromaticAmount = uChromaticStrength * depthReactivity * rimProfile;
870
+ vec2 chromaticDir = vNormal * chromaticAmount;
871
+ float cr = texture(uInteriorColor, refractUv + chromaticDir).r;
872
+ float cg = refractedColor.g;
873
+ float cb = texture(uInteriorColor, refractUv - chromaticDir).b;
874
+ vec3 chromaticColor = vec3(cr, cg, cb);
875
+
876
+ // === OCCLUSION CONTACT SHADOW ===
877
+ float occlusionAmount = smoothstep(0.4, 0.0, interiorDepth) * uOcclusionIntensity * rimProfile;
878
+
879
+ // === VOLUMETRIC EDGE WALL ===
880
+ // Sample distance field to get the inner-side distance at this boundary location
881
+ float edgeDist = texture(uDistField, sampleUv).r;
882
+ float wallZone = smoothstep(uEdgeThickness, 0.0, edgeDist) * rimProfile;
883
+
884
+ // Wall lighting from distance field gradient
885
+ vec2 dtx = vec2(1.0) / vec2(textureSize(uDistField, 0));
886
+ float wdL = texture(uDistField, sampleUv + vec2(-dtx.x, 0.0)).r;
887
+ float wdR = texture(uDistField, sampleUv + vec2( dtx.x, 0.0)).r;
888
+ float wdU = texture(uDistField, sampleUv + vec2(0.0, dtx.y)).r;
889
+ float wdD = texture(uDistField, sampleUv + vec2(0.0, -dtx.y)).r;
890
+ vec2 wallNormal = vec2(wdR - wdL, wdU - wdD);
891
+ float wnLen = length(wallNormal);
892
+ if (wnLen > 0.001) wallNormal /= wnLen;
893
+
894
+ float wallSpec = pow(max(dot(wallNormal, uLightDir), 0.0), 16.0) * uEdgeSpecular;
895
+ vec3 wallColor = mix(refractedColor.rgb * 0.4, uEdgeColor, 0.3);
896
+ wallColor += vec3(wallSpec);
897
+
898
+ // === COMPOSITE ===
899
+ vec3 color = mix(refractedColor.rgb, chromaticColor, min(chromaticAmount * 10.0, 1.0));
900
+ color *= (1.0 - occlusionAmount * 0.4);
901
+
902
+ // Blend in volumetric wall
903
+ color = mix(color, wallColor, wallZone * uBevelIntensity);
904
+
905
+ // Add rim energy on top
906
+ color += rimCol * rim;
907
+
908
+ // Alpha: rim edge fades out
909
+ float alpha = rimProfile * max(rim, occlusionAmount + chromaticAmount * 5.0 + wallZone * 0.5);
910
+ alpha = clamp(alpha, 0.0, 1.0);
911
+
912
+ fragColor = vec4(color * alpha, alpha);
913
+ }
914
+ `,$e=`#version 300 es
915
+ in vec2 aPosition;
916
+ in vec3 aNormal3;
917
+ in float aLerpT; // 0 = inner (at silhouette), 1 = outer edge
918
+ uniform vec2 uMeshScale;
919
+ out vec3 vNormal;
920
+ out vec2 vScreenUv;
921
+ out float vLerpT;
922
+
923
+ void main() {
924
+ vec2 sp = aPosition * uMeshScale;
925
+ vNormal = aNormal3;
926
+ vScreenUv = sp * 0.5 + 0.5;
927
+ vLerpT = aLerpT;
928
+ gl_Position = vec4(sp, 0.0, 1.0);
929
+ }
930
+ `,Ke=`#version 300 es
931
+ precision highp float;
932
+ uniform vec3 uLightDir3;
933
+ uniform vec3 uChamferColor;
934
+ uniform float uChamferAmbient;
935
+ uniform float uChamferSpecular;
936
+ uniform float uChamferShininess;
937
+ uniform sampler2D uInteriorColor;
938
+ uniform vec2 uTexelSize; // 1 / viewport resolution
939
+
940
+ in vec3 vNormal;
941
+ in vec2 vScreenUv;
942
+ in float vLerpT;
943
+ out vec4 fragColor;
944
+
945
+ vec3 toLinear(vec3 s) {
946
+ return mix(s / 12.92, pow((s + 0.055) / 1.055, vec3(2.4)), step(0.04045, s));
947
+ }
948
+ vec3 toSRGB(vec3 l) {
949
+ return mix(l * 12.92, 1.055 * pow(l, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, l));
950
+ }
951
+
952
+ // Approximate gaussian blur via 13-tap poisson disc, radius scaled by vLerpT.
953
+ vec3 blurSample(vec2 center, float radius) {
954
+ // Poisson disc offsets (normalized to unit circle)
955
+ const vec2 offsets[12] = vec2[12](
956
+ vec2(-0.326, -0.406), vec2(-0.840, -0.074), vec2(-0.696, 0.457),
957
+ vec2(-0.203, 0.621), vec2( 0.962, -0.195), vec2( 0.473, -0.480),
958
+ vec2( 0.519, 0.767), vec2( 0.185, -0.893), vec2( 0.507, 0.064),
959
+ vec2(-0.321, -0.860), vec2(-0.791, 0.557), vec2( 0.330, 0.418)
960
+ );
961
+ vec3 sum = texture(uInteriorColor, center).rgb;
962
+ for (int i = 0; i < 12; i++) {
963
+ vec2 uv = center + offsets[i] * radius;
964
+ uv = clamp(uv, vec2(0.001), vec2(0.999));
965
+ sum += texture(uInteriorColor, uv).rgb;
720
966
  }
721
- `;function L(r,t,e){const i=r.createShader(t);if(!i)throw new Error("Failed to create shader.");if(r.shaderSource(i,e),r.compileShader(i),!r.getShaderParameter(i,r.COMPILE_STATUS)){const o=r.getShaderInfoLog(i)??"";throw r.deleteShader(i),new Error(`Shader compilation failed:
722
- ${o}`)}return i}function O(r,t,e){const i=r.createProgram();if(!i)throw new Error("Failed to create program.");if(r.attachShader(i,t),r.attachShader(i,e),r.linkProgram(i),!r.getProgramParameter(i,r.LINK_STATUS)){const o=r.getProgramInfoLog(i)??"";throw r.deleteProgram(i),new Error(`Program linking failed:
723
- ${o}`)}return r.detachShader(i,t),r.detachShader(i,e),r.deleteShader(t),r.deleteShader(e),i}function de(r){const t=[];let e=0;for(let i=0;i<r.length-2;i+=2){const o=r[i],n=r[i+1],s=r[i+2],h=r[i+3],a=s-o,l=h-n,u=Math.sqrt(a*a+l*l);if(u<1e-6)continue;const f=-l/u,c=a/u;t.push(o,n,f,c,o,n,-f,-c,s,h,f,c,s,h,f,c,o,n,-f,-c,s,h,-f,-c),e+=6}return{vertices:new Float32Array(t),count:e}}function me(r,t,e,i,o){if(i<=0)return{vertices:new Float32Array(0),count:0};const n=o*Math.PI/180,s=-Math.cos(n),h=Math.sin(n),a=[];let l=0;for(let u=0;u<t.length;u++){const f=t[u],d=((u+1<t.length?t[u+1]:r.length)-f)/2;if(d<3)continue;const m=d-1;let U=0;for(let x=0;x<m;x++){const A=f+x*2,S=r[A],D=r[A+1],P=r[A+2],_=r[A+3];U+=S*_-P*D}const y=U>=0?1:-1,E=[],p=[];for(let x=0;x<m;x++){const A=f+x*2,S=r[A+2]-r[A],D=r[A+3]-r[A+1],P=Math.sqrt(S*S+D*D);P<1e-8?(E.push(x>0?E[x-1]:0),p.push(x>0?p[x-1]:0)):(E.push(-D/P*y),p.push(S/P*y))}const v=[],T=[];for(let x=0;x<m;x++){const A=(x-1+m)%m;let S=E[A]+E[x],D=p[A]+p[x];const P=Math.sqrt(S*S+D*D);P>1e-8?(S/=P,D/=P):(S=E[x],D=p[x]),v.push(S),T.push(D)}for(let x=0;x<m;x++){const A=x,S=(x+1)%m,D=f+x*2,P=f+(x+1)%m*2,_=r[D],k=r[D+1],F=r[P],R=r[P+1],C=v[A]*h,M=T[A]*h,B=s,I=v[S]*h,G=T[S]*h,tt=s,ct=_+v[A]*i,Lt=k+T[A]*i,ze=F+v[S]*i,We=R+T[S]*i;a.push(_,k,C,M,B,0),a.push(ct,Lt,C,M,B,1),a.push(F,R,I,G,tt,0),a.push(F,R,I,G,tt,0),a.push(ct,Lt,C,M,B,1),a.push(ze,We,I,G,tt,1),l+=6}}return{vertices:new Float32Array(a),count:l}}class ft{static RESIZE_DEBOUNCE_MS=100;canvas;gl=null;container;stencilProgram=null;maskProgram=null;jfaSeedProgram=null;jfaFloodProgram=null;jfaDistProgram=null;interiorProgram=null;compositeProgram=null;boundaryProgram=null;chamferProgram=null;stencilUniforms={};maskUniforms={};jfaSeedUniforms={};jfaFloodUniforms={};jfaDistUniforms={};interiorUniforms={};compositeUniforms={};boundaryUniforms={};chamferUniforms={};quadVao=null;stencilVao=null;stencilIndexCount=0;maskVao=null;boundaryVao=null;boundaryVertexCount=0;chamferVao=null;chamferVertexCount=0;videoTexture=null;depthTexture=null;interiorFbo=null;interiorColorTex=null;interiorDepthTex=null;fboWidth=0;fboHeight=0;maskFbo=null;maskTex=null;jfaPingFbo=null;jfaPingTex=null;jfaPongFbo=null;jfaPongTex=null;distFbo=null;distTex=null;jfaWidth=0;jfaHeight=0;distFieldDirty=!0;hasColorBufferFloat=!1;depthWidth=0;depthHeight=0;videoAspect=1.7777777777777777;meshAspect=1;meshScaleX=.65;meshScaleY=.65;readDepth=null;readInput=null;playbackVideo=null;onVideoFrame=null;animationFrameHandle=0;rvfcHandle=0;rvfcSupported=!1;resizeObserver=null;resizeTimer=null;uvOffset=[0,0];uvScale=[1,1];lightDirX=-.707;lightDirY=.707;lightDir3=[-.5,.7,-.3];config;constructor(t,e){this.container=t,this.config={...e};const i=this.config.bevelLightAngle*Math.PI/180;this.lightDirX=Math.cos(i),this.lightDirY=Math.sin(i);const o=this.config.lightDirection,n=Math.sqrt(o[0]*o[0]+o[1]*o[1]+o[2]*o[2]);n>1e-6&&(this.lightDir3=[o[0]/n,o[1]/n,o[2]/n]),this.canvas=document.createElement("canvas");const s=this.canvas.getContext("webgl2",{antialias:!0,alpha:!0,premultipliedAlpha:!0,stencil:!0,desynchronized:!0,powerPreference:"high-performance"});if(!s)throw new Error("WebGL 2 is not supported.");this.gl=s,"drawingBufferColorSpace"in s&&(s.drawingBufferColorSpace="srgb"),this.hasColorBufferFloat=!!s.getExtension("EXT_color_buffer_float"),s.clearColor(0,0,0,0),s.pixelStorei(s.UNPACK_FLIP_Y_WEBGL,!0),this.container.appendChild(this.canvas),this.initGPUResources(),this.setupResizeHandling(),this.canvas.addEventListener("webglcontextlost",this.handleContextLost),this.canvas.addEventListener("webglcontextrestored",this.handleContextRestored)}initialize(t,e,i,o){const n=this.gl;n&&(this.disposeTextures(),this.disposeFBO(),this.disposeJFA(),this.disposeStencilGeometry(),this.disposeBoundaryGeometry(),this.disposeChamferGeometry(),this.videoAspect=t.videoWidth/t.videoHeight,this.meshAspect=o.aspect,this.depthWidth=e,this.depthHeight=i,this.videoTexture=n.createTexture(),n.activeTexture(n.TEXTURE0),n.bindTexture(n.TEXTURE_2D,this.videoTexture),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),this.depthTexture=n.createTexture(),n.activeTexture(n.TEXTURE1),n.bindTexture(n.TEXTURE_2D,this.depthTexture),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),n.texStorage2D(n.TEXTURE_2D,1,n.R8,e,i),this.uploadStencilMesh(o),this.uploadMaskMesh(o),this.uploadBoundaryMesh(o),this.uploadChamferMesh(o),this.interiorProgram&&(n.useProgram(this.interiorProgram),n.uniform1i(this.interiorUniforms.uImage,0),n.uniform1i(this.interiorUniforms.uDepth,1),n.uniform1f(this.interiorUniforms.uStrength,this.config.parallaxStrength),n.uniform1i(this.interiorUniforms.uPomSteps,this.config.pomSteps),n.uniform1f(this.interiorUniforms.uDepthPower,this.config.depthPower),n.uniform1f(this.interiorUniforms.uDepthScale,this.config.depthScale),n.uniform1f(this.interiorUniforms.uDepthBias,this.config.depthBias),n.uniform1f(this.interiorUniforms.uContrastLow,this.config.contrastLow),n.uniform1f(this.interiorUniforms.uContrastHigh,this.config.contrastHigh),n.uniform1f(this.interiorUniforms.uVerticalReduction,this.config.verticalReduction),n.uniform1f(this.interiorUniforms.uDofStart,this.config.dofStart),n.uniform1f(this.interiorUniforms.uDofStrength,this.config.dofStrength),n.uniform2f(this.interiorUniforms.uImageTexelSize,1/t.videoWidth,1/t.videoHeight),n.uniform1f(this.interiorUniforms.uFogDensity,this.config.fogDensity),n.uniform3f(this.interiorUniforms.uFogColor,...this.config.fogColor),n.uniform1f(this.interiorUniforms.uColorShift,this.config.colorShift),n.uniform1f(this.interiorUniforms.uBrightnessBias,this.config.brightnessBias)),this.compositeProgram&&(n.useProgram(this.compositeProgram),n.uniform1i(this.compositeUniforms.uInteriorColor,2),n.uniform1i(this.compositeUniforms.uDistField,4),n.uniform1f(this.compositeUniforms.uEdgeOcclusionWidth,this.config.edgeOcclusionWidth),n.uniform1f(this.compositeUniforms.uEdgeOcclusionStrength,this.config.edgeOcclusionStrength)),this.chamferProgram&&(n.useProgram(this.chamferProgram),n.uniform3f(this.chamferUniforms.uLightDir3,...this.lightDir3),n.uniform3f(this.chamferUniforms.uChamferColor,...this.config.chamferColor),n.uniform1f(this.chamferUniforms.uChamferAmbient,this.config.chamferAmbient),n.uniform1f(this.chamferUniforms.uChamferSpecular,this.config.chamferSpecular),n.uniform1f(this.chamferUniforms.uChamferShininess,this.config.chamferShininess),n.uniform1i(this.chamferUniforms.uInteriorColor,2)),this.boundaryProgram&&(n.useProgram(this.boundaryProgram),n.uniform1i(this.boundaryUniforms.uInteriorColor,2),n.uniform1i(this.boundaryUniforms.uInteriorDepth,3),n.uniform1i(this.boundaryUniforms.uDistField,4),n.uniform1f(this.boundaryUniforms.uRimIntensity,this.config.rimLightIntensity),n.uniform3f(this.boundaryUniforms.uRimColor,...this.config.rimLightColor),n.uniform1f(this.boundaryUniforms.uRefractionStrength,this.config.refractionStrength),n.uniform1f(this.boundaryUniforms.uChromaticStrength,this.config.chromaticStrength),n.uniform1f(this.boundaryUniforms.uOcclusionIntensity,this.config.occlusionIntensity),n.uniform1f(this.boundaryUniforms.uEdgeThickness,this.config.edgeThickness),n.uniform1f(this.boundaryUniforms.uEdgeSpecular,this.config.edgeSpecular),n.uniform3f(this.boundaryUniforms.uEdgeColor,...this.config.edgeColor),n.uniform2f(this.boundaryUniforms.uLightDir,this.lightDirX,this.lightDirY),n.uniform1f(this.boundaryUniforms.uBevelIntensity,this.config.bevelIntensity)),this.recalculateViewportLayout())}uploadStencilMesh(t){const e=this.gl;if(!e||!this.stencilProgram)return;this.stencilVao=e.createVertexArray(),e.bindVertexArray(this.stencilVao);const i=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,i),e.bufferData(e.ARRAY_BUFFER,t.vertices,e.STATIC_DRAW);const o=e.getAttribLocation(this.stencilProgram,"aPosition");e.enableVertexAttribArray(o),e.vertexAttribPointer(o,2,e.FLOAT,!1,0,0);const n=e.createBuffer();e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,n),e.bufferData(e.ELEMENT_ARRAY_BUFFER,t.indices,e.STATIC_DRAW),this.stencilIndexCount=t.indices.length,e.bindVertexArray(null)}uploadMaskMesh(t){const e=this.gl;if(!e||!this.maskProgram)return;this.maskVao=e.createVertexArray(),e.bindVertexArray(this.maskVao);const i=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,i),e.bufferData(e.ARRAY_BUFFER,t.vertices,e.STATIC_DRAW);const o=e.getAttribLocation(this.maskProgram,"aPosition");e.enableVertexAttribArray(o),e.vertexAttribPointer(o,2,e.FLOAT,!1,0,0);const n=e.createBuffer();e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,n),e.bufferData(e.ELEMENT_ARRAY_BUFFER,t.indices,e.STATIC_DRAW),e.bindVertexArray(null)}uploadBoundaryMesh(t){const e=this.gl;if(!e||!this.boundaryProgram)return;const i=de(t.edgeVertices);if(i.count===0)return;this.boundaryVao=e.createVertexArray(),e.bindVertexArray(this.boundaryVao);const o=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,o),e.bufferData(e.ARRAY_BUFFER,i.vertices,e.STATIC_DRAW);const n=16,s=e.getAttribLocation(this.boundaryProgram,"aPosition");e.enableVertexAttribArray(s),e.vertexAttribPointer(s,2,e.FLOAT,!1,n,0);const h=e.getAttribLocation(this.boundaryProgram,"aNormal");h>=0&&(e.enableVertexAttribArray(h),e.vertexAttribPointer(h,2,e.FLOAT,!1,n,8)),this.boundaryVertexCount=i.count,e.bindVertexArray(null)}uploadChamferMesh(t){const e=this.gl;if(!e||!this.chamferProgram||this.config.chamferWidth<=0)return;const i=me(t.edgeVertices,t.contourOffsets,t.contourIsHole,this.config.chamferWidth,this.config.chamferAngle);if(i.count===0)return;this.chamferVao=e.createVertexArray(),e.bindVertexArray(this.chamferVao);const o=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,o),e.bufferData(e.ARRAY_BUFFER,i.vertices,e.STATIC_DRAW);const n=24,s=e.getAttribLocation(this.chamferProgram,"aPosition");e.enableVertexAttribArray(s),e.vertexAttribPointer(s,2,e.FLOAT,!1,n,0);const h=e.getAttribLocation(this.chamferProgram,"aNormal3");h>=0&&(e.enableVertexAttribArray(h),e.vertexAttribPointer(h,3,e.FLOAT,!1,n,8));const a=e.getAttribLocation(this.chamferProgram,"aLerpT");a>=0&&(e.enableVertexAttribArray(a),e.vertexAttribPointer(a,1,e.FLOAT,!1,n,20)),this.chamferVertexCount=i.count,e.bindVertexArray(null)}disposeChamferGeometry(){const t=this.gl;t&&(this.chamferVao&&(t.deleteVertexArray(this.chamferVao),this.chamferVao=null),this.chamferVertexCount=0)}createFBO(t,e){const i=this.gl;if(!i)return;this.disposeFBO(),this.fboWidth=t,this.fboHeight=e,this.interiorFbo=i.createFramebuffer(),i.bindFramebuffer(i.FRAMEBUFFER,this.interiorFbo),this.interiorColorTex=i.createTexture(),i.activeTexture(i.TEXTURE2),i.bindTexture(i.TEXTURE_2D,this.interiorColorTex),i.texStorage2D(i.TEXTURE_2D,1,i.RGBA8,t,e),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.framebufferTexture2D(i.FRAMEBUFFER,i.COLOR_ATTACHMENT0,i.TEXTURE_2D,this.interiorColorTex,0),this.interiorDepthTex=i.createTexture(),i.activeTexture(i.TEXTURE3),i.bindTexture(i.TEXTURE_2D,this.interiorDepthTex),i.texStorage2D(i.TEXTURE_2D,1,i.RGBA8,t,e),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.framebufferTexture2D(i.FRAMEBUFFER,i.COLOR_ATTACHMENT1,i.TEXTURE_2D,this.interiorDepthTex,0),i.drawBuffers([i.COLOR_ATTACHMENT0,i.COLOR_ATTACHMENT1]);const o=i.checkFramebufferStatus(i.FRAMEBUFFER);o!==i.FRAMEBUFFER_COMPLETE&&console.error("Interior FBO incomplete:",o),i.bindFramebuffer(i.FRAMEBUFFER,null)}createJFAResources(t,e){const i=this.gl;if(!i)return;this.disposeJFA();const o=Math.max(1,Math.round(t/2)),n=Math.max(1,Math.round(e/2));this.jfaWidth=o,this.jfaHeight=n;const s=(a,l,u,f)=>{const c=i.createFramebuffer();return i.bindFramebuffer(i.FRAMEBUFFER,c),i.bindTexture(i.TEXTURE_2D,a),i.texStorage2D(i.TEXTURE_2D,1,l,u,f),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MIN_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_MAG_FILTER,i.LINEAR),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_S,i.CLAMP_TO_EDGE),i.texParameteri(i.TEXTURE_2D,i.TEXTURE_WRAP_T,i.CLAMP_TO_EDGE),i.framebufferTexture2D(i.FRAMEBUFFER,i.COLOR_ATTACHMENT0,i.TEXTURE_2D,a,0),i.bindFramebuffer(i.FRAMEBUFFER,null),c};this.maskTex=i.createTexture(),this.maskFbo=s(this.maskTex,i.R8,o,n);const h=this.hasColorBufferFloat?i.RG16F:i.RGBA8;this.jfaPingTex=i.createTexture(),this.jfaPingFbo=s(this.jfaPingTex,h,o,n),this.jfaPongTex=i.createTexture(),this.jfaPongFbo=s(this.jfaPongTex,h,o,n),this.distTex=i.createTexture(),this.distFbo=s(this.distTex,i.RGBA8,o,n),this.distFieldDirty=!0}computeDistanceField(){const t=this.gl;if(!t||!this.maskFbo||!this.maskVao||!this.quadVao||!this.jfaPingFbo||!this.jfaPongFbo||!this.distFbo)return;const e=this.jfaWidth,i=this.jfaHeight;if(e===0||i===0)return;t.viewport(0,0,e,i),t.disable(t.STENCIL_TEST),t.disable(t.BLEND),t.bindFramebuffer(t.FRAMEBUFFER,this.maskFbo),t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(this.maskProgram),t.uniform2f(this.maskUniforms.uMeshScale,this.meshScaleX,this.meshScaleY),t.bindVertexArray(this.maskVao),t.drawElements(t.TRIANGLES,this.stencilIndexCount,t.UNSIGNED_SHORT,0),t.bindFramebuffer(t.FRAMEBUFFER,this.jfaPingFbo),t.clearColor(-1,-1,0,0),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(this.jfaSeedProgram),t.activeTexture(t.TEXTURE5),t.bindTexture(t.TEXTURE_2D,this.maskTex),t.uniform1i(this.jfaSeedUniforms.uMask,5),t.uniform2f(this.jfaSeedUniforms.uTexelSize,1/e,1/i),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4);const o=Math.max(e,i),n=[];let s=Math.ceil(o/2);for(;s>=1;)n.push(s),s=Math.floor(s/2);t.useProgram(this.jfaFloodProgram);let h=this.jfaPingTex,a=this.jfaPongFbo,l=this.jfaPongTex;for(let f=0;f<n.length;f++){const c=n[f]/Math.max(e,i);t.bindFramebuffer(t.FRAMEBUFFER,a),t.activeTexture(t.TEXTURE5),t.bindTexture(t.TEXTURE_2D,h),t.uniform1i(this.jfaFloodUniforms.uSeedTex,5),t.uniform1f(this.jfaFloodUniforms.uStepSize,c),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4);const g=h,d=a;h=l,a=d===this.jfaPongFbo?this.jfaPingFbo:this.jfaPongFbo,l=g}t.bindFramebuffer(t.FRAMEBUFFER,this.distFbo),t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(this.jfaDistProgram),t.activeTexture(t.TEXTURE5),t.bindTexture(t.TEXTURE_2D,h),t.uniform1i(this.jfaDistUniforms.uSeedTex,5),t.activeTexture(t.TEXTURE6),t.bindTexture(t.TEXTURE_2D,this.maskTex),t.uniform1i(this.jfaDistUniforms.uMask,6);const u=Math.max(this.config.bevelWidth,this.config.edgeOcclusionWidth);t.uniform1f(this.jfaDistUniforms.uBevelWidth,u),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4),t.activeTexture(t.TEXTURE4),t.bindTexture(t.TEXTURE_2D,this.distTex),t.bindFramebuffer(t.FRAMEBUFFER,null),this.distFieldDirty=!1}start(t,e,i,o){this.stop(),this.playbackVideo=t,this.readDepth=e,this.readInput=i,this.onVideoFrame=o??null,this.rvfcSupported="requestVideoFrameCallback"in HTMLVideoElement.prototype,this.rvfcSupported&&(this.rvfcHandle=t.requestVideoFrameCallback(this.videoFrameLoop)),this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop)}stop(){this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0),this.rvfcHandle&&this.playbackVideo&&(this.playbackVideo.cancelVideoFrameCallback(this.rvfcHandle),this.rvfcHandle=0),this.playbackVideo=null,this.readDepth=null,this.readInput=null,this.onVideoFrame=null,this.rvfcSupported=!1}dispose(){this.stop(),this.disposeTextures(),this.disposeFBO(),this.disposeJFA(),this.disposeStencilGeometry(),this.disposeBoundaryGeometry(),this.disposeChamferGeometry(),this.disposeGPUResources(),this.canvas.removeEventListener("webglcontextlost",this.handleContextLost),this.canvas.removeEventListener("webglcontextrestored",this.handleContextRestored),this.gl&&(this.gl.getExtension("WEBGL_lose_context")?.loseContext(),this.gl=null),this.canvas.remove(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),window.removeEventListener("resize",this.scheduleResizeRecalculate),this.resizeTimer!==null&&(window.clearTimeout(this.resizeTimer),this.resizeTimer=null)}initGPUResources(){const t=this.gl;if(!t)return;this.stencilProgram=O(t,L(t,t.VERTEX_SHADER,Zt),L(t,t.FRAGMENT_SHADER,$t)),this.stencilUniforms={uMeshScale:t.getUniformLocation(this.stencilProgram,"uMeshScale")},this.maskProgram=O(t,L(t,t.VERTEX_SHADER,Jt),L(t,t.FRAGMENT_SHADER,Kt)),this.maskUniforms={uMeshScale:t.getUniformLocation(this.maskProgram,"uMeshScale")},this.jfaSeedProgram=O(t,L(t,t.VERTEX_SHADER,Qt),L(t,t.FRAGMENT_SHADER,te)),this.jfaSeedUniforms=this.getUniforms(this.jfaSeedProgram,["uMask","uTexelSize"]),this.jfaFloodProgram=O(t,L(t,t.VERTEX_SHADER,ee),L(t,t.FRAGMENT_SHADER,ie)),this.jfaFloodUniforms=this.getUniforms(this.jfaFloodProgram,["uSeedTex","uStepSize"]),this.jfaDistProgram=O(t,L(t,t.VERTEX_SHADER,re),L(t,t.FRAGMENT_SHADER,oe)),this.jfaDistUniforms=this.getUniforms(this.jfaDistProgram,["uSeedTex","uMask","uBevelWidth"]),this.interiorProgram=O(t,L(t,t.VERTEX_SHADER,ne),L(t,t.FRAGMENT_SHADER,se)),this.interiorUniforms=this.getUniforms(this.interiorProgram,["uImage","uDepth","uOffset","uStrength","uPomSteps","uDepthPower","uDepthScale","uDepthBias","uContrastLow","uContrastHigh","uVerticalReduction","uDofStart","uDofStrength","uImageTexelSize","uFogDensity","uFogColor","uColorShift","uBrightnessBias","uUvOffset","uUvScale"]),this.compositeProgram=O(t,L(t,t.VERTEX_SHADER,ae),L(t,t.FRAGMENT_SHADER,he)),this.compositeUniforms=this.getUniforms(this.compositeProgram,["uInteriorColor","uDistField","uEdgeOcclusionWidth","uEdgeOcclusionStrength"]),this.boundaryProgram=O(t,L(t,t.VERTEX_SHADER,le),L(t,t.FRAGMENT_SHADER,ce)),this.boundaryUniforms=this.getUniforms(this.boundaryProgram,["uInteriorColor","uInteriorDepth","uDistField","uRimIntensity","uRimColor","uRimWidth","uMeshScale","uRefractionStrength","uChromaticStrength","uOcclusionIntensity","uTexelSize","uEdgeThickness","uEdgeSpecular","uEdgeColor","uLightDir","uBevelIntensity"]),this.chamferProgram=O(t,L(t,t.VERTEX_SHADER,ue),L(t,t.FRAGMENT_SHADER,fe)),this.chamferUniforms=this.getUniforms(this.chamferProgram,["uMeshScale","uLightDir3","uChamferColor","uChamferAmbient","uChamferSpecular","uChamferShininess","uInteriorColor","uTexelSize"]);const e=new Float32Array([-1,-1,1,-1,-1,1,1,1]);this.quadVao=t.createVertexArray(),t.bindVertexArray(this.quadVao);const i=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,i),t.bufferData(t.ARRAY_BUFFER,e,t.STATIC_DRAW);const o=t.getAttribLocation(this.interiorProgram,"aPosition");t.enableVertexAttribArray(o),t.vertexAttribPointer(o,2,t.FLOAT,!1,0,0),t.bindVertexArray(null),t.disable(t.DEPTH_TEST)}getUniforms(t,e){const i=this.gl,o={};for(const n of e)o[n]=i.getUniformLocation(t,n);return o}videoFrameLoop=(t,e)=>{const i=this.playbackVideo;if(!i)return;this.rvfcHandle=i.requestVideoFrameCallback(this.videoFrameLoop);const o=e.mediaTime??i.currentTime;this.updateDepthTexture(o),this.onVideoFrame&&this.onVideoFrame(o,e.presentedFrames??0)};renderLoop=()=>{this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop);const t=this.gl,e=this.playbackVideo;if(!t||!this.interiorProgram||!this.quadVao||!e||e.readyState<HTMLMediaElement.HAVE_CURRENT_DATA||!this.interiorFbo||!this.interiorColorTex||!this.interiorDepthTex)return;this.distFieldDirty&&this.maskVao&&this.distFbo&&(this.computeDistanceField(),t.viewport(0,0,this.canvas.width,this.canvas.height)),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.videoTexture),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,e),this.rvfcSupported||this.updateDepthTexture(e.currentTime);let i=0,o=0;if(this.readInput){const n=this.readInput();i=-n.x,o=n.y}if(t.bindFramebuffer(t.FRAMEBUFFER,this.interiorFbo),t.checkFramebufferStatus(t.FRAMEBUFFER)!==t.FRAMEBUFFER_COMPLETE){t.bindFramebuffer(t.FRAMEBUFFER,null);return}t.viewport(0,0,this.fboWidth,this.fboHeight),t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(this.interiorProgram),t.uniform2f(this.interiorUniforms.uOffset,i,o),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.videoTexture),t.activeTexture(t.TEXTURE1),t.bindTexture(t.TEXTURE_2D,this.depthTexture),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4),t.bindFramebuffer(t.FRAMEBUFFER,null),t.clearColor(0,0,0,0),t.viewport(0,0,this.canvas.width,this.canvas.height),t.clear(t.COLOR_BUFFER_BIT|t.STENCIL_BUFFER_BIT),this.stencilVao&&this.stencilProgram&&this.stencilIndexCount>0&&(t.enable(t.STENCIL_TEST),t.stencilFunc(t.ALWAYS,1,255),t.stencilOp(t.KEEP,t.KEEP,t.REPLACE),t.stencilMask(255),t.colorMask(!1,!1,!1,!1),t.useProgram(this.stencilProgram),t.bindVertexArray(this.stencilVao),t.drawElements(t.TRIANGLES,this.stencilIndexCount,t.UNSIGNED_SHORT,0),t.colorMask(!0,!0,!0,!0)),t.stencilFunc(t.EQUAL,1,255),t.stencilMask(0),t.activeTexture(t.TEXTURE2),t.bindTexture(t.TEXTURE_2D,this.interiorColorTex),t.activeTexture(t.TEXTURE3),t.bindTexture(t.TEXTURE_2D,this.interiorDepthTex),t.activeTexture(t.TEXTURE4),t.bindTexture(t.TEXTURE_2D,this.distTex),t.useProgram(this.compositeProgram),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4),t.disable(t.STENCIL_TEST),this.chamferVao&&this.chamferProgram&&this.chamferVertexCount>0&&(t.useProgram(this.chamferProgram),t.uniform2f(this.chamferUniforms.uMeshScale,this.meshScaleX,this.meshScaleY),t.uniform2f(this.chamferUniforms.uTexelSize,1/this.canvas.width,1/this.canvas.height),t.bindVertexArray(this.chamferVao),t.drawArrays(t.TRIANGLES,0,this.chamferVertexCount)),this.boundaryVao&&this.boundaryProgram&&this.boundaryVertexCount>0&&this.config.rimLightIntensity>0&&(t.enable(t.BLEND),t.blendFunc(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA),t.useProgram(this.boundaryProgram),t.bindVertexArray(this.boundaryVao),t.drawArrays(t.TRIANGLES,0,this.boundaryVertexCount),t.disable(t.BLEND))};updateDepthTexture(t){const e=this.gl;if(!e||!this.readDepth||!this.depthTexture)return;const i=this.readDepth(t);e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.depthTexture),e.texSubImage2D(e.TEXTURE_2D,0,0,0,this.depthWidth,this.depthHeight,e.RED,e.UNSIGNED_BYTE,i)}setupResizeHandling(){typeof ResizeObserver<"u"&&(this.resizeObserver=new ResizeObserver(()=>{this.scheduleResizeRecalculate()}),this.resizeObserver.observe(this.container)),window.addEventListener("resize",this.scheduleResizeRecalculate),this.recalculateViewportLayout()}scheduleResizeRecalculate=()=>{this.resizeTimer!==null&&window.clearTimeout(this.resizeTimer),this.resizeTimer=window.setTimeout(()=>{this.resizeTimer=null,this.recalculateViewportLayout()},ft.RESIZE_DEBOUNCE_MS)};recalculateViewportLayout(){const t=this.gl;if(!t)return;const{width:e,height:i}=this.getViewportSize(),o=Math.min(window.devicePixelRatio,2),n=Math.round(e*o),s=Math.round(i*o);(this.canvas.width!==n||this.canvas.height!==s)&&(this.canvas.width=n,this.canvas.height=s,t.viewport(0,0,n,s)),(this.fboWidth!==n||this.fboHeight!==s)&&this.createFBO(n,s);const h=Math.max(1,Math.round(n/2)),a=Math.max(1,Math.round(s/2));(this.jfaWidth!==h||this.jfaHeight!==a)&&this.createJFAResources(n,s);const l=e/i,u=this.config.parallaxStrength+this.config.overscanPadding;let f=1,c=1;l>this.videoAspect?c=this.videoAspect/l:f=l/this.videoAspect;const g=1+u*2;f/=g,c/=g,this.uvOffset=[(1-f)/2,(1-c)/2],this.uvScale=[f,c],this.interiorProgram&&(t.useProgram(this.interiorProgram),t.uniform2f(this.interiorUniforms.uUvOffset,this.uvOffset[0],this.uvOffset[1]),t.uniform2f(this.interiorUniforms.uUvScale,this.uvScale[0],this.uvScale[1]));const d=.65;this.meshScaleX=d,this.meshScaleY=d,l>this.meshAspect?this.meshScaleX=d*(this.meshAspect/l):this.meshScaleY=d*(l/this.meshAspect),this.stencilProgram&&(t.useProgram(this.stencilProgram),t.uniform2f(this.stencilUniforms.uMeshScale,this.meshScaleX,this.meshScaleY)),this.boundaryProgram&&(t.useProgram(this.boundaryProgram),t.uniform2f(this.boundaryUniforms.uMeshScale,this.meshScaleX,this.meshScaleY),t.uniform1f(this.boundaryUniforms.uRimWidth,this.config.rimLightWidth),t.uniform2f(this.boundaryUniforms.uTexelSize,1/n,1/s)),this.chamferProgram&&(t.useProgram(this.chamferProgram),t.uniform2f(this.chamferUniforms.uMeshScale,this.meshScaleX,this.meshScaleY)),this.distFieldDirty=!0}getViewportSize(){const t=Math.max(1,Math.round(this.container.clientWidth||window.innerWidth)),e=Math.max(1,Math.round(this.container.clientHeight||window.innerHeight));return{width:t,height:e}}handleContextLost=t=>{t.preventDefault(),this.animationFrameHandle&&(window.cancelAnimationFrame(this.animationFrameHandle),this.animationFrameHandle=0)};handleContextRestored=()=>{const t=this.canvas.getContext("webgl2",{alpha:!0,premultipliedAlpha:!0,stencil:!0});t&&(this.gl=t,this.hasColorBufferFloat=!!t.getExtension("EXT_color_buffer_float"),t.clearColor(0,0,0,0),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this.initGPUResources(),this.recalculateViewportLayout(),this.playbackVideo&&(this.animationFrameHandle=window.requestAnimationFrame(this.renderLoop)))};disposeTextures(){const t=this.gl;t&&(this.videoTexture&&(t.deleteTexture(this.videoTexture),this.videoTexture=null),this.depthTexture&&(t.deleteTexture(this.depthTexture),this.depthTexture=null))}disposeFBO(){const t=this.gl;t&&(this.interiorColorTex&&(t.deleteTexture(this.interiorColorTex),this.interiorColorTex=null),this.interiorDepthTex&&(t.deleteTexture(this.interiorDepthTex),this.interiorDepthTex=null),this.interiorFbo&&(t.deleteFramebuffer(this.interiorFbo),this.interiorFbo=null),this.fboWidth=0,this.fboHeight=0)}disposeJFA(){const t=this.gl;t&&(this.maskTex&&(t.deleteTexture(this.maskTex),this.maskTex=null),this.maskFbo&&(t.deleteFramebuffer(this.maskFbo),this.maskFbo=null),this.jfaPingTex&&(t.deleteTexture(this.jfaPingTex),this.jfaPingTex=null),this.jfaPingFbo&&(t.deleteFramebuffer(this.jfaPingFbo),this.jfaPingFbo=null),this.jfaPongTex&&(t.deleteTexture(this.jfaPongTex),this.jfaPongTex=null),this.jfaPongFbo&&(t.deleteFramebuffer(this.jfaPongFbo),this.jfaPongFbo=null),this.distTex&&(t.deleteTexture(this.distTex),this.distTex=null),this.distFbo&&(t.deleteFramebuffer(this.distFbo),this.distFbo=null),this.jfaWidth=0,this.jfaHeight=0,this.distFieldDirty=!0)}disposeStencilGeometry(){const t=this.gl;t&&(this.stencilVao&&(t.deleteVertexArray(this.stencilVao),this.stencilVao=null),this.maskVao&&(t.deleteVertexArray(this.maskVao),this.maskVao=null),this.stencilIndexCount=0)}disposeBoundaryGeometry(){const t=this.gl;t&&(this.boundaryVao&&(t.deleteVertexArray(this.boundaryVao),this.boundaryVao=null),this.boundaryVertexCount=0)}disposeGPUResources(){const t=this.gl;t&&(this.stencilProgram&&(t.deleteProgram(this.stencilProgram),this.stencilProgram=null),this.maskProgram&&(t.deleteProgram(this.maskProgram),this.maskProgram=null),this.jfaSeedProgram&&(t.deleteProgram(this.jfaSeedProgram),this.jfaSeedProgram=null),this.jfaFloodProgram&&(t.deleteProgram(this.jfaFloodProgram),this.jfaFloodProgram=null),this.jfaDistProgram&&(t.deleteProgram(this.jfaDistProgram),this.jfaDistProgram=null),this.interiorProgram&&(t.deleteProgram(this.interiorProgram),this.interiorProgram=null),this.compositeProgram&&(t.deleteProgram(this.compositeProgram),this.compositeProgram=null),this.boundaryProgram&&(t.deleteProgram(this.boundaryProgram),this.boundaryProgram=null),this.chamferProgram&&(t.deleteProgram(this.chamferProgram),this.chamferProgram=null),this.quadVao&&(t.deleteVertexArray(this.quadVao),this.quadVao=null),this.stencilUniforms={},this.maskUniforms={},this.jfaSeedUniforms={},this.jfaFloodUniforms={},this.jfaDistUniforms={},this.interiorUniforms={},this.compositeUniforms={},this.boundaryUniforms={},this.chamferUniforms={})}}async function pe(r){const t=await fetch(r);if(!t.ok)throw new Error(`Failed to fetch SVG: ${t.status} ${t.statusText}`);const e=await t.text();return ge(e)}function ge(r){const i=new DOMParser().parseFromString(r,"image/svg+xml").querySelector("svg");if(!i)throw new Error("No <svg> element found in document.");const o=ve(i);if(o.length===0)throw new Error("No path data found in SVG.");let n=1/0,s=1/0,h=-1/0,a=-1/0;for(const F of o)for(let R=0;R<F.length;R+=2)n=Math.min(n,F[R]),h=Math.max(h,F[R]),s=Math.min(s,F[R+1]),a=Math.max(a,F[R+1]);const l=h-n,u=a-s,f=(n+h)/2,c=(s+a)/2,g=2/Math.max(l,u),d=l/u,m=o.map(F=>{const R=[];for(let C=0;C<F.length;C+=2)R.push((F[C]-f)*g),R.push(-((F[C+1]-c)*g));return R}),U=Re(m),y=[],E=[];for(const F of U){const{flatCoords:R,holeIndices:C}=Se(F),M=Fe(R,C),B=y.length/2;for(const I of M)E.push(I+B);for(const I of R)y.push(I)}const p=y,v=E,T=[],x=[],A=[],S=St(m);for(let F=0;F<m.length;F++){const R=m[F];x.push(T.length),A.push(S[F]);for(let C=0;C<R.length;C++)T.push(R[C]);R.length>=2&&T.push(R[0],R[1])}let D=1/0,P=1/0,_=-1/0,k=-1/0;for(let F=0;F<p.length;F+=2)D=Math.min(D,p[F]),_=Math.max(_,p[F]),P=Math.min(P,p[F+1]),k=Math.max(k,p[F+1]);return{vertices:new Float32Array(p),indices:new Uint16Array(v),edgeVertices:new Float32Array(T),contourOffsets:x,contourIsHole:A,bounds:{minX:D,maxX:_,minY:P,maxY:k},aspect:d}}function ve(r){const t=[];return r.querySelectorAll("path").forEach(a=>{const l=a.getAttribute("d");if(!l)return;const u=Ee(l);t.push(...u)}),r.querySelectorAll("polygon").forEach(a=>{const l=a.getAttribute("points");if(!l)return;const u=bt(l);u.length>=6&&t.push(u)}),r.querySelectorAll("polyline").forEach(a=>{const l=a.getAttribute("points");if(!l)return;const u=bt(l);u.length>=6&&t.push(u)}),r.querySelectorAll("rect").forEach(a=>{const l=parseFloat(a.getAttribute("x")||"0"),u=parseFloat(a.getAttribute("y")||"0"),f=parseFloat(a.getAttribute("width")||"0"),c=parseFloat(a.getAttribute("height")||"0");f>0&&c>0&&t.push([l,u,l+f,u,l+f,u+c,l,u+c])}),r.querySelectorAll("circle").forEach(a=>{const l=parseFloat(a.getAttribute("cx")||"0"),u=parseFloat(a.getAttribute("cy")||"0"),f=parseFloat(a.getAttribute("r")||"0");f>0&&t.push(xe(l,u,f))}),r.querySelectorAll("ellipse").forEach(a=>{const l=parseFloat(a.getAttribute("cx")||"0"),u=parseFloat(a.getAttribute("cy")||"0"),f=parseFloat(a.getAttribute("rx")||"0"),c=parseFloat(a.getAttribute("ry")||"0");f>0&&c>0&&t.push(Te(l,u,f,c))}),t}function bt(r){const t=[],e=r.trim().split(/[\s,]+/);for(let i=0;i<e.length-1;i+=2){const o=parseFloat(e[i]),n=parseFloat(e[i+1]);Number.isFinite(o)&&Number.isFinite(n)&&t.push(o,n)}return t}function xe(r,t,e,i=64){const o=[];for(let n=0;n<i;n++){const s=2*Math.PI*n/i;o.push(r+e*Math.cos(s),t+e*Math.sin(s))}return o}function Te(r,t,e,i,o=64){const n=[];for(let s=0;s<o;s++){const h=2*Math.PI*s/o;n.push(r+e*Math.cos(h),t+i*Math.sin(h))}return n}function Ee(r){const t=[];let e=[],i=0,o=0,n=0,s=0,h=0,a=0,l="";const u=be(r);let f=0;function c(){return f>=u.length?0:parseFloat(u[f++])}for(;f<u.length;){const g=u[f];let d;/^[a-zA-Z]$/.test(g)?(d=g,f++):d=l==="M"?"L":l==="m"?"l":l;const m=d===d.toLowerCase();switch(d.toUpperCase()){case"M":{e.length>0&&t.push(e),e=[];const y=c()+(m?i:0),E=c()+(m?o:0);i=y,o=E,n=y,s=E,e.push(i,o),h=i,a=o;break}case"L":{i=c()+(m?i:0),o=c()+(m?o:0),e.push(i,o),h=i,a=o;break}case"H":{i=c()+(m?i:0),e.push(i,o),h=i,a=o;break}case"V":{o=c()+(m?o:0),e.push(i,o),h=i,a=o;break}case"C":{const y=c()+(m?i:0),E=c()+(m?o:0),p=c()+(m?i:0),v=c()+(m?o:0),T=c()+(m?i:0),x=c()+(m?o:0);$(e,i,o,y,E,p,v,T,x),i=T,o=x,h=p,a=v;break}case"S":{const y=2*i-h,E=2*o-a,p=c()+(m?i:0),v=c()+(m?o:0),T=c()+(m?i:0),x=c()+(m?o:0);$(e,i,o,y,E,p,v,T,x),i=T,o=x,h=p,a=v;break}case"Q":{const y=c()+(m?i:0),E=c()+(m?o:0),p=c()+(m?i:0),v=c()+(m?o:0);yt(e,i,o,y,E,p,v),i=p,o=v,h=y,a=E;break}case"T":{const y=2*i-h,E=2*o-a,p=c()+(m?i:0),v=c()+(m?o:0);yt(e,i,o,y,E,p,v),i=p,o=v,h=y,a=E;break}case"A":{const y=c(),E=c(),p=c(),v=c(),T=c(),x=c()+(m?i:0),A=c()+(m?o:0);Ae(e,i,o,y,E,p,!!v,!!T,x,A),i=x,o=A,h=i,a=o;break}case"Z":{i=n,o=s,e.length>0&&t.push(e),e=[],h=i,a=o;break}default:f++;break}l=d}return e.length>=6&&t.push(e),t}function be(r){const t=[],e=/([a-zA-Z])|([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)/g;let i;for(;(i=e.exec(r))!==null;)t.push(i[0]);return t}const ye=.5;function $(r,t,e,i,o,n,s,h,a,l=0){if(l>12){r.push(h,a);return}const u=h-t,f=a-e,c=Math.sqrt(u*u+f*f);if(c<1e-6){r.push(h,a);return}const g=Math.abs((i-h)*f-(o-a)*u)/c,d=Math.abs((n-h)*f-(s-a)*u)/c;if(g+d<ye){r.push(h,a);return}const m=(t+i)/2,U=(e+o)/2,y=(i+n)/2,E=(o+s)/2,p=(n+h)/2,v=(s+a)/2,T=(m+y)/2,x=(U+E)/2,A=(y+p)/2,S=(E+v)/2,D=(T+A)/2,P=(x+S)/2;$(r,t,e,m,U,T,x,D,P,l+1),$(r,D,P,A,S,p,v,h,a,l+1)}function yt(r,t,e,i,o,n,s){const h=t+.6666666666666666*(i-t),a=e+2/3*(o-e),l=n+2/3*(i-n),u=s+2/3*(o-s);$(r,t,e,h,a,l,u,n,s)}function Ae(r,t,e,i,o,n,s,h,a,l){if(i===0||o===0){r.push(a,l);return}let u=Math.abs(i),f=Math.abs(o);const c=n*Math.PI/180,g=Math.cos(c),d=Math.sin(c),m=(t-a)/2,U=(e-l)/2,y=g*m+d*U,E=-d*m+g*U;let p=y*y/(u*u)+E*E/(f*f);if(p>1){const M=Math.sqrt(p);u*=M,f*=M,p=1}const v=u*u,T=f*f,x=y*y,A=E*E;let S=Math.max(0,(v*T-v*A-T*x)/(v*A+T*x));S=Math.sqrt(S),s===h&&(S=-S);const D=S*(u*E)/f,P=S*-(f*y)/u,_=g*D-d*P+(t+a)/2,k=d*D+g*P+(e+l)/2,F=At(1,0,(y-D)/u,(E-P)/f);let R=At((y-D)/u,(E-P)/f,(-y-D)/u,(-E-P)/f);!h&&R>0&&(R-=2*Math.PI),h&&R<0&&(R+=2*Math.PI);const C=Math.max(4,Math.ceil(Math.abs(R)/(Math.PI/16)));for(let M=1;M<=C;M++){const B=F+M/C*R,I=Math.cos(B),G=Math.sin(B),tt=g*u*I-d*f*G+_,ct=d*u*I+g*f*G+k;r.push(tt,ct)}}function At(r,t,e,i){const o=r*i-t*e<0?-1:1,n=r*e+t*i,s=Math.sqrt(r*r+t*t),h=Math.sqrt(e*e+i*i),a=n/(s*h);return o*Math.acos(Math.max(-1,Math.min(1,a)))}function Se(r){const t=[],e=[];for(let i=0;i<r.length;i++){i>0&&e.push(t.length/2);for(const o of r[i])t.push(o)}return{flatCoords:t,holeIndices:e}}function St(r){const t=r.length,e=r.map(o=>Math.abs(Rt(o))),i=new Array(t).fill(!1);for(let o=0;o<t;o++){let n=0;const s=r[o][0],h=r[o][1];for(let a=0;a<t;a++)o!==a&&e[a]>e[o]&&Ft(s,h,r[a])&&n++;i[o]=n%2===1}return i}function Re(r){if(r.length<=1)return[r];const t=St(r),e=r.map((s,h)=>{const a=Rt(s);return{index:h,contour:s,area:a,isOuter:!t[h]}}),i=e.filter(s=>s.isOuter),o=e.filter(s=>!s.isOuter);if(i.length===0)return r.map(s=>[s]);const n=i.map(s=>({outer:s.contour,holes:[]}));for(const s of o){const h=s.contour[0],a=s.contour[1];let l=-1,u=1/0;for(let f=0;f<i.length;f++)if(Ft(h,a,i[f].contour)){const c=Math.abs(i[f].area);c<u&&(u=c,l=f)}l>=0?n[l].holes.push(s.contour):n.push({outer:s.contour,holes:[]})}return n.map(s=>[s.outer,...s.holes])}function Rt(r){let t=0;const e=r.length;for(let i=0;i<e;i+=2){const o=r[i],n=r[i+1],s=r[(i+2)%e],h=r[(i+3)%e];t+=o*h-s*n}return t/2}function Ft(r,t,e){let i=!1;const o=e.length;for(let n=0,s=o-2;n<o;s=n,n+=2){const h=e[n],a=e[n+1],l=e[s],u=e[s+1];a>t!=u>t&&r<(l-h)*(t-a)/(u-a)+h&&(i=!i)}return i}function Fe(r,t,e=2){const i=t&&t.length>0,o=i?t[0]*e:r.length;let n=Dt(r,0,o,e,!0);const s=[];if(!n||n.next===n.prev)return s;i&&(n=Le(r,t,n,e));let h=1/0,a=1/0,l=-1/0,u=-1/0,f=0;if(r.length>80*e){for(let c=0;c<o;c+=e){const g=r[c],d=r[c+1];g<h&&(h=g),d<a&&(a=d),g>l&&(l=g),d>u&&(u=d)}f=Math.max(l-h,u-a),f=f!==0?32767/f:0}return J(n,s,e,h,a,f,0),s}function Dt(r,t,e,i,o){let n=null;if(o===Xe(r,t,e,i)>0)for(let s=t;s<e;s+=i)n=wt(s,r[s],r[s+1],n);else for(let s=e-i;s>=t;s-=i)n=wt(s,r[s],r[s+1],n);return n&&nt(n,n.next)&&(Q(n),n=n.next),n?(n.next.prev=n,n.prev.next=n,n.next):null}function X(r,t){t||(t=r);let e=r,i;do if(i=!1,!e.steiner&&(nt(e,e.next)||w(e.prev,e,e.next)===0)){if(Q(e),e=t=e.prev,e===e.next)break;i=!0}else e=e.next;while(i||e!==t);return t}function J(r,t,e,i,o,n,s){if(!r)return;!s&&n&&Ie(r,i,o,n);let h=r,a,l;for(;r.prev!==r.next;){if(a=r.prev,l=r.next,n?Ue(r,i,o,n):De(r)){t.push(a.i/e,r.i/e,l.i/e),Q(r),r=l.next,h=l.next;continue}if(r=l,r===h){s?s===1?(r=Pe(X(r),t,e),J(r,t,e,i,o,n,2)):s===2&&we(r,t,e,i,o,n):J(X(r),t,e,i,o,n,1);break}}}function De(r){const t=r.prev,e=r,i=r.next;if(w(t,e,i)>=0)return!1;const o=t.x,n=e.x,s=i.x,h=t.y,a=e.y,l=i.y,u=o<n?o<s?o:s:n<s?n:s,f=h<a?h<l?h:l:a<l?a:l,c=o>n?o>s?o:s:n>s?n:s,g=h>a?h>l?h:l:a>l?a:l;let d=i.next;for(;d!==t;){if(d.x>=u&&d.x<=c&&d.y>=f&&d.y<=g&&z(o,h,n,a,s,l,d.x,d.y)&&w(d.prev,d,d.next)>=0)return!1;d=d.next}return!0}function Ue(r,t,e,i){const o=r.prev,n=r,s=r.next;if(w(o,n,s)>=0)return!1;const h=o.x,a=n.x,l=s.x,u=o.y,f=n.y,c=s.y,g=h<a?h<l?h:l:a<l?a:l,d=u<f?u<c?u:c:f<c?f:c,m=h>a?h>l?h:l:a>l?a:l,U=u>f?u>c?u:c:f>c?f:c,y=dt(g,d,t,e,i),E=dt(m,U,t,e,i);let p=r.prevZ,v=r.nextZ;for(;p&&p.z>=y&&v&&v.z<=E;){if(p.x>=g&&p.x<=m&&p.y>=d&&p.y<=U&&p!==o&&p!==s&&z(h,u,a,f,l,c,p.x,p.y)&&w(p.prev,p,p.next)>=0||(p=p.prevZ,v.x>=g&&v.x<=m&&v.y>=d&&v.y<=U&&v!==o&&v!==s&&z(h,u,a,f,l,c,v.x,v.y)&&w(v.prev,v,v.next)>=0))return!1;v=v.nextZ}for(;p&&p.z>=y;){if(p.x>=g&&p.x<=m&&p.y>=d&&p.y<=U&&p!==o&&p!==s&&z(h,u,a,f,l,c,p.x,p.y)&&w(p.prev,p,p.next)>=0)return!1;p=p.prevZ}for(;v&&v.z<=E;){if(v.x>=g&&v.x<=m&&v.y>=d&&v.y<=U&&v!==o&&v!==s&&z(h,u,a,f,l,c,v.x,v.y)&&w(v.prev,v,v.next)>=0)return!1;v=v.nextZ}return!0}function Pe(r,t,e){let i=r;do{const o=i.prev,n=i.next.next;!nt(o,n)&&Ut(o,i,i.next,n)&&K(o,n)&&K(n,o)&&(t.push(o.i/e,i.i/e,n.i/e),Q(i),Q(i.next),i=r=n),i=i.next}while(i!==r);return X(i)}function we(r,t,e,i,o,n){let s=r;do{let h=s.next.next;for(;h!==s.prev;){if(s.i!==h.i&&ke(s,h)){let a=Pt(s,h);s=X(s,s.next),a=X(a,a.next),J(s,t,e,i,o,n,0),J(a,t,e,i,o,n,0);return}h=h.next}s=s.next}while(s!==r)}function Le(r,t,e,i){const o=[];for(let n=0;n<t.length;n++){const s=t[n]*i,h=n<t.length-1?t[n+1]*i:r.length,a=Dt(r,s,h,i,!1);a&&(a===a.next&&(a.steiner=!0),o.push(Oe(a)))}o.sort((n,s)=>n.x-s.x);for(const n of o)e=Ce(n,e);return e}function Ce(r,t){const e=Me(r,t);if(!e)return t;const i=Pt(e,r);return X(i,i.next),X(e,e.next)}function Me(r,t){let e=t;const i=r.x,o=r.y;let n=-1/0,s=null;do{if(o<=e.y&&o>=e.next.y&&e.next.y!==e.y){const f=e.x+(o-e.y)/(e.next.y-e.y)*(e.next.x-e.x);if(f<=i&&f>n&&(n=f,s=e.x<e.next.x?e:e.next,f===i))return s}e=e.next}while(e!==t);if(!s)return null;const h=s,a=s.x,l=s.y;let u=1/0;e=s;do{if(i>=e.x&&e.x>=a&&i!==e.x&&z(o<l?i:n,o,a,l,o<l?n:i,o,e.x,e.y)){const f=Math.abs(o-e.y)/(i-e.x);K(e,r)&&(f<u||f===u&&(e.x>s.x||_e(s,e)))&&(s=e,u=f)}e=e.next}while(e!==h);return s}function _e(r,t){return w(r.prev,r,t.prev)<0&&w(t.next,r,r.next)<0}function Ie(r,t,e,i){let o=r;do o.z===0&&(o.z=dt(o.x,o.y,t,e,i)),o.prevZ=o.prev,o.nextZ=o.next,o=o.next;while(o!==r);o.prevZ.nextZ=null,o.prevZ=null,Ve(o)}function Ve(r){let t=1,e;do{let i=r;r=null;let o=null;for(e=0;i;){e++;let n=i,s=0;for(let a=0;a<t&&(s++,n=n.nextZ,!!n);a++);let h=t;for(;s>0||h>0&&n;){let a;s!==0&&(h===0||!n||i.z<=n.z)?(a=i,i=i.nextZ,s--):(a=n,n=n.nextZ,h--),o?o.nextZ=a:r=a,a.prevZ=o,o=a}i=n}o.nextZ=null,t*=2}while(e>1);return r}function dt(r,t,e,i,o){let n=(r-e)*o|0,s=(t-i)*o|0;return n=(n|n<<8)&16711935,n=(n|n<<4)&252645135,n=(n|n<<2)&858993459,n=(n|n<<1)&1431655765,s=(s|s<<8)&16711935,s=(s|s<<4)&252645135,s=(s|s<<2)&858993459,s=(s|s<<1)&1431655765,n|s<<1}function Oe(r){let t=r,e=r;do(t.x<e.x||t.x===e.x&&t.y<e.y)&&(e=t),t=t.next;while(t!==r);return e}function z(r,t,e,i,o,n,s,h){return(o-s)*(t-h)-(r-s)*(n-h)>=0&&(r-s)*(i-h)-(e-s)*(t-h)>=0&&(e-s)*(n-h)-(o-s)*(i-h)>=0}function ke(r,t){return r.next.i!==t.i&&r.prev.i!==t.i&&!Be(r,t)&&(K(r,t)&&K(t,r)&&He(r,t)&&(w(r.prev,r,t.prev)!==0||w(r,t.prev,t)!==0)||nt(r,t)&&w(r.prev,r,r.next)>0&&w(t.prev,t,t.next)>0)}function w(r,t,e){return(t.y-r.y)*(e.x-t.x)-(t.x-r.x)*(e.y-t.y)}function nt(r,t){return r.x===t.x&&r.y===t.y}function Ut(r,t,e,i){const o=at(w(r,t,e)),n=at(w(r,t,i)),s=at(w(e,i,r)),h=at(w(e,i,t));return!!(o!==n&&s!==h||o===0&&st(r,e,t)||n===0&&st(r,i,t)||s===0&&st(e,r,i)||h===0&&st(e,t,i))}function st(r,t,e){return t.x<=Math.max(r.x,e.x)&&t.x>=Math.min(r.x,e.x)&&t.y<=Math.max(r.y,e.y)&&t.y>=Math.min(r.y,e.y)}function at(r){return r>0?1:r<0?-1:0}function Be(r,t){let e=r;do{if(e.i!==r.i&&e.next.i!==r.i&&e.i!==t.i&&e.next.i!==t.i&&Ut(e,e.next,r,t))return!0;e=e.next}while(e!==r);return!1}function K(r,t){return w(r.prev,r,r.next)<0?w(r,t,r.next)>=0&&w(r,r.prev,t)>=0:w(r,t,r.prev)<0||w(r,r.next,t)<0}function He(r,t){let e=r,i=!1;const o=(r.x+t.x)/2,n=(r.y+t.y)/2;do e.y>n!=e.next.y>n&&e.next.y!==e.y&&o<(e.next.x-e.x)*(n-e.y)/(e.next.y-e.y)+e.x&&(i=!i),e=e.next;while(e!==r);return i}function Pt(r,t){const e=mt(r.i,r.x,r.y),i=mt(t.i,t.x,t.y),o=r.next,n=t.prev;return r.next=t,t.prev=r,e.next=o,o.prev=e,i.next=e,e.prev=i,n.next=i,i.prev=n,i}function wt(r,t,e,i){const o=mt(r,t,e);return i?(o.next=i.next,o.prev=i,i.next.prev=o,i.next=o):(o.prev=o,o.next=o),o}function Q(r){r.next.prev=r.prev,r.prev.next=r.next,r.prevZ&&(r.prevZ.nextZ=r.nextZ),r.nextZ&&(r.nextZ.prevZ=r.prevZ)}function mt(r,t,e){return{i:r,x:t,y:e,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}function Xe(r,t,e,i){let o=0;for(let n=t,s=e-i;n<e;n+=i)o+=(r[s]-r[n])*(r[n+1]+r[s+1]),s=n;return o}const b={parallaxX:.4,parallaxY:.8,parallaxMax:30,overscan:.06,pomSteps:16,rimIntensity:.6,rimColor:"#ffffff",rimWidth:.025,refractionStrength:.015,chromaticStrength:.008,occlusionIntensity:.4,depthPower:.7,depthScale:1.2,depthBias:-.05,fogDensity:.15,fogColor:"#1a1a2e",colorShift:.6,brightnessBias:.05,contrastLow:.02,contrastHigh:.98,verticalReduction:.5,dofStart:.5,dofStrength:.5,bevelIntensity:.5,bevelWidth:.04,bevelDarkening:.2,bevelDesaturation:.12,bevelLightAngle:135,edgeThickness:.01,edgeSpecular:.35,edgeColor:"#a0a0a0",chamferWidth:.025,chamferAngle:45,chamferColor:"#262630",chamferAmbient:.12,chamferSpecular:.3,chamferShininess:24,edgeOcclusionWidth:.03,edgeOcclusionStrength:.2,lightDirection:"-0.5,0.7,-0.3",autoplay:!0,loop:!0,muted:!0};class pt{constructor(t,e=.08,i=.06){this.host=t,this.lerpFactor=e,this.motionLerpFactor=i,this.host.addEventListener("mousemove",this.handleMouseMove),this.host.addEventListener("mouseleave",this.resetPointerTarget),this.host.addEventListener("touchstart",this.handleTouchStart,{passive:!0}),this.host.addEventListener("touchmove",this.handleTouchMove,{passive:!0}),this.host.addEventListener("touchend",this.handleTouchEnd,{passive:!0}),this.host.addEventListener("touchcancel",this.handleTouchEnd,{passive:!0})}pointerTarget={x:0,y:0};motionTarget={x:0,y:0};smoothedOutput={x:0,y:0};usingMotionInput=!1;motionListenerAttached=!1;motionRequested=!1;touchActive=!1;touchAnchorX=0;touchAnchorY=0;lerpFactor;motionLerpFactor;static TOUCH_DRAG_RANGE=100;update(){const t=this.touchActive?this.pointerTarget:this.usingMotionInput?this.motionTarget:this.pointerTarget,e=this.usingMotionInput&&!this.touchActive?this.motionLerpFactor:this.lerpFactor;return this.smoothedOutput.x=lt(this.smoothedOutput.x,t.x,e),this.smoothedOutput.y=lt(this.smoothedOutput.y,t.y,e),this.smoothedOutput}dispose(){this.host.removeEventListener("mousemove",this.handleMouseMove),this.host.removeEventListener("mouseleave",this.resetPointerTarget),this.host.removeEventListener("touchstart",this.handleTouchStart),this.host.removeEventListener("touchmove",this.handleTouchMove),this.host.removeEventListener("touchend",this.handleTouchEnd),this.host.removeEventListener("touchcancel",this.handleTouchEnd),this.motionListenerAttached&&(window.removeEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!1)}handleMouseMove=t=>{const e=this.host.getBoundingClientRect(),i=(t.clientX-e.left)/e.width*2-1,o=(t.clientY-e.top)/e.height*2-1;this.pointerTarget.x=W(i,-1,1),this.pointerTarget.y=W(o,-1,1)};resetPointerTarget=()=>{this.pointerTarget.x=0,this.pointerTarget.y=0};handleTouchStart=t=>{const e=t.touches[0];e&&(this.touchActive=!0,this.touchAnchorX=e.clientX,this.touchAnchorY=e.clientY,this.pointerTarget.x=0,this.pointerTarget.y=0,this.motionRequested||(this.motionRequested=!0,this.requestMotionPermission()))};handleTouchMove=t=>{const e=t.touches[0];if(!e)return;const i=e.clientX-this.touchAnchorX,o=e.clientY-this.touchAnchorY,n=pt.TOUCH_DRAG_RANGE;this.pointerTarget.x=W(i/n,-1,1),this.pointerTarget.y=W(o/n,-1,1)};handleTouchEnd=()=>{this.touchActive=!1,this.pointerTarget.x=0,this.pointerTarget.y=0};async requestMotionPermission(){if(typeof DeviceOrientationEvent>"u")return;const t=DeviceOrientationEvent;if(typeof t.requestPermission=="function")try{if(await t.requestPermission()!=="granted")return}catch{return}this.motionListenerAttached||(window.addEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!0),this.usingMotionInput=!0}handleDeviceOrientation=t=>{const e=W((t.gamma??0)/45,-1,1),i=W((t.beta??0)/45,-1,1);this.motionTarget.x=lt(this.motionTarget.x,e,this.motionLerpFactor),this.motionTarget.y=lt(this.motionTarget.y,i,this.motionLerpFactor)}}class ht extends HTMLElement{static TAG_NAME="layershift-portal";static get observedAttributes(){return["src","depth-src","depth-meta","logo-src","parallax-x","parallax-y","parallax-max","overscan","pom-steps","rim-intensity","rim-color","rim-width","refraction-strength","chromatic-strength","occlusion-intensity","depth-power","depth-scale","depth-bias","fog-density","fog-color","color-shift","brightness-bias","contrast-low","contrast-high","vertical-reduction","dof-start","dof-strength","bevel-intensity","bevel-width","bevel-darkening","bevel-desaturation","bevel-light-angle","edge-thickness","edge-specular","edge-color","chamfer-width","chamfer-angle","chamfer-color","chamfer-ambient","chamfer-specular","chamfer-shininess","edge-occlusion-width","edge-occlusion-strength","light-direction","autoplay","loop","muted"]}reinitAttributes=["src","depth-src","depth-meta","logo-src"];shadow;container=null;renderer=null;inputHandler=null;depthWorker=null;video=null;mesh=null;loopCount=0;lifecycle;constructor(){super(),this.shadow=this.attachShadow({mode:"open"}),this.lifecycle=new Et(this)}getAttrFloat(t,e){const i=this.getAttribute(t);if(i===null)return e;const o=parseFloat(i);return Number.isFinite(o)?o:e}getAttrBool(t,e){if(!this.hasAttribute(t))return e;const i=this.getAttribute(t);return!(i==="false"||i==="0")}getAttrColor(t,e){const i=this.getAttribute(t)??e;return Ne(i)}getAttrVec3(t,e){const o=(this.getAttribute(t)??e).split(",").map(s=>parseFloat(s.trim()));if(o.length>=3&&o.every(Number.isFinite))return[o[0],o[1],o[2]];const n=e.split(",").map(s=>parseFloat(s.trim()));return[n[0],n[1],n[2]]}get parallaxX(){return this.getAttrFloat("parallax-x",b.parallaxX)}get parallaxY(){return this.getAttrFloat("parallax-y",b.parallaxY)}get parallaxMax(){return this.getAttrFloat("parallax-max",b.parallaxMax)}get overscan(){return this.getAttrFloat("overscan",b.overscan)}get pomSteps(){return this.getAttrFloat("pom-steps",b.pomSteps)}get rimIntensity(){return this.getAttrFloat("rim-intensity",b.rimIntensity)}get rimWidth(){return this.getAttrFloat("rim-width",b.rimWidth)}get rimColor(){return this.getAttrColor("rim-color",b.rimColor)}get refractionStrength(){return this.getAttrFloat("refraction-strength",b.refractionStrength)}get chromaticStrength(){return this.getAttrFloat("chromatic-strength",b.chromaticStrength)}get occlusionIntensity(){return this.getAttrFloat("occlusion-intensity",b.occlusionIntensity)}get depthPower(){return this.getAttrFloat("depth-power",b.depthPower)}get depthScale(){return this.getAttrFloat("depth-scale",b.depthScale)}get depthBias(){return this.getAttrFloat("depth-bias",b.depthBias)}get fogDensity(){return this.getAttrFloat("fog-density",b.fogDensity)}get fogColor(){return this.getAttrColor("fog-color",b.fogColor)}get colorShift(){return this.getAttrFloat("color-shift",b.colorShift)}get brightnessBias(){return this.getAttrFloat("brightness-bias",b.brightnessBias)}get contrastLow(){return this.getAttrFloat("contrast-low",b.contrastLow)}get contrastHigh(){return this.getAttrFloat("contrast-high",b.contrastHigh)}get verticalReduction(){return this.getAttrFloat("vertical-reduction",b.verticalReduction)}get dofStart(){return this.getAttrFloat("dof-start",b.dofStart)}get dofStrength(){return this.getAttrFloat("dof-strength",b.dofStrength)}get bevelIntensity(){return this.getAttrFloat("bevel-intensity",b.bevelIntensity)}get bevelWidth(){return this.getAttrFloat("bevel-width",b.bevelWidth)}get bevelDarkening(){return this.getAttrFloat("bevel-darkening",b.bevelDarkening)}get bevelDesaturation(){return this.getAttrFloat("bevel-desaturation",b.bevelDesaturation)}get bevelLightAngle(){return this.getAttrFloat("bevel-light-angle",b.bevelLightAngle)}get edgeThickness(){return this.getAttrFloat("edge-thickness",b.edgeThickness)}get edgeSpecular(){return this.getAttrFloat("edge-specular",b.edgeSpecular)}get edgeColor(){return this.getAttrColor("edge-color",b.edgeColor)}get chamferWidth(){return this.getAttrFloat("chamfer-width",b.chamferWidth)}get chamferAngle(){return this.getAttrFloat("chamfer-angle",b.chamferAngle)}get chamferColor(){return this.getAttrColor("chamfer-color",b.chamferColor)}get chamferAmbient(){return this.getAttrFloat("chamfer-ambient",b.chamferAmbient)}get chamferSpecular(){return this.getAttrFloat("chamfer-specular",b.chamferSpecular)}get chamferShininess(){return this.getAttrFloat("chamfer-shininess",b.chamferShininess)}get edgeOcclusionWidth(){return this.getAttrFloat("edge-occlusion-width",b.edgeOcclusionWidth)}get edgeOcclusionStrength(){return this.getAttrFloat("edge-occlusion-strength",b.edgeOcclusionStrength)}get lightDirection3(){return this.getAttrVec3("light-direction",b.lightDirection)}get shouldAutoplay(){return this.getAttrBool("autoplay",b.autoplay)}get shouldLoop(){return this.getAttrBool("loop",b.loop)}get shouldMute(){return this.getAttrBool("muted",b.muted)}emit(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}attachVideoEventListeners(t){t.addEventListener("play",()=>{this.emit("layershift-portal:play",{currentTime:t.currentTime})}),t.addEventListener("pause",()=>{this.emit("layershift-portal:pause",{currentTime:t.currentTime})}),t.addEventListener("ended",()=>{t.loop&&(this.loopCount+=1,this.emit("layershift-portal:loop",{loopCount:this.loopCount}))})}connectedCallback(){this.lifecycle.onConnected()}disconnectedCallback(){this.lifecycle.onDisconnected()}attributeChangedCallback(t,e,i){this.lifecycle.onAttributeChanged(t,e,i)}setupShadowDOM(){this.shadow.innerHTML="";const t=document.createElement("style");t.textContent=`
967
+ return sum / 13.0;
968
+ }
969
+
970
+ void main() {
971
+ vec3 N = normalize(vNormal);
972
+ vec3 L = normalize(uLightDir3);
973
+ vec3 V = vec3(0.0, 0.0, -1.0); // orthographic view direction
974
+
975
+ // Blinn-Phong lighting in linear space
976
+ float diff = max(dot(N, L), 0.0);
977
+ vec3 H = normalize(L + V);
978
+ float spec = pow(max(dot(N, H), 0.0), uChamferShininess) * uChamferSpecular;
979
+
980
+ // Sample interior video with progressive blur (sharper at inner edge)
981
+ vec2 uv = clamp(vScreenUv, vec2(0.001), vec2(0.999));
982
+ float blurRadius = vLerpT * 12.0 * length(uTexelSize);
983
+ vec3 videoSample = blurRadius > 0.0001
984
+ ? blurSample(uv, blurRadius)
985
+ : texture(uInteriorColor, uv).rgb;
986
+
987
+ // Base color: video tinted through chamfer color (like frosted glass)
988
+ vec3 video = toLinear(videoSample);
989
+ vec3 tint = toLinear(uChamferColor);
990
+ // Blend: mostly video near inner edge, more tinted at outer edge
991
+ vec3 base = mix(video, video * tint * 3.0, vLerpT * 0.5);
992
+
993
+ // Apply Blinn-Phong
994
+ vec3 lit = base * (uChamferAmbient + (1.0 - uChamferAmbient) * diff) + vec3(spec);
995
+ fragColor = vec4(toSRGB(lit), 1.0);
996
+ }
997
+ `;function Je(o){const t=[];let e=0;for(let n=0;n<o.length-2;n+=2){const i=o[n],r=o[n+1],s=o[n+2],a=o[n+3],l=s-i,u=a-r,h=Math.sqrt(l*l+u*u);if(h<1e-6)continue;const f=-u/h,c=l/h;t.push(i,r,f,c,i,r,-f,-c,s,a,f,c,s,a,f,c,i,r,-f,-c,s,a,-f,-c),e+=6}return{vertices:new Float32Array(t),count:e}}function Qe(o,t,e,n,i){if(n<=0)return{vertices:new Float32Array(0),count:0};const r=i*Math.PI/180,s=-Math.cos(r),a=Math.sin(r),l=[];let u=0;for(let h=0;h<t.length;h++){const f=t[h],v=((h+1<t.length?t[h+1]:o.length)-f)/2;if(v<3)continue;const p=v-1;let P=0;for(let T=0;T<p;T++){const m=f+T*2,S=o[m],w=o[m+1],F=o[m+2],U=o[m+3];P+=S*U-F*w}const x=P>=0?1:-1,y=[],d=[];for(let T=0;T<p;T++){const m=f+T*2,S=o[m+2]-o[m],w=o[m+3]-o[m+1],F=Math.sqrt(S*S+w*w);F<1e-8?(y.push(T>0?y[T-1]:0),d.push(T>0?d[T-1]:0)):(y.push(-w/F*x),d.push(S/F*x))}const g=[],b=[];for(let T=0;T<p;T++){const m=(T-1+p)%p;let S=y[m]+y[T],w=d[m]+d[T];const F=Math.sqrt(S*S+w*w);F>1e-8?(S/=F,w/=F):(S=y[T],w=d[T]),g.push(S),b.push(w)}for(let T=0;T<p;T++){const m=T,S=(T+1)%p,w=f+T*2,F=f+(T+1)%p*2,U=o[w],_=o[w+1],R=o[F],D=o[F+1],L=g[m]*a,M=b[m]*a,V=s,B=g[S]*a,H=b[S]*a,ot=s,Tt=U+g[m]*n,Jt=_+b[m]*n,Fn=R+g[S]*n,Cn=D+b[S]*n;l.push(U,_,L,M,V,0),l.push(Tt,Jt,L,M,V,1),l.push(R,D,B,H,ot,0),l.push(R,D,B,H,ot,0),l.push(Tt,Jt,L,M,V,1),l.push(Fn,Cn,B,H,ot,1),u+=6}}return{vertices:new Float32Array(l),count:u}}class tn extends at{gl=null;stencilPass=null;maskPass=null;jfaSeedPass=null;jfaFloodPass=null;jfaDistPass=null;interiorPass=null;compositePass=null;boundaryPass=null;chamferPass=null;quadVao=null;stencilVao=null;stencilIndexCount=0;maskVao=null;boundaryVao=null;boundaryVertexCount=0;chamferVao=null;chamferVertexCount=0;textures=new Mt;videoSlot;depthSlot;interiorFbo=null;interiorColorTex=null;interiorDepthTex=null;fboWidth=0;fboHeight=0;jfa=null;hasColorBufferFloat=!1;meshAspect=1;meshScaleX=.65;meshScaleY=.65;lightDirX=-.707;lightDirY=.707;lightDir3=[-.5,.7,-.3];config;constructor(t,e){super(t),this.config={...e},this.videoSlot=this.textures.register("video"),this.depthSlot=this.textures.register("depth");const n=this.config.bevelLightAngle*Math.PI/180;this.lightDirX=Math.cos(n),this.lightDirY=Math.sin(n);const i=this.config.lightDirection,r=Math.sqrt(i[0]*i[0]+i[1]*i[1]+i[2]*i[2]);r>1e-6&&(this.lightDir3=[i[0]/r,i[1]/r,i[2]/r]);const s=this.canvas.getContext("webgl2",{antialias:!0,alpha:!0,premultipliedAlpha:!0,stencil:!0,desynchronized:!0,powerPreference:"high-performance"});if(!s)throw new Error("WebGL 2 is not supported.");this.gl=s,this.qualityParams=It(s,e.quality),"drawingBufferColorSpace"in s&&(s.drawingBufferColorSpace="srgb"),this.hasColorBufferFloat=!!s.getExtension("EXT_color_buffer_float"),s.clearColor(0,0,0,0),s.pixelStorei(s.UNPACK_FLIP_Y_WEBGL,!0),this.initGPUResources(),this.setupResizeHandling()}initialize(t,e,n,i){const r=this.gl;r&&(this.disposeTextures(),this.disposeFBO(),this.jfa&&(this.jfa.dispose(),this.jfa=null),this.disposeStencilGeometry(),this.disposeBoundaryGeometry(),this.disposeChamferGeometry(),this.isCameraSource=t.type==="camera",this.videoAspect=t.width/t.height,this.meshAspect=i.aspect,this.clampDepthDimensions(e,n,this.qualityParams.depthMaxDim),this.videoSlot.texture=r.createTexture(),r.activeTexture(r.TEXTURE0+this.videoSlot.unit),r.bindTexture(r.TEXTURE_2D,this.videoSlot.texture),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MIN_FILTER,r.LINEAR),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MAG_FILTER,r.LINEAR),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_S,r.CLAMP_TO_EDGE),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_T,r.CLAMP_TO_EDGE),this.depthSlot.texture=r.createTexture(),r.activeTexture(r.TEXTURE0+this.depthSlot.unit),r.bindTexture(r.TEXTURE_2D,this.depthSlot.texture),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MIN_FILTER,r.LINEAR),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_MAG_FILTER,r.LINEAR),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_S,r.CLAMP_TO_EDGE),r.texParameteri(r.TEXTURE_2D,r.TEXTURE_WRAP_T,r.CLAMP_TO_EDGE),r.texStorage2D(r.TEXTURE_2D,1,r.R8,this.depthWidth,this.depthHeight),this.uploadStencilMesh(i),this.uploadMaskMesh(i),this.uploadBoundaryMesh(i),this.uploadChamferMesh(i),this.interiorPass&&(r.useProgram(this.interiorPass.program),r.uniform1i(this.interiorPass.uniforms.uImage,0),r.uniform1i(this.interiorPass.uniforms.uDepth,1),r.uniform1f(this.interiorPass.uniforms.uStrength,this.config.parallaxStrength),r.uniform1i(this.interiorPass.uniforms.uPomSteps,this.config.pomSteps),r.uniform1f(this.interiorPass.uniforms.uDepthPower,this.config.depthPower),r.uniform1f(this.interiorPass.uniforms.uDepthScale,this.config.depthScale),r.uniform1f(this.interiorPass.uniforms.uDepthBias,this.config.depthBias),r.uniform1f(this.interiorPass.uniforms.uContrastLow,this.config.contrastLow),r.uniform1f(this.interiorPass.uniforms.uContrastHigh,this.config.contrastHigh),r.uniform1f(this.interiorPass.uniforms.uVerticalReduction,this.config.verticalReduction),r.uniform1f(this.interiorPass.uniforms.uDofStart,this.config.dofStart),r.uniform1f(this.interiorPass.uniforms.uDofStrength,this.config.dofStrength),r.uniform2f(this.interiorPass.uniforms.uImageTexelSize,1/t.width,1/t.height),r.uniform1f(this.interiorPass.uniforms.uFogDensity,this.config.fogDensity),r.uniform3f(this.interiorPass.uniforms.uFogColor,...this.config.fogColor),r.uniform1f(this.interiorPass.uniforms.uColorShift,this.config.colorShift),r.uniform1f(this.interiorPass.uniforms.uBrightnessBias,this.config.brightnessBias)),this.compositePass&&(r.useProgram(this.compositePass.program),r.uniform1i(this.compositePass.uniforms.uInteriorColor,2),r.uniform1i(this.compositePass.uniforms.uDistField,4),r.uniform1f(this.compositePass.uniforms.uEdgeOcclusionWidth,this.config.edgeOcclusionWidth),r.uniform1f(this.compositePass.uniforms.uEdgeOcclusionStrength,this.config.edgeOcclusionStrength)),this.chamferPass&&(r.useProgram(this.chamferPass.program),r.uniform3f(this.chamferPass.uniforms.uLightDir3,...this.lightDir3),r.uniform3f(this.chamferPass.uniforms.uChamferColor,...this.config.chamferColor),r.uniform1f(this.chamferPass.uniforms.uChamferAmbient,this.config.chamferAmbient),r.uniform1f(this.chamferPass.uniforms.uChamferSpecular,this.config.chamferSpecular),r.uniform1f(this.chamferPass.uniforms.uChamferShininess,this.config.chamferShininess),r.uniform1i(this.chamferPass.uniforms.uInteriorColor,2)),this.boundaryPass&&(r.useProgram(this.boundaryPass.program),r.uniform1i(this.boundaryPass.uniforms.uInteriorColor,2),r.uniform1i(this.boundaryPass.uniforms.uInteriorDepth,3),r.uniform1i(this.boundaryPass.uniforms.uDistField,4),r.uniform1f(this.boundaryPass.uniforms.uRimIntensity,this.config.rimLightIntensity),r.uniform3f(this.boundaryPass.uniforms.uRimColor,...this.config.rimLightColor),r.uniform1f(this.boundaryPass.uniforms.uRefractionStrength,this.config.refractionStrength),r.uniform1f(this.boundaryPass.uniforms.uChromaticStrength,this.config.chromaticStrength),r.uniform1f(this.boundaryPass.uniforms.uOcclusionIntensity,this.config.occlusionIntensity),r.uniform1f(this.boundaryPass.uniforms.uEdgeThickness,this.config.edgeThickness),r.uniform1f(this.boundaryPass.uniforms.uEdgeSpecular,this.config.edgeSpecular),r.uniform3f(this.boundaryPass.uniforms.uEdgeColor,...this.config.edgeColor),r.uniform2f(this.boundaryPass.uniforms.uLightDir,this.lightDirX,this.lightDirY),r.uniform1f(this.boundaryPass.uniforms.uBevelIntensity,this.config.bevelIntensity)),this.recalculateViewportLayout())}uploadStencilMesh(t){const e=this.gl;if(!e||!this.stencilPass)return;this.stencilVao=e.createVertexArray(),e.bindVertexArray(this.stencilVao);const n=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,n),e.bufferData(e.ARRAY_BUFFER,t.vertices,e.STATIC_DRAW);const i=e.getAttribLocation(this.stencilPass.program,"aPosition");e.enableVertexAttribArray(i),e.vertexAttribPointer(i,2,e.FLOAT,!1,0,0);const r=e.createBuffer();e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,r),e.bufferData(e.ELEMENT_ARRAY_BUFFER,t.indices,e.STATIC_DRAW),this.stencilIndexCount=t.indices.length,e.bindVertexArray(null)}uploadMaskMesh(t){const e=this.gl;if(!e||!this.maskPass)return;this.maskVao=e.createVertexArray(),e.bindVertexArray(this.maskVao);const n=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,n),e.bufferData(e.ARRAY_BUFFER,t.vertices,e.STATIC_DRAW);const i=e.getAttribLocation(this.maskPass.program,"aPosition");e.enableVertexAttribArray(i),e.vertexAttribPointer(i,2,e.FLOAT,!1,0,0);const r=e.createBuffer();e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,r),e.bufferData(e.ELEMENT_ARRAY_BUFFER,t.indices,e.STATIC_DRAW),e.bindVertexArray(null)}uploadBoundaryMesh(t){const e=this.gl;if(!e||!this.boundaryPass)return;const n=Je(t.edgeVertices);if(n.count===0)return;this.boundaryVao=e.createVertexArray(),e.bindVertexArray(this.boundaryVao);const i=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,i),e.bufferData(e.ARRAY_BUFFER,n.vertices,e.STATIC_DRAW);const r=16,s=e.getAttribLocation(this.boundaryPass.program,"aPosition");e.enableVertexAttribArray(s),e.vertexAttribPointer(s,2,e.FLOAT,!1,r,0);const a=e.getAttribLocation(this.boundaryPass.program,"aNormal");a>=0&&(e.enableVertexAttribArray(a),e.vertexAttribPointer(a,2,e.FLOAT,!1,r,8)),this.boundaryVertexCount=n.count,e.bindVertexArray(null)}uploadChamferMesh(t){const e=this.gl;if(!e||!this.chamferPass||this.config.chamferWidth<=0)return;const n=Qe(t.edgeVertices,t.contourOffsets,t.contourIsHole,this.config.chamferWidth,this.config.chamferAngle);if(n.count===0)return;this.chamferVao=e.createVertexArray(),e.bindVertexArray(this.chamferVao);const i=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,i),e.bufferData(e.ARRAY_BUFFER,n.vertices,e.STATIC_DRAW);const r=24,s=e.getAttribLocation(this.chamferPass.program,"aPosition");e.enableVertexAttribArray(s),e.vertexAttribPointer(s,2,e.FLOAT,!1,r,0);const a=e.getAttribLocation(this.chamferPass.program,"aNormal3");a>=0&&(e.enableVertexAttribArray(a),e.vertexAttribPointer(a,3,e.FLOAT,!1,r,8));const l=e.getAttribLocation(this.chamferPass.program,"aLerpT");l>=0&&(e.enableVertexAttribArray(l),e.vertexAttribPointer(l,1,e.FLOAT,!1,r,20)),this.chamferVertexCount=n.count,e.bindVertexArray(null)}disposeChamferGeometry(){const t=this.gl;t&&(this.chamferVao&&(t.deleteVertexArray(this.chamferVao),this.chamferVao=null),this.chamferVertexCount=0)}createFBO(t,e){const n=this.gl;if(!n)return;this.disposeFBO(),this.fboWidth=t,this.fboHeight=e,this.interiorFbo=n.createFramebuffer(),n.bindFramebuffer(n.FRAMEBUFFER,this.interiorFbo),this.interiorColorTex=n.createTexture(),n.activeTexture(n.TEXTURE2),n.bindTexture(n.TEXTURE_2D,this.interiorColorTex),n.texStorage2D(n.TEXTURE_2D,1,n.RGBA8,t,e),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),n.framebufferTexture2D(n.FRAMEBUFFER,n.COLOR_ATTACHMENT0,n.TEXTURE_2D,this.interiorColorTex,0),this.interiorDepthTex=n.createTexture(),n.activeTexture(n.TEXTURE3),n.bindTexture(n.TEXTURE_2D,this.interiorDepthTex),n.texStorage2D(n.TEXTURE_2D,1,n.RGBA8,t,e),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),n.framebufferTexture2D(n.FRAMEBUFFER,n.COLOR_ATTACHMENT1,n.TEXTURE_2D,this.interiorDepthTex,0),n.drawBuffers([n.COLOR_ATTACHMENT0,n.COLOR_ATTACHMENT1]);const i=n.checkFramebufferStatus(n.FRAMEBUFFER);i!==n.FRAMEBUFFER_COMPLETE&&console.error("Interior FBO incomplete:",i),n.bindFramebuffer(n.FRAMEBUFFER,null)}createJFAResources(t,e){const n=this.gl;n&&(this.jfa||(this.jfa=new wt(n,this.hasColorBufferFloat)),this.jfa.createResources(t,e,this.qualityParams.jfaDivisor))}computeDistanceField(){!this.jfa||!this.maskPass||!this.jfaSeedPass||!this.jfaFloodPass||!this.jfaDistPass||!this.maskVao||!this.quadVao||this.jfa.compute({maskPass:this.maskPass,seedPass:this.jfaSeedPass,floodPass:this.jfaFloodPass,distPass:this.jfaDistPass,maskVao:this.maskVao,quadVao:this.quadVao,meshScaleX:this.meshScaleX,meshScaleY:this.meshScaleY,stencilIndexCount:this.stencilIndexCount,distRange:Math.max(this.config.bevelWidth,this.config.edgeOcclusionWidth)})}initGPUResources(){const t=this.gl;t&&(this.stencilPass=k(t,"stencil",Me,Ie,["uMeshScale"]),this.maskPass=k(t,"mask",Be,Ve,["uMeshScale"]),this.jfaSeedPass=k(t,"jfa-seed",Oe,ke,["uMask","uTexelSize"]),this.jfaFloodPass=k(t,"jfa-flood",Ne,He,["uSeedTex","uStepSize"]),this.jfaDistPass=k(t,"jfa-dist",Xe,Ge,["uSeedTex","uMask","uBevelWidth"]),this.interiorPass=k(t,"interior",We,ze,["uImage","uDepth","uOffset","uStrength","uPomSteps","uDepthPower","uDepthScale","uDepthBias","uContrastLow","uContrastHigh","uVerticalReduction","uDofStart","uDofStrength","uImageTexelSize","uFogDensity","uFogColor","uColorShift","uBrightnessBias","uUvOffset","uUvScale"]),this.compositePass=k(t,"composite",qe,Ye,["uInteriorColor","uDistField","uEdgeOcclusionWidth","uEdgeOcclusionStrength"]),this.boundaryPass=k(t,"boundary",je,Ze,["uInteriorColor","uInteriorDepth","uDistField","uRimIntensity","uRimColor","uRimWidth","uMeshScale","uRefractionStrength","uChromaticStrength","uOcclusionIntensity","uTexelSize","uEdgeThickness","uEdgeSpecular","uEdgeColor","uLightDir","uBevelIntensity"]),this.chamferPass=k(t,"chamfer",$e,Ke,["uMeshScale","uLightDir3","uChamferColor","uChamferAmbient","uChamferSpecular","uChamferShininess","uInteriorColor","uTexelSize"]),this.quadVao=_t(t,this.interiorPass.program),t.disable(t.DEPTH_TEST))}onRenderFrame(){const t=this.gl,e=this.mediaSource;if(!t||!this.interiorPass||!this.quadVao)return;const n=e?.getImageSource();if(!n||!this.interiorFbo||!this.interiorColorTex||!this.interiorDepthTex)return;this.jfa?.isDirty&&this.maskVao&&(this.computeDistanceField(),t.viewport(0,0,this.canvas.width,this.canvas.height)),t.activeTexture(t.TEXTURE0+this.videoSlot.unit),t.bindTexture(t.TEXTURE_2D,this.videoSlot.texture),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,n),this.rvfcSupported||this.onDepthUpdate(e.currentTime);let i=0,r=0;if(this.readInput){const s=this.readInput();i=-s.x,r=s.y}if(t.bindFramebuffer(t.FRAMEBUFFER,this.interiorFbo),t.checkFramebufferStatus(t.FRAMEBUFFER)!==t.FRAMEBUFFER_COMPLETE){t.bindFramebuffer(t.FRAMEBUFFER,null);return}t.viewport(0,0,this.fboWidth,this.fboHeight),t.clearColor(0,0,0,1),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(this.interiorPass.program),t.uniform2f(this.interiorPass.uniforms.uOffset,i,r),t.activeTexture(t.TEXTURE0+this.videoSlot.unit),t.bindTexture(t.TEXTURE_2D,this.videoSlot.texture),t.activeTexture(t.TEXTURE0+this.depthSlot.unit),t.bindTexture(t.TEXTURE_2D,this.depthSlot.texture),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4),t.bindFramebuffer(t.FRAMEBUFFER,null),t.clearColor(0,0,0,0),t.viewport(0,0,this.canvas.width,this.canvas.height),t.clear(t.COLOR_BUFFER_BIT|t.STENCIL_BUFFER_BIT),this.stencilVao&&this.stencilPass&&this.stencilIndexCount>0&&(t.enable(t.STENCIL_TEST),t.stencilFunc(t.ALWAYS,1,255),t.stencilOp(t.KEEP,t.KEEP,t.REPLACE),t.stencilMask(255),t.colorMask(!1,!1,!1,!1),t.useProgram(this.stencilPass.program),t.bindVertexArray(this.stencilVao),t.drawElements(t.TRIANGLES,this.stencilIndexCount,t.UNSIGNED_SHORT,0),t.colorMask(!0,!0,!0,!0)),t.stencilFunc(t.EQUAL,1,255),t.stencilMask(0),t.activeTexture(t.TEXTURE2),t.bindTexture(t.TEXTURE_2D,this.interiorColorTex),t.activeTexture(t.TEXTURE3),t.bindTexture(t.TEXTURE_2D,this.interiorDepthTex),t.activeTexture(t.TEXTURE4),t.bindTexture(t.TEXTURE_2D,this.jfa?.distanceTexture??null),t.useProgram(this.compositePass.program),t.bindVertexArray(this.quadVao),t.drawArrays(t.TRIANGLE_STRIP,0,4),t.disable(t.STENCIL_TEST),this.chamferVao&&this.chamferPass&&this.chamferVertexCount>0&&(t.useProgram(this.chamferPass.program),t.uniform2f(this.chamferPass.uniforms.uMeshScale,this.meshScaleX,this.meshScaleY),t.uniform2f(this.chamferPass.uniforms.uTexelSize,1/this.canvas.width,1/this.canvas.height),t.bindVertexArray(this.chamferVao),t.drawArrays(t.TRIANGLES,0,this.chamferVertexCount)),this.boundaryVao&&this.boundaryPass&&this.boundaryVertexCount>0&&this.config.rimLightIntensity>0&&(t.enable(t.BLEND),t.blendFunc(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA),t.useProgram(this.boundaryPass.program),t.bindVertexArray(this.boundaryVao),t.drawArrays(t.TRIANGLES,0,this.boundaryVertexCount),t.disable(t.BLEND))}onDepthUpdate(t){const e=this.gl;if(!e||!this.readDepth||!this.depthSlot.texture)return;const n=this.subsampleDepth(this.readDepth(t));e.activeTexture(e.TEXTURE0+this.depthSlot.unit),e.bindTexture(e.TEXTURE_2D,this.depthSlot.texture),e.texSubImage2D(e.TEXTURE_2D,0,0,0,this.depthWidth,this.depthHeight,e.RED,e.UNSIGNED_BYTE,n)}recalculateViewportLayout(){const t=this.gl;if(!t)return;const{width:e,height:n}=this.getViewportSize(),i=Math.min(window.devicePixelRatio,this.qualityParams.dprCap),r=Math.round(e*i),s=Math.round(n*i);(this.canvas.width!==r||this.canvas.height!==s)&&(this.canvas.width=r,this.canvas.height=s,t.viewport(0,0,r,s)),(this.fboWidth!==r||this.fboHeight!==s)&&this.createFBO(r,s);const a=this.qualityParams.jfaDivisor,l=Math.max(1,Math.round(r/a)),u=Math.max(1,Math.round(s/a));(!this.jfa||this.jfa.width!==l||this.jfa.height!==u)&&this.createJFAResources(r,s),this.computeCoverFitUV(this.config.parallaxStrength,this.config.overscanPadding),this.interiorPass&&(t.useProgram(this.interiorPass.program),t.uniform2f(this.interiorPass.uniforms.uUvOffset,this.uvOffset[0],this.uvOffset[1]),t.uniform2f(this.interiorPass.uniforms.uUvScale,this.uvScale[0],this.uvScale[1]));const h=e/n,f=.65;this.meshScaleX=f,this.meshScaleY=f,h>this.meshAspect?this.meshScaleX=f*(this.meshAspect/h):this.meshScaleY=f*(h/this.meshAspect),this.stencilPass&&(t.useProgram(this.stencilPass.program),t.uniform2f(this.stencilPass.uniforms.uMeshScale,this.meshScaleX,this.meshScaleY)),this.boundaryPass&&(t.useProgram(this.boundaryPass.program),t.uniform2f(this.boundaryPass.uniforms.uMeshScale,this.meshScaleX,this.meshScaleY),t.uniform1f(this.boundaryPass.uniforms.uRimWidth,this.config.rimLightWidth),t.uniform2f(this.boundaryPass.uniforms.uTexelSize,1/r,1/s)),this.chamferPass&&(t.useProgram(this.chamferPass.program),t.uniform2f(this.chamferPass.uniforms.uMeshScale,this.meshScaleX,this.meshScaleY)),this.jfa&&this.jfa.markDirty()}onContextRestored(){const t=this.canvas.getContext("webgl2",{alpha:!0,premultipliedAlpha:!0,stencil:!0});t&&(this.gl=t,this.hasColorBufferFloat=!!t.getExtension("EXT_color_buffer_float"),t.clearColor(0,0,0,0),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!0),this.initGPUResources(),this.recalculateViewportLayout(),this.mediaSource&&(this.animationFrameHandle=window.requestAnimationFrame(()=>this.onRenderFrame())))}disposeTextures(){const t=this.gl;t&&this.textures.disposeAll(t)}disposeFBO(){const t=this.gl;t&&(this.interiorColorTex&&(t.deleteTexture(this.interiorColorTex),this.interiorColorTex=null),this.interiorDepthTex&&(t.deleteTexture(this.interiorDepthTex),this.interiorDepthTex=null),this.interiorFbo&&(t.deleteFramebuffer(this.interiorFbo),this.interiorFbo=null),this.fboWidth=0,this.fboHeight=0)}disposeStencilGeometry(){const t=this.gl;t&&(this.stencilVao&&(t.deleteVertexArray(this.stencilVao),this.stencilVao=null),this.maskVao&&(t.deleteVertexArray(this.maskVao),this.maskVao=null),this.stencilIndexCount=0)}disposeBoundaryGeometry(){const t=this.gl;t&&(this.boundaryVao&&(t.deleteVertexArray(this.boundaryVao),this.boundaryVao=null),this.boundaryVertexCount=0)}disposeRenderer(){this.disposeTextures(),this.disposeFBO(),this.jfa&&(this.jfa.dispose(),this.jfa=null),this.disposeStencilGeometry(),this.disposeBoundaryGeometry(),this.disposeChamferGeometry(),this.disposeGPUResources(),this.gl&&(this.gl.getExtension("WEBGL_lose_context")?.loseContext(),this.gl=null)}disposeGPUResources(){const t=this.gl;if(!t)return;const e=[this.stencilPass,this.maskPass,this.jfaSeedPass,this.jfaFloodPass,this.jfaDistPass,this.interiorPass,this.compositePass,this.boundaryPass,this.chamferPass];for(const n of e)n&&n.dispose(t);this.stencilPass=null,this.maskPass=null,this.jfaSeedPass=null,this.jfaFloodPass=null,this.jfaDistPass=null,this.interiorPass=null,this.compositePass=null,this.boundaryPass=null,this.chamferPass=null,this.quadVao&&(t.deleteVertexArray(this.quadVao),this.quadVao=null)}}async function Rt(o){const t=await fetch(o);if(!t.ok)throw new Error(`Failed to fetch SVG: ${t.status} ${t.statusText}`);const e=await t.text();return en(e)}function en(o){const n=new DOMParser().parseFromString(o,"image/svg+xml").querySelector("svg");if(!n)throw new Error("No <svg> element found in document.");const i=nn(n);if(i.length===0)throw new Error("No path data found in SVG.");let r=1/0,s=1/0,a=-1/0,l=-1/0;for(const R of i)for(let D=0;D<R.length;D+=2)r=Math.min(r,R[D]),a=Math.max(a,R[D]),s=Math.min(s,R[D+1]),l=Math.max(l,R[D+1]);const u=a-r,h=l-s,f=(r+a)/2,c=(s+l)/2,E=2/Math.max(u,h),v=u/h,p=i.map(R=>{const D=[];for(let L=0;L<R.length;L+=2)D.push((R[L]-f)*E),D.push(-((R[L+1]-c)*E));return D}),P=cn(p),x=[],y=[];for(const R of P){const{flatCoords:D,holeIndices:L}=un(R),M=fn(D,L),V=x.length/2;for(const B of M)y.push(B+V);for(const B of D)x.push(B)}const d=x,g=y,b=[],T=[],m=[],S=zt(p);for(let R=0;R<p.length;R++){const D=p[R];T.push(b.length),m.push(S[R]);for(let L=0;L<D.length;L++)b.push(D[L]);D.length>=2&&b.push(D[0],D[1])}let w=1/0,F=1/0,U=-1/0,_=-1/0;for(let R=0;R<d.length;R+=2)w=Math.min(w,d[R]),U=Math.max(U,d[R]),F=Math.min(F,d[R+1]),_=Math.max(_,d[R+1]);return{vertices:new Float32Array(d),indices:new Uint16Array(g),edgeVertices:new Float32Array(b),contourOffsets:T,contourIsHole:m,bounds:{minX:w,maxX:U,minY:F,maxY:_},aspect:v}}function nn(o){const t=[];return o.querySelectorAll("path").forEach(l=>{const u=l.getAttribute("d");if(!u)return;const h=sn(u);t.push(...h)}),o.querySelectorAll("polygon").forEach(l=>{const u=l.getAttribute("points");if(!u)return;const h=Xt(u);h.length>=6&&t.push(h)}),o.querySelectorAll("polyline").forEach(l=>{const u=l.getAttribute("points");if(!u)return;const h=Xt(u);h.length>=6&&t.push(h)}),o.querySelectorAll("rect").forEach(l=>{const u=parseFloat(l.getAttribute("x")||"0"),h=parseFloat(l.getAttribute("y")||"0"),f=parseFloat(l.getAttribute("width")||"0"),c=parseFloat(l.getAttribute("height")||"0");f>0&&c>0&&t.push([u,h,u+f,h,u+f,h+c,u,h+c])}),o.querySelectorAll("circle").forEach(l=>{const u=parseFloat(l.getAttribute("cx")||"0"),h=parseFloat(l.getAttribute("cy")||"0"),f=parseFloat(l.getAttribute("r")||"0");f>0&&t.push(rn(u,h,f))}),o.querySelectorAll("ellipse").forEach(l=>{const u=parseFloat(l.getAttribute("cx")||"0"),h=parseFloat(l.getAttribute("cy")||"0"),f=parseFloat(l.getAttribute("rx")||"0"),c=parseFloat(l.getAttribute("ry")||"0");f>0&&c>0&&t.push(on(u,h,f,c))}),t}function Xt(o){const t=[],e=o.trim().split(/[\s,]+/);for(let n=0;n<e.length-1;n+=2){const i=parseFloat(e[n]),r=parseFloat(e[n+1]);Number.isFinite(i)&&Number.isFinite(r)&&t.push(i,r)}return t}function rn(o,t,e,n=64){const i=[];for(let r=0;r<n;r++){const s=2*Math.PI*r/n;i.push(o+e*Math.cos(s),t+e*Math.sin(s))}return i}function on(o,t,e,n,i=64){const r=[];for(let s=0;s<i;s++){const a=2*Math.PI*s/i;r.push(o+e*Math.cos(a),t+n*Math.sin(a))}return r}function sn(o){const t=[];let e=[],n=0,i=0,r=0,s=0,a=0,l=0,u="";const h=an(o);let f=0;function c(){return f>=h.length?0:parseFloat(h[f++])}for(;f<h.length;){const E=h[f];let v;/^[a-zA-Z]$/.test(E)?(v=E,f++):v=u==="M"?"L":u==="m"?"l":u;const p=v===v.toLowerCase();switch(v.toUpperCase()){case"M":{e.length>0&&t.push(e),e=[];const x=c()+(p?n:0),y=c()+(p?i:0);n=x,i=y,r=x,s=y,e.push(n,i),a=n,l=i;break}case"L":{n=c()+(p?n:0),i=c()+(p?i:0),e.push(n,i),a=n,l=i;break}case"H":{n=c()+(p?n:0),e.push(n,i),a=n,l=i;break}case"V":{i=c()+(p?i:0),e.push(n,i),a=n,l=i;break}case"C":{const x=c()+(p?n:0),y=c()+(p?i:0),d=c()+(p?n:0),g=c()+(p?i:0),b=c()+(p?n:0),T=c()+(p?i:0);et(e,n,i,x,y,d,g,b,T),n=b,i=T,a=d,l=g;break}case"S":{const x=2*n-a,y=2*i-l,d=c()+(p?n:0),g=c()+(p?i:0),b=c()+(p?n:0),T=c()+(p?i:0);et(e,n,i,x,y,d,g,b,T),n=b,i=T,a=d,l=g;break}case"Q":{const x=c()+(p?n:0),y=c()+(p?i:0),d=c()+(p?n:0),g=c()+(p?i:0);Gt(e,n,i,x,y,d,g),n=d,i=g,a=x,l=y;break}case"T":{const x=2*n-a,y=2*i-l,d=c()+(p?n:0),g=c()+(p?i:0);Gt(e,n,i,x,y,d,g),n=d,i=g,a=x,l=y;break}case"A":{const x=c(),y=c(),d=c(),g=c(),b=c(),T=c()+(p?n:0),m=c()+(p?i:0);hn(e,n,i,x,y,d,!!g,!!b,T,m),n=T,i=m,a=n,l=i;break}case"Z":{n=r,i=s,e.length>0&&t.push(e),e=[],a=n,l=i;break}default:f++;break}u=v}return e.length>=6&&t.push(e),t}function an(o){const t=[],e=/([a-zA-Z])|([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)/g;let n;for(;(n=e.exec(o))!==null;)t.push(n[0]);return t}const ln=.5;function et(o,t,e,n,i,r,s,a,l,u=0){if(u>12){o.push(a,l);return}const h=a-t,f=l-e,c=Math.sqrt(h*h+f*f);if(c<1e-6){o.push(a,l);return}const E=Math.abs((n-a)*f-(i-l)*h)/c,v=Math.abs((r-a)*f-(s-l)*h)/c;if(E+v<ln){o.push(a,l);return}const p=(t+n)/2,P=(e+i)/2,x=(n+r)/2,y=(i+s)/2,d=(r+a)/2,g=(s+l)/2,b=(p+x)/2,T=(P+y)/2,m=(x+d)/2,S=(y+g)/2,w=(b+m)/2,F=(T+S)/2;et(o,t,e,p,P,b,T,w,F,u+1),et(o,w,F,m,S,d,g,a,l,u+1)}function Gt(o,t,e,n,i,r,s){const a=t+.6666666666666666*(n-t),l=e+2/3*(i-e),u=r+2/3*(n-r),h=s+2/3*(i-s);et(o,t,e,a,l,u,h,r,s)}function hn(o,t,e,n,i,r,s,a,l,u){if(n===0||i===0){o.push(l,u);return}let h=Math.abs(n),f=Math.abs(i);const c=r*Math.PI/180,E=Math.cos(c),v=Math.sin(c),p=(t-l)/2,P=(e-u)/2,x=E*p+v*P,y=-v*p+E*P;let d=x*x/(h*h)+y*y/(f*f);if(d>1){const M=Math.sqrt(d);h*=M,f*=M,d=1}const g=h*h,b=f*f,T=x*x,m=y*y;let S=Math.max(0,(g*b-g*m-b*T)/(g*m+b*T));S=Math.sqrt(S),s===a&&(S=-S);const w=S*(h*y)/f,F=S*-(f*x)/h,U=E*w-v*F+(t+l)/2,_=v*w+E*F+(e+u)/2,R=Wt(1,0,(x-w)/h,(y-F)/f);let D=Wt((x-w)/h,(y-F)/f,(-x-w)/h,(-y-F)/f);!a&&D>0&&(D-=2*Math.PI),a&&D<0&&(D+=2*Math.PI);const L=Math.max(4,Math.ceil(Math.abs(D)/(Math.PI/16)));for(let M=1;M<=L;M++){const V=R+M/L*D,B=Math.cos(V),H=Math.sin(V),ot=E*h*B-v*f*H+U,Tt=v*h*B+E*f*H+_;o.push(ot,Tt)}}function Wt(o,t,e,n){const i=o*n-t*e<0?-1:1,r=o*e+t*n,s=Math.sqrt(o*o+t*t),a=Math.sqrt(e*e+n*n),l=r/(s*a);return i*Math.acos(Math.max(-1,Math.min(1,l)))}function un(o){const t=[],e=[];for(let n=0;n<o.length;n++){n>0&&e.push(t.length/2);for(const i of o[n])t.push(i)}return{flatCoords:t,holeIndices:e}}function zt(o){const t=o.length,e=o.map(i=>Math.abs(qt(i))),n=new Array(t).fill(!1);for(let i=0;i<t;i++){let r=0;const s=o[i][0],a=o[i][1];for(let l=0;l<t;l++)i!==l&&e[l]>e[i]&&Yt(s,a,o[l])&&r++;n[i]=r%2===1}return n}function cn(o){if(o.length<=1)return[o];const t=zt(o),e=o.map((s,a)=>{const l=qt(s);return{index:a,contour:s,area:l,isOuter:!t[a]}}),n=e.filter(s=>s.isOuter),i=e.filter(s=>!s.isOuter);if(n.length===0)return o.map(s=>[s]);const r=n.map(s=>({outer:s.contour,holes:[]}));for(const s of i){const a=s.contour[0],l=s.contour[1];let u=-1,h=1/0;for(let f=0;f<n.length;f++)if(Yt(a,l,n[f].contour)){const c=Math.abs(n[f].area);c<h&&(h=c,u=f)}u>=0?r[u].holes.push(s.contour):r.push({outer:s.contour,holes:[]})}return r.map(s=>[s.outer,...s.holes])}function qt(o){let t=0;const e=o.length;for(let n=0;n<e;n+=2){const i=o[n],r=o[n+1],s=o[(n+2)%e],a=o[(n+3)%e];t+=i*a-s*r}return t/2}function Yt(o,t,e){let n=!1;const i=e.length;for(let r=0,s=i-2;r<i;s=r,r+=2){const a=e[r],l=e[r+1],u=e[s],h=e[s+1];l>t!=h>t&&o<(u-a)*(t-l)/(h-l)+a&&(n=!n)}return n}function fn(o,t,e=2){const n=t&&t.length>0,i=n?t[0]*e:o.length;let r=jt(o,0,i,e,!0);const s=[];if(!r||r.next===r.prev)return s;n&&(r=vn(o,t,r,e));let a=1/0,l=1/0,u=-1/0,h=-1/0,f=0;if(o.length>80*e){for(let c=0;c<i;c+=e){const E=o[c],v=o[c+1];E<a&&(a=E),v<l&&(l=v),E>u&&(u=E),v>h&&(h=v)}f=Math.max(u-a,h-l),f=f!==0?32767/f:0}return nt(r,s,e,a,l,f,0),s}function jt(o,t,e,n,i){let r=null;if(i===Dn(o,t,e,n)>0)for(let s=t;s<e;s+=n)r=Kt(s,o[s],o[s+1],r);else for(let s=e-n;s>=t;s-=n)r=Kt(s,o[s],o[s+1],r);return r&&mt(r,r.next)&&(rt(r),r=r.next),r?(r.next.prev=r,r.prev.next=r,r.next):null}function W(o,t){t||(t=o);let e=o,n;do if(n=!1,!e.steiner&&(mt(e,e.next)||C(e.prev,e,e.next)===0)){if(rt(e),e=t=e.prev,e===e.next)break;n=!0}else e=e.next;while(n||e!==t);return t}function nt(o,t,e,n,i,r,s){if(!o)return;!s&&r&&bn(o,n,i,r);let a=o,l,u;for(;o.prev!==o.next;){if(l=o.prev,u=o.next,r?mn(o,n,i,r):dn(o)){t.push(l.i/e,o.i/e,u.i/e),rt(o),o=u.next,a=u.next;continue}if(o=u,o===a){s?s===1?(o=pn(W(o),t,e),nt(o,t,e,n,i,r,2)):s===2&&gn(o,t,e,n,i,r):nt(W(o),t,e,n,i,r,1);break}}}function dn(o){const t=o.prev,e=o,n=o.next;if(C(t,e,n)>=0)return!1;const i=t.x,r=e.x,s=n.x,a=t.y,l=e.y,u=n.y,h=i<r?i<s?i:s:r<s?r:s,f=a<l?a<u?a:u:l<u?l:u,c=i>r?i>s?i:s:r>s?r:s,E=a>l?a>u?a:u:l>u?l:u;let v=n.next;for(;v!==t;){if(v.x>=h&&v.x<=c&&v.y>=f&&v.y<=E&&$(i,a,r,l,s,u,v.x,v.y)&&C(v.prev,v,v.next)>=0)return!1;v=v.next}return!0}function mn(o,t,e,n){const i=o.prev,r=o,s=o.next;if(C(i,r,s)>=0)return!1;const a=i.x,l=r.x,u=s.x,h=i.y,f=r.y,c=s.y,E=a<l?a<u?a:u:l<u?l:u,v=h<f?h<c?h:c:f<c?f:c,p=a>l?a>u?a:u:l>u?l:u,P=h>f?h>c?h:c:f>c?f:c,x=Dt(E,v,t,e,n),y=Dt(p,P,t,e,n);let d=o.prevZ,g=o.nextZ;for(;d&&d.z>=x&&g&&g.z<=y;){if(d.x>=E&&d.x<=p&&d.y>=v&&d.y<=P&&d!==i&&d!==s&&$(a,h,l,f,u,c,d.x,d.y)&&C(d.prev,d,d.next)>=0||(d=d.prevZ,g.x>=E&&g.x<=p&&g.y>=v&&g.y<=P&&g!==i&&g!==s&&$(a,h,l,f,u,c,g.x,g.y)&&C(g.prev,g,g.next)>=0))return!1;g=g.nextZ}for(;d&&d.z>=x;){if(d.x>=E&&d.x<=p&&d.y>=v&&d.y<=P&&d!==i&&d!==s&&$(a,h,l,f,u,c,d.x,d.y)&&C(d.prev,d,d.next)>=0)return!1;d=d.prevZ}for(;g&&g.z<=y;){if(g.x>=E&&g.x<=p&&g.y>=v&&g.y<=P&&g!==i&&g!==s&&$(a,h,l,f,u,c,g.x,g.y)&&C(g.prev,g,g.next)>=0)return!1;g=g.nextZ}return!0}function pn(o,t,e){let n=o;do{const i=n.prev,r=n.next.next;!mt(i,r)&&Zt(i,n,n.next,r)&&it(i,r)&&it(r,i)&&(t.push(i.i/e,n.i/e,r.i/e),rt(n),rt(n.next),n=o=r),n=n.next}while(n!==o);return W(n)}function gn(o,t,e,n,i,r){let s=o;do{let a=s.next.next;for(;a!==s.prev;){if(s.i!==a.i&&An(s,a)){let l=$t(s,a);s=W(s,s.next),l=W(l,l.next),nt(s,t,e,n,i,r,0),nt(l,t,e,n,i,r,0);return}a=a.next}s=s.next}while(s!==o)}function vn(o,t,e,n){const i=[];for(let r=0;r<t.length;r++){const s=t[r]*n,a=r<t.length-1?t[r+1]*n:o.length,l=jt(o,s,a,n,!1);l&&(l===l.next&&(l.steiner=!0),i.push(Sn(l)))}i.sort((r,s)=>r.x-s.x);for(const r of i)e=xn(r,e);return e}function xn(o,t){const e=Tn(o,t);if(!e)return t;const n=$t(e,o);return W(n,n.next),W(e,e.next)}function Tn(o,t){let e=t;const n=o.x,i=o.y;let r=-1/0,s=null;do{if(i<=e.y&&i>=e.next.y&&e.next.y!==e.y){const f=e.x+(i-e.y)/(e.next.y-e.y)*(e.next.x-e.x);if(f<=n&&f>r&&(r=f,s=e.x<e.next.x?e:e.next,f===n))return s}e=e.next}while(e!==t);if(!s)return null;const a=s,l=s.x,u=s.y;let h=1/0;e=s;do{if(n>=e.x&&e.x>=l&&n!==e.x&&$(i<u?n:r,i,l,u,i<u?r:n,i,e.x,e.y)){const f=Math.abs(i-e.y)/(n-e.x);it(e,o)&&(f<h||f===h&&(e.x>s.x||En(s,e)))&&(s=e,h=f)}e=e.next}while(e!==a);return s}function En(o,t){return C(o.prev,o,t.prev)<0&&C(t.next,o,o.next)<0}function bn(o,t,e,n){let i=o;do i.z===0&&(i.z=Dt(i.x,i.y,t,e,n)),i.prevZ=i.prev,i.nextZ=i.next,i=i.next;while(i!==o);i.prevZ.nextZ=null,i.prevZ=null,yn(i)}function yn(o){let t=1,e;do{let n=o;o=null;let i=null;for(e=0;n;){e++;let r=n,s=0;for(let l=0;l<t&&(s++,r=r.nextZ,!!r);l++);let a=t;for(;s>0||a>0&&r;){let l;s!==0&&(a===0||!r||n.z<=r.z)?(l=n,n=n.nextZ,s--):(l=r,r=r.nextZ,a--),i?i.nextZ=l:o=l,l.prevZ=i,i=l}n=r}i.nextZ=null,t*=2}while(e>1);return o}function Dt(o,t,e,n,i){let r=(o-e)*i|0,s=(t-n)*i|0;return r=(r|r<<8)&16711935,r=(r|r<<4)&252645135,r=(r|r<<2)&858993459,r=(r|r<<1)&1431655765,s=(s|s<<8)&16711935,s=(s|s<<4)&252645135,s=(s|s<<2)&858993459,s=(s|s<<1)&1431655765,r|s<<1}function Sn(o){let t=o,e=o;do(t.x<e.x||t.x===e.x&&t.y<e.y)&&(e=t),t=t.next;while(t!==o);return e}function $(o,t,e,n,i,r,s,a){return(i-s)*(t-a)-(o-s)*(r-a)>=0&&(o-s)*(n-a)-(e-s)*(t-a)>=0&&(e-s)*(r-a)-(i-s)*(n-a)>=0}function An(o,t){return o.next.i!==t.i&&o.prev.i!==t.i&&!wn(o,t)&&(it(o,t)&&it(t,o)&&Rn(o,t)&&(C(o.prev,o,t.prev)!==0||C(o,t.prev,t)!==0)||mt(o,t)&&C(o.prev,o,o.next)>0&&C(t.prev,t,t.next)>0)}function C(o,t,e){return(t.y-o.y)*(e.x-t.x)-(t.x-o.x)*(e.y-t.y)}function mt(o,t){return o.x===t.x&&o.y===t.y}function Zt(o,t,e,n){const i=gt(C(o,t,e)),r=gt(C(o,t,n)),s=gt(C(e,n,o)),a=gt(C(e,n,t));return!!(i!==r&&s!==a||i===0&&pt(o,e,t)||r===0&&pt(o,n,t)||s===0&&pt(e,o,n)||a===0&&pt(e,t,n))}function pt(o,t,e){return t.x<=Math.max(o.x,e.x)&&t.x>=Math.min(o.x,e.x)&&t.y<=Math.max(o.y,e.y)&&t.y>=Math.min(o.y,e.y)}function gt(o){return o>0?1:o<0?-1:0}function wn(o,t){let e=o;do{if(e.i!==o.i&&e.next.i!==o.i&&e.i!==t.i&&e.next.i!==t.i&&Zt(e,e.next,o,t))return!0;e=e.next}while(e!==o);return!1}function it(o,t){return C(o.prev,o,o.next)<0?C(o,t,o.next)>=0&&C(o,o.prev,t)>=0:C(o,t,o.prev)<0||C(o,o.next,t)<0}function Rn(o,t){let e=o,n=!1;const i=(o.x+t.x)/2,r=(o.y+t.y)/2;do e.y>r!=e.next.y>r&&e.next.y!==e.y&&i<(e.next.x-e.x)*(r-e.y)/(e.next.y-e.y)+e.x&&(n=!n),e=e.next;while(e!==o);return n}function $t(o,t){const e=Pt(o.i,o.x,o.y),n=Pt(t.i,t.x,t.y),i=o.next,r=t.prev;return o.next=t,t.prev=o,e.next=i,i.prev=e,n.next=e,e.prev=n,r.next=n,n.prev=r,n}function Kt(o,t,e,n){const i=Pt(o,t,e);return n?(i.next=n.next,i.prev=n,n.next.prev=i,n.next=i):(i.prev=i,i.next=i),i}function rt(o){o.next.prev=o.prev,o.prev.next=o.next,o.prevZ&&(o.prevZ.nextZ=o.nextZ),o.nextZ&&(o.nextZ.prevZ=o.prevZ)}function Pt(o,t,e){return{i:o,x:t,y:e,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}function Dn(o,t,e,n){let i=0;for(let r=t,s=e-n;r<e;r+=n)i+=(o[s]-o[r])*(o[r+1]+o[s+1]),s=r;return i}const A={parallaxX:.4,parallaxY:.8,parallaxMax:30,overscan:.06,pomSteps:16,rimIntensity:.6,rimColor:"#ffffff",rimWidth:.025,refractionStrength:.015,chromaticStrength:.008,occlusionIntensity:.4,depthPower:.7,depthScale:1.2,depthBias:-.05,fogDensity:.15,fogColor:"#1a1a2e",colorShift:.6,brightnessBias:.05,contrastLow:.02,contrastHigh:.98,verticalReduction:.5,dofStart:.5,dofStrength:.5,bevelIntensity:.5,bevelWidth:.04,bevelDarkening:.2,bevelDesaturation:.12,bevelLightAngle:135,edgeThickness:.01,edgeSpecular:.35,edgeColor:"#a0a0a0",chamferWidth:.025,chamferAngle:45,chamferColor:"#262630",chamferAmbient:.12,chamferSpecular:.3,chamferShininess:24,edgeOcclusionWidth:.03,edgeOcclusionStrength:.2,lightDirection:"-0.5,0.7,-0.3",autoplay:!0,loop:!0,muted:!0},K=512,J=512;class Ft{constructor(t,e=.08,n=.06){this.host=t,this.lerpFactor=e,this.motionLerpFactor=n,this.host.addEventListener("mousemove",this.handleMouseMove),this.host.addEventListener("mouseleave",this.resetPointerTarget),this.host.addEventListener("touchstart",this.handleTouchStart,{passive:!0}),this.host.addEventListener("touchmove",this.handleTouchMove,{passive:!0}),this.host.addEventListener("touchend",this.handleTouchEnd,{passive:!0}),this.host.addEventListener("touchcancel",this.handleTouchEnd,{passive:!0})}pointerTarget={x:0,y:0};motionTarget={x:0,y:0};smoothedOutput={x:0,y:0};usingMotionInput=!1;motionListenerAttached=!1;motionRequested=!1;touchActive=!1;touchAnchorX=0;touchAnchorY=0;lerpFactor;motionLerpFactor;static TOUCH_DRAG_RANGE=100;update(){const t=this.touchActive?this.pointerTarget:this.usingMotionInput?this.motionTarget:this.pointerTarget,e=this.usingMotionInput&&!this.touchActive?this.motionLerpFactor:this.lerpFactor;return this.smoothedOutput.x=xt(this.smoothedOutput.x,t.x,e),this.smoothedOutput.y=xt(this.smoothedOutput.y,t.y,e),this.smoothedOutput}dispose(){this.host.removeEventListener("mousemove",this.handleMouseMove),this.host.removeEventListener("mouseleave",this.resetPointerTarget),this.host.removeEventListener("touchstart",this.handleTouchStart),this.host.removeEventListener("touchmove",this.handleTouchMove),this.host.removeEventListener("touchend",this.handleTouchEnd),this.host.removeEventListener("touchcancel",this.handleTouchEnd),this.motionListenerAttached&&(window.removeEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!1)}handleMouseMove=t=>{const e=this.host.getBoundingClientRect(),n=(t.clientX-e.left)/e.width*2-1,i=(t.clientY-e.top)/e.height*2-1;this.pointerTarget.x=Q(n,-1,1),this.pointerTarget.y=Q(i,-1,1)};resetPointerTarget=()=>{this.pointerTarget.x=0,this.pointerTarget.y=0};handleTouchStart=t=>{const e=t.touches[0];e&&(this.touchActive=!0,this.touchAnchorX=e.clientX,this.touchAnchorY=e.clientY,this.pointerTarget.x=0,this.pointerTarget.y=0,this.motionRequested||(this.motionRequested=!0,this.requestMotionPermission()))};handleTouchMove=t=>{const e=t.touches[0];if(!e)return;const n=e.clientX-this.touchAnchorX,i=e.clientY-this.touchAnchorY,r=Ft.TOUCH_DRAG_RANGE;this.pointerTarget.x=Q(n/r,-1,1),this.pointerTarget.y=Q(i/r,-1,1)};handleTouchEnd=()=>{this.touchActive=!1,this.pointerTarget.x=0,this.pointerTarget.y=0};async requestMotionPermission(){if(typeof DeviceOrientationEvent>"u")return;const t=DeviceOrientationEvent;if(typeof t.requestPermission=="function")try{if(await t.requestPermission()!=="granted")return}catch{return}this.motionListenerAttached||(window.addEventListener("deviceorientation",this.handleDeviceOrientation),this.motionListenerAttached=!0),this.usingMotionInput=!0}handleDeviceOrientation=t=>{const e=Q((t.gamma??0)/45,-1,1),n=Q((t.beta??0)/45,-1,1);this.motionTarget.x=xt(this.motionTarget.x,e,this.motionLerpFactor),this.motionTarget.y=xt(this.motionTarget.y,n,this.motionLerpFactor)}}class vt extends HTMLElement{static TAG_NAME="layershift-portal";static get observedAttributes(){return["src","depth-src","depth-meta","depth-model","logo-src","source-type","parallax-x","parallax-y","parallax-max","overscan","pom-steps","quality","gpu-backend","rim-intensity","rim-color","rim-width","refraction-strength","chromatic-strength","occlusion-intensity","depth-power","depth-scale","depth-bias","fog-density","fog-color","color-shift","brightness-bias","contrast-low","contrast-high","vertical-reduction","dof-start","dof-strength","bevel-intensity","bevel-width","bevel-darkening","bevel-desaturation","bevel-light-angle","edge-thickness","edge-specular","edge-color","chamfer-width","chamfer-angle","chamfer-color","chamfer-ambient","chamfer-specular","chamfer-shininess","edge-occlusion-width","edge-occlusion-strength","light-direction","autoplay","loop","muted"]}reinitAttributes=["src","depth-src","depth-meta","depth-model","logo-src","source-type"];canInit(){const t=!!this.getAttribute("logo-src");if(this.sourceType==="camera")return t;const e=!!this.getAttribute("src"),n=!!this.getAttribute("depth-src")&&!!this.getAttribute("depth-meta"),i=!!this.getAttribute("depth-model");return e&&t&&(n||i)}shadow;container=null;renderer=null;inputHandler=null;source=null;depthEstimator=null;mesh=null;loopCount=0;lifecycle;constructor(){super(),this.shadow=this.attachShadow({mode:"open"}),this.lifecycle=new Ht(this)}getAttrFloat(t,e){const n=this.getAttribute(t);if(n===null)return e;const i=parseFloat(n);return Number.isFinite(i)?i:e}getAttrBool(t,e){if(!this.hasAttribute(t))return e;const n=this.getAttribute(t);return!(n==="false"||n==="0")}getAttrColor(t,e){const n=this.getAttribute(t)??e;return Pn(n)}getAttrVec3(t,e){const i=(this.getAttribute(t)??e).split(",").map(s=>parseFloat(s.trim()));if(i.length>=3&&i.every(Number.isFinite))return[i[0],i[1],i[2]];const r=e.split(",").map(s=>parseFloat(s.trim()));return[r[0],r[1],r[2]]}get sourceType(){const t=this.getAttribute("source-type");return t==="camera"?"camera":t==="image"?"image":"video"}get parallaxX(){return this.getAttrFloat("parallax-x",A.parallaxX)}get parallaxY(){return this.getAttrFloat("parallax-y",A.parallaxY)}get parallaxMax(){return this.getAttrFloat("parallax-max",A.parallaxMax)}get overscan(){return this.getAttrFloat("overscan",A.overscan)}get pomSteps(){return this.getAttrFloat("pom-steps",A.pomSteps)}get quality(){const t=this.getAttribute("quality");if(t==="auto"||t==="high"||t==="medium"||t==="low")return t}get gpuBackend(){return"webgl2"}get rimIntensity(){return this.getAttrFloat("rim-intensity",A.rimIntensity)}get rimWidth(){return this.getAttrFloat("rim-width",A.rimWidth)}get rimColor(){return this.getAttrColor("rim-color",A.rimColor)}get refractionStrength(){return this.getAttrFloat("refraction-strength",A.refractionStrength)}get chromaticStrength(){return this.getAttrFloat("chromatic-strength",A.chromaticStrength)}get occlusionIntensity(){return this.getAttrFloat("occlusion-intensity",A.occlusionIntensity)}get depthPower(){return this.getAttrFloat("depth-power",A.depthPower)}get depthScale(){return this.getAttrFloat("depth-scale",A.depthScale)}get depthBias(){return this.getAttrFloat("depth-bias",A.depthBias)}get fogDensity(){return this.getAttrFloat("fog-density",A.fogDensity)}get fogColor(){return this.getAttrColor("fog-color",A.fogColor)}get colorShift(){return this.getAttrFloat("color-shift",A.colorShift)}get brightnessBias(){return this.getAttrFloat("brightness-bias",A.brightnessBias)}get contrastLow(){return this.getAttrFloat("contrast-low",A.contrastLow)}get contrastHigh(){return this.getAttrFloat("contrast-high",A.contrastHigh)}get verticalReduction(){return this.getAttrFloat("vertical-reduction",A.verticalReduction)}get dofStart(){return this.getAttrFloat("dof-start",A.dofStart)}get dofStrength(){return this.getAttrFloat("dof-strength",A.dofStrength)}get bevelIntensity(){return this.getAttrFloat("bevel-intensity",A.bevelIntensity)}get bevelWidth(){return this.getAttrFloat("bevel-width",A.bevelWidth)}get bevelDarkening(){return this.getAttrFloat("bevel-darkening",A.bevelDarkening)}get bevelDesaturation(){return this.getAttrFloat("bevel-desaturation",A.bevelDesaturation)}get bevelLightAngle(){return this.getAttrFloat("bevel-light-angle",A.bevelLightAngle)}get edgeThickness(){return this.getAttrFloat("edge-thickness",A.edgeThickness)}get edgeSpecular(){return this.getAttrFloat("edge-specular",A.edgeSpecular)}get edgeColor(){return this.getAttrColor("edge-color",A.edgeColor)}get chamferWidth(){return this.getAttrFloat("chamfer-width",A.chamferWidth)}get chamferAngle(){return this.getAttrFloat("chamfer-angle",A.chamferAngle)}get chamferColor(){return this.getAttrColor("chamfer-color",A.chamferColor)}get chamferAmbient(){return this.getAttrFloat("chamfer-ambient",A.chamferAmbient)}get chamferSpecular(){return this.getAttrFloat("chamfer-specular",A.chamferSpecular)}get chamferShininess(){return this.getAttrFloat("chamfer-shininess",A.chamferShininess)}get edgeOcclusionWidth(){return this.getAttrFloat("edge-occlusion-width",A.edgeOcclusionWidth)}get edgeOcclusionStrength(){return this.getAttrFloat("edge-occlusion-strength",A.edgeOcclusionStrength)}get lightDirection3(){return this.getAttrVec3("light-direction",A.lightDirection)}get depthModel(){return this.getAttribute("depth-model")}get shouldAutoplay(){return this.getAttrBool("autoplay",A.autoplay)}get shouldLoop(){return this.getAttrBool("loop",A.loop)}get shouldMute(){return this.getAttrBool("muted",A.muted)}emit(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}attachSourceEventListeners(t){t.addEventListener&&(t.addEventListener("play",(()=>{this.emit("layershift-portal:play",{currentTime:t.currentTime})})),t.addEventListener("pause",(()=>{this.emit("layershift-portal:pause",{currentTime:t.currentTime})})),t.addEventListener("ended",(()=>{this.loopCount+=1,this.emit("layershift-portal:loop",{loopCount:this.loopCount})})))}connectedCallback(){console.warn("[layershift] <layershift-portal> is deprecated and will be removed in a future major version. Use <layershift-effect> with a filter-config.json instead. See https://layershift.io/docs/migration"),this.lifecycle.onConnected()}disconnectedCallback(){this.lifecycle.onDisconnected()}attributeChangedCallback(t,e,n){this.lifecycle.onAttributeChanged(t,e,n)}setupShadowDOM(){this.shadow.innerHTML="";const t=document.createElement("style");t.textContent=`
724
998
  :host {
725
999
  display: block;
726
1000
  width: 100%;
@@ -740,4 +1014,4 @@ ${o}`)}return r.detachShader(i,t),r.detachShader(i,e),r.deleteShader(t),r.delete
740
1014
  width: 100%;
741
1015
  height: 100%;
742
1016
  }
743
- `,this.shadow.appendChild(t),this.container=document.createElement("div"),this.container.className="container",this.shadow.appendChild(this.container)}async doInit(t){const e=this.getAttribute("src"),i=this.getAttribute("depth-src"),o=this.getAttribute("depth-meta"),n=this.getAttribute("logo-src");if(this.container)try{const[s,h,a]=await Promise.all([this.createVideoElement(e),vt(i,o),pe(n)]);if(t.aborted){s.remove();return}this.video=s,this.mesh=a,this.loopCount=0,this.attachVideoEventListeners(s);const l=this.parallaxMax/Math.max(s.videoWidth,1);let u;try{const d=await it.create(h,h.meta.width,h.meta.height);this.depthWorker=d,u=m=>d.sample(m)}catch{const d=new gt(h,h.meta.width,h.meta.height);u=m=>d.sample(m)}if(t.aborted){s.remove(),this.depthWorker?.dispose(),this.depthWorker=null;return}const f={parallaxStrength:l,overscanPadding:this.overscan,pomSteps:this.pomSteps,rimLightIntensity:this.rimIntensity,rimLightColor:this.rimColor,rimLightWidth:this.rimWidth,refractionStrength:this.refractionStrength,chromaticStrength:this.chromaticStrength,occlusionIntensity:this.occlusionIntensity,depthPower:this.depthPower,depthScale:this.depthScale,depthBias:this.depthBias,fogDensity:this.fogDensity,fogColor:this.fogColor,colorShift:this.colorShift,brightnessBias:this.brightnessBias,contrastLow:this.contrastLow,contrastHigh:this.contrastHigh,verticalReduction:this.verticalReduction,dofStart:this.dofStart,dofStrength:this.dofStrength,bevelIntensity:this.bevelIntensity,bevelWidth:this.bevelWidth,bevelDarkening:this.bevelDarkening,bevelDesaturation:this.bevelDesaturation,bevelLightAngle:this.bevelLightAngle,edgeThickness:this.edgeThickness,edgeSpecular:this.edgeSpecular,edgeColor:this.edgeColor,chamferWidth:this.chamferWidth,chamferAngle:this.chamferAngle,chamferColor:this.chamferColor,chamferAmbient:this.chamferAmbient,chamferSpecular:this.chamferSpecular,chamferShininess:this.chamferShininess,edgeOcclusionWidth:this.edgeOcclusionWidth,edgeOcclusionStrength:this.edgeOcclusionStrength,lightDirection:this.lightDirection3};this.renderer=new ft(this.container,f),this.renderer.initialize(s,h.meta.width,h.meta.height,a),this.inputHandler=new pt(this);const c=this.parallaxX,g=this.parallaxY;if(this.renderer.start(s,u,()=>{if(!this.inputHandler)return{x:0,y:0};const d=this.inputHandler.update();return{x:d.x*c,y:d.y*g}},(d,m)=>{this.emit("layershift-portal:frame",{currentTime:d,frameNumber:m})}),this.shouldAutoplay){s.currentTime=0;try{await s.play()}catch{}}if(t.aborted)return;this.lifecycle.markInitialized(),this.emit("layershift-portal:ready",{videoWidth:s.videoWidth,videoHeight:s.videoHeight,duration:s.duration})}catch(s){const h=s instanceof Error?s.message:"Failed to initialize.";console.error("<layershift-portal>: Failed to initialize.",s),this.emit("layershift-portal:error",{message:h})}}async createVideoElement(t){const e=document.createElement("video");return e.crossOrigin="anonymous",e.setAttribute("crossorigin","anonymous"),e.playsInline=!0,e.setAttribute("playsinline",""),e.setAttribute("webkit-playsinline","true"),e.muted=this.shouldMute,e.defaultMuted=this.shouldMute,this.shouldMute&&e.setAttribute("muted",""),e.loop=this.shouldLoop,e.preload="auto",e.style.display="none",e.src=t,this.shadow.appendChild(e),await new Promise((i,o)=>{if(e.readyState>=HTMLMediaElement.HAVE_METADATA){i();return}const n=()=>{h(),i()},s=()=>{h(),o(new Error("Failed to load video metadata."))},h=()=>{e.removeEventListener("loadedmetadata",n),e.removeEventListener("error",s)};e.addEventListener("loadedmetadata",n),e.addEventListener("error",s),e.load()}),e}doDispose(){this.renderer?.dispose(),this.renderer=null,this.inputHandler?.dispose(),this.inputHandler=null,this.depthWorker?.dispose(),this.depthWorker=null,this.video&&(this.video.pause(),this.video.removeAttribute("src"),this.video.load(),this.video.remove(),this.video=null),this.mesh=null,this.loopCount=0,this.container=null}}function W(r,t,e){return Math.min(e,Math.max(t,r))}function lt(r,t,e){return r+(t-r)*e}function Ne(r){const t=r.replace("#","");if(t.length===3){const e=parseInt(t[0]+t[0],16)/255,i=parseInt(t[1]+t[1],16)/255,o=parseInt(t[2]+t[2],16)/255;return[e,i,o]}if(t.length===6){const e=parseInt(t.substring(0,2),16)/255,i=parseInt(t.substring(2,4),16)/255,o=parseInt(t.substring(4,6),16)/255;return[e,i,o]}return[0,0,0]}return customElements.get(rt.TAG_NAME)||customElements.define(rt.TAG_NAME,rt),customElements.get(ht.TAG_NAME)||customElements.define(ht.TAG_NAME,ht),et.LayershiftElement=rt,et.LayershiftPortalElement=ht,Object.defineProperty(et,Symbol.toStringTag,{value:"Module"}),et})({});
1017
+ `,this.shadow.appendChild(t),this.container=document.createElement("div"),this.container.className="container",this.shadow.appendChild(this.container)}async doInit(t){const e=this.getAttribute("logo-src");if(!this.container)return;const n=this.sourceType==="camera",i=this.depthModel;try{let r,s,a,l=null;const u=x=>{this.emit("layershift-portal:model-progress",x)};if(n){const[x,y]=await Promise.all([Bt({video:{facingMode:"user"}},{parent:this.shadow}),Rt(e)]);if(t.aborted){x.dispose();return}if(r=x,a=y,i){if(l=await ut(i,K,J,u),t.aborted){l.dispose(),r.dispose();return}s=N(K,J)}else s=N(r.width,r.height)}else{const x=this.getAttribute("src"),y=this.getAttribute("depth-src"),d=this.getAttribute("depth-meta"),g=!!y&&!!d,b=this.sourceType==="image"||/\.(jpe?g|png|webp|gif|avif|bmp)(\?|$)/i.test(x);if(g){const[T,m,S]=await Promise.all([b?ht(x):lt(x,{parent:this.shadow,loop:this.shouldLoop,muted:this.shouldMute}),Lt(y,d),Rt(e)]);if(t.aborted){T.dispose();return}r=T,s=m,a=S}else if(i){const[T,m,S]=await Promise.all([b?ht(x):lt(x,{parent:this.shadow,loop:this.shouldLoop,muted:this.shouldMute}),ut(i,K,J,u),Rt(e)]);if(t.aborted){T.dispose(),m.dispose();return}if(r=T,l=m,a=S,b||!r.isLive){const w=r.getImageSource();if(w){const F=await l.submitFrameAndWait(w);s={meta:{frameCount:1,fps:1,width:K,height:J,sourceFps:1},frames:[F]}}else s=N(K,J)}else s=N(K,J)}else throw new Error("Either depth-src/depth-meta or depth-model must be provided.")}this.source=r,this.depthEstimator=l,this.mesh=a,this.loopCount=0,this.attachSourceEventListeners(r);const h=this.container?.clientWidth||r.width,f=this.parallaxMax/Math.max(h,1);let c;if(l)c=()=>l.getLatestDepth();else{const x=new Ct(s);c=y=>x.sample(y)}const E={parallaxStrength:f,overscanPadding:this.overscan,pomSteps:this.pomSteps,quality:this.quality,rimLightIntensity:this.rimIntensity,rimLightColor:this.rimColor,rimLightWidth:this.rimWidth,refractionStrength:this.refractionStrength,chromaticStrength:this.chromaticStrength,occlusionIntensity:this.occlusionIntensity,depthPower:this.depthPower,depthScale:this.depthScale,depthBias:this.depthBias,fogDensity:this.fogDensity,fogColor:this.fogColor,colorShift:this.colorShift,brightnessBias:this.brightnessBias,contrastLow:this.contrastLow,contrastHigh:this.contrastHigh,verticalReduction:this.verticalReduction,dofStart:this.dofStart,dofStrength:this.dofStrength,bevelIntensity:this.bevelIntensity,bevelWidth:this.bevelWidth,bevelDarkening:this.bevelDarkening,bevelDesaturation:this.bevelDesaturation,bevelLightAngle:this.bevelLightAngle,edgeThickness:this.edgeThickness,edgeSpecular:this.edgeSpecular,edgeColor:this.edgeColor,chamferWidth:this.chamferWidth,chamferAngle:this.chamferAngle,chamferColor:this.chamferColor,chamferAmbient:this.chamferAmbient,chamferSpecular:this.chamferSpecular,chamferShininess:this.chamferShininess,edgeOcclusionWidth:this.edgeOcclusionWidth,edgeOcclusionStrength:this.edgeOcclusionStrength,lightDirection:this.lightDirection3};if(t.aborted)return;this.renderer=new tn(this.container,E),this.renderer.initialize(r,s.meta.width,s.meta.height,a),this.inputHandler=new Ft(this);const v=this.parallaxX,p=this.parallaxY,P=l;if(this.renderer.start(r,c,()=>{if(!this.inputHandler)return{x:0,y:0};const x=this.inputHandler.update();return{x:x.x*v,y:x.y*p}},(x,y)=>{if(P){const d=r.getImageSource();d&&P.submitFrame(d)}this.emit("layershift-portal:frame",{currentTime:x,frameNumber:y})}),!n&&r.isLive&&this.shouldAutoplay&&r.play)try{await r.play()}catch{}if(t.aborted)return;this.lifecycle.markInitialized(),this.emit("layershift-portal:ready",{videoWidth:r.width,videoHeight:r.height,duration:r.duration})}catch(r){const s=r instanceof Error?r.message:"Failed to initialize.";console.error("<layershift-portal>: Failed to initialize.",r),this.emit("layershift-portal:error",{message:s})}}doDispose(){this.renderer?.dispose(),this.renderer=null,this.inputHandler?.dispose(),this.inputHandler=null,this.depthEstimator?.dispose(),this.depthEstimator=null,this.source?.dispose(),this.source=null,this.mesh=null,this.loopCount=0,this.container=null}}function Q(o,t,e){return Math.min(e,Math.max(t,o))}function xt(o,t,e){return o+(t-o)*e}function Pn(o){const t=o.replace("#","");if(t.length===3){const e=parseInt(t[0]+t[0],16)/255,n=parseInt(t[1]+t[1],16)/255,i=parseInt(t[2]+t[2],16)/255;return[e,n,i]}if(t.length===6){const e=parseInt(t.substring(0,2),16)/255,n=parseInt(t.substring(2,4),16)/255,i=parseInt(t.substring(4,6),16)/255;return[e,n,i]}return[0,0,0]}return customElements.get(ft.TAG_NAME)||customElements.define(ft.TAG_NAME,ft),customElements.get(vt.TAG_NAME)||customElements.define(vt.TAG_NAME,vt),st.LayershiftElement=ft,st.LayershiftPortalElement=vt,Object.defineProperty(st,Symbol.toStringTag,{value:"Module"}),st})({});