#ifndef PORTAL_PROJECTION_MASK_INCLUDED #define PORTAL_PROJECTION_MASK_INCLUDED // Global matrices set by Portal.cs via Shader.SetGlobalMatrix float4x4 _PortalMatrix; float4x4 _CameraMatrix; // URP includes (Core pulls in the right per-draw declarations for SRP). #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl" uint GetMyRenderingLayerMask() { // Returns asuint(unity_RenderingLayer.x) in URP. return GetMeshRenderingLayer(); } bool HasRenderingLayer(uint mask, uint layerIndex /* 0..31 */) { return (mask & (1u << layerIndex)) != 0u; } void PortalProjectionMask_float( float3 WorldPos, out float ClipValue //out float3 DebugColor ) { // Detect shadow pass by comparing camera positions // In shadow pass: UNITY_MATRIX_I_V contains light camera, not Main Camera float3 currentCamPos = UNITY_MATRIX_I_V._m03_m13_m23; float3 mainCamPos = float3(_CameraMatrix[0][3], _CameraMatrix[1][3], _CameraMatrix[2][3]); float distanceBetweenCams = length(currentCamPos - mainCamPos); bool isShadowPass = (distanceBetweenCams > 0.01); // Threshold for comparison if (isShadowPass) // For viewport preview { if ((UNITY_MATRIX_P[0][0] != _CameraMatrix[0][0])) // Is scene view isShadowPass = false; } // Don't clip in shadow pass if (isShadowPass) { ClipValue = 1; return; } else { // Extract camera world position from the matrix float3 camPosWorld = float3(_CameraMatrix[0][3], _CameraMatrix[1][3], _CameraMatrix[2][3]); // Transform to portal local space float3 localPixelPos = mul(_PortalMatrix, float4(WorldPos, 1)).xyz; float3 localCamPos = mul(_PortalMatrix, float4(camPosWorld, 1)).xyz; // Perspective projection of the pixel onto the portal plane (z=0) float3 rayDir = localPixelPos - localCamPos; float t = -localCamPos.z / rayDir.z; float3 projectedPos = localCamPos + rayDir * t; // Check if projected point is inside the quad frame [-0.5, 0.5] bool insideFrame = (abs(projectedPos.x) < .5 && abs(projectedPos.y) < 0.5 && t > 0); // Check if pixel is behind the portal (z > 0 in local space) bool pixelBehind = localPixelPos.z > 0; // Check if camera is behind the portal (z > 0 in local space) bool camBehind = localCamPos.z < 0; uint renderingLayer = GetMeshRenderingLayer(); //DebugColor = float3(renderingLayer & 1u, (renderingLayer >> 1) & 1u, (renderingLayer >> 2) & 1u); if (HasRenderingLayer(renderingLayer, 1u)) // Is behind portal { // Next room: visible ONLY through the frame AND behind the portal ClipValue = (insideFrame && pixelBehind && camBehind) ? 1 : 0; } else { // Active room: visible everywhere EXCEPT through the frame when behind portal ClipValue = (insideFrame && pixelBehind && camBehind) ? 0 : 1; } } } #endif