fix(lab): AR Passthrough black screen — transparent canvas + immersive-ar detection + dynamic AR background
- gl.alpha:true + setClearColor(0,0) no WebGL renderer
- ARBackground detecta environmentBlendMode='additive' e vira transparente
- Tenta navigator.xr.requestSession('immersive-ar') antes do fallback immersive-vr
- local-floor required + hand-tracking/plane-detection/hit-test optional
This commit is contained in:
@@ -316,10 +316,11 @@ const PhysicsScene = ({
|
||||
// ===================================================================
|
||||
const SceneContent = ({ locked, resetSignal }: { locked: boolean; resetSignal: number }) => (
|
||||
<>
|
||||
<color attach="background" args={["#0a0a0a"]} />
|
||||
<fog attach="fog" args={["#0a0a0a", 5, 18]} />
|
||||
<PerspectiveCamera makeDefault position={[0, 1.5, 3]} fov={50} />
|
||||
<ambientLight intensity={0.4} />
|
||||
{/* Em modo AR Passthrough, NÃO pinta cor de fundo — ela veda o passthrough */}
|
||||
{/* Em modo desktop (fora do XR), pinta fundo escuro pra não ficar transparente estranho */}
|
||||
<ARBackground />
|
||||
|
||||
<ambientLight intensity={0.6} />
|
||||
<directionalLight
|
||||
position={[5, 5, 5]}
|
||||
intensity={1.2}
|
||||
@@ -327,7 +328,7 @@ const SceneContent = ({ locked, resetSignal }: { locked: boolean; resetSignal: n
|
||||
shadow-mapSize={[1024, 1024]}
|
||||
/>
|
||||
|
||||
{/* Grid estilo HLA */}
|
||||
{/* Grid estilo HLA — funciona em ambos os modos */}
|
||||
<Grid
|
||||
position={[0, 0.001, 0]}
|
||||
args={[20, 20]}
|
||||
@@ -359,6 +360,21 @@ const SceneContent = ({ locked, resetSignal }: { locked: boolean; resetSignal: n
|
||||
</>
|
||||
);
|
||||
|
||||
// ===================================================================
|
||||
// FUNDO DINÂMICO — VR imersivo fica preto; AR Passthrough fica transparente
|
||||
// ===================================================================
|
||||
const ARBackground = () => {
|
||||
const session = useXR((s) => s.session);
|
||||
const isAR = session && "environmentBlendMode" in session && (session as any).environmentBlendMode === "additive";
|
||||
if (isAR) return null; // AR = transparente (mostra passthrough do Quest 3)
|
||||
return (
|
||||
<>
|
||||
<color attach="background" args={["#0a0a0a"]} />
|
||||
<fog attach="fog" args={["#0a0a0a", 5, 18]} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// ===================================================================
|
||||
// JOYSTICK HANDLER — gamepad pro controle fora do XR
|
||||
// ===================================================================
|
||||
@@ -410,7 +426,39 @@ export default function QuestCubeLab() {
|
||||
|
||||
useEffect(() => {
|
||||
if (autoEnter) {
|
||||
store.enterXR?.().catch(() => {});
|
||||
// Meta Quest 3 suporta immersive-ar (Passthrough AR). Tenta primeiro.
|
||||
// Fallback pra immersive-vr se AR não disponível (óculos sem passthrough).
|
||||
(async () => {
|
||||
try {
|
||||
if ('xr' in navigator) {
|
||||
const xr = (navigator as any).xr;
|
||||
const supported: boolean = await xr.isSessionSupported('immersive-ar').catch(() => false);
|
||||
if (supported) {
|
||||
const sessionInit: XRSessionInit = {
|
||||
requiredFeatures: ['local-floor'],
|
||||
optionalFeatures: ['hand-tracking', 'plane-detection', 'hit-test'],
|
||||
};
|
||||
await xr.requestSession('immersive-ar', sessionInit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Fallback: usa o XR controller padrão do store
|
||||
const ctrl: any = store as any;
|
||||
const enterFn = ctrl.enterAR || ctrl.enterVR || ctrl.enterXR;
|
||||
try {
|
||||
await enterFn.call(ctrl, 'immersive-vr' as any);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
} catch {
|
||||
// qualquer erro, tenta o enter padrão
|
||||
try {
|
||||
await Promise.resolve(store.enterXR?.());
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
}, [autoEnter]);
|
||||
|
||||
@@ -473,9 +521,17 @@ export default function QuestCubeLab() {
|
||||
Rapier Physics ativo • Half-Life: Alyx-like
|
||||
</div>
|
||||
|
||||
{/* Canvas com XR */}
|
||||
{/* Canvas com XR — gl.alpha:true permite passthrough por trás */}
|
||||
<XR store={store}>
|
||||
<Canvas shadows>
|
||||
<Canvas
|
||||
shadows
|
||||
gl={{ alpha: true, antialias: true, powerPreference: "high-performance" }}
|
||||
camera={{ position: [0, 1.5, 3], fov: 50 }}
|
||||
onCreated={({ gl }) => {
|
||||
gl.setClearColor(0x000000, 0); // transparente
|
||||
gl.xr.enabled = true;
|
||||
}}
|
||||
>
|
||||
<SceneContent locked={locked} resetSignal={resetSignal} />
|
||||
</Canvas>
|
||||
</XR>
|
||||
|
||||
Reference in New Issue
Block a user