From 8f0b1c331df894961174711bd020448fde55990d Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 10 Jul 2025 14:50:55 +0200 Subject: [PATCH 001/149] wip; access depth buffer in shader --- G3DCamera.cs | 31 +++ Resources/G3DViewGeneration.shader | 306 ++++++++++++++++++++++ Resources/G3DViewGeneration.shader.meta | 9 + Scripts/G3DHDRPCustomPass.cs | 8 +- Scripts/G3DHDRPViewGenerationPass.cs | 68 +++++ Scripts/G3DHDRPViewGenerationPass.cs.meta | 11 + 6 files changed, 431 insertions(+), 2 deletions(-) create mode 100644 Resources/G3DViewGeneration.shader create mode 100644 Resources/G3DViewGeneration.shader.meta create mode 100644 Scripts/G3DHDRPViewGenerationPass.cs create mode 100644 Scripts/G3DHDRPViewGenerationPass.cs.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index a280b96..ee8b2b2 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -194,6 +194,7 @@ public class G3DCamera private Material material; #if G3D_HDRP private G3DHDRPCustomPass customPass; + private G3DHDRPViewGenerationPass viewGenerationPass; private HDAdditionalCameraData.AntialiasingMode antialiasingMode = HDAdditionalCameraData .AntialiasingMode .None; @@ -249,6 +250,8 @@ private float scaledHalfCameraWidthAtStart #endregion + public bool generateViews = true; + // TODO Handle viewport resizing/ moving #region Initialization @@ -326,6 +329,28 @@ void Start() customPassVolume.isGlobal = true; // Make the volume invisible in the inspector customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; + + // add multiview generation pass + viewGenerationPass = + customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationPass)) + as G3DHDRPViewGenerationPass; + Material viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); + RenderTexture depthMap = new RenderTexture(mainCamera.pixelWidth, mainCamera.pixelHeight, 0) + { + format = RenderTextureFormat.Depth, + depthStencilFormat = UnityEngine + .Experimental + .Rendering + .GraphicsFormat + .D32_SFloat_S8_UInt, + }; + viewGenerationMaterial.SetTexture("DepthMap", depthMap, RenderTextureSubElement.Depth); + viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; + viewGenerationPass.materialPassName = "G3DViewGeneration"; + RTHandle rtHandle = RTHandles.Alloc(depthMap); + viewGenerationPass.depthMapHandle = rtHandle; + + // add autostereo generation pass customPass = customPassVolume.AddPassOfType(typeof(G3DHDRPCustomPass)) as G3DHDRPCustomPass; customPass.fullscreenPassMaterial = material; customPass.materialPassName = "G3DFullScreen3D"; @@ -1020,6 +1045,12 @@ private bool updateCameraCountBasedOnMode() { internalCameraCount = MAX_CAMERAS; } + + // TODO check if it is a good idea to simply set this to one. what if someone wants only one view? + if (generateViews) + { + internalCameraCount = 2; + } } if (internalCameraCount != previousCameraCount) diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader new file mode 100644 index 0000000..1e4bb05 --- /dev/null +++ b/Resources/G3DViewGeneration.shader @@ -0,0 +1,306 @@ +Shader "G3D/ViewGeneration" +{ + Properties + { + DepthMap ("Texture", 2D) = "green" {} + } + SubShader + { + PackageRequirements + { + "com.unity.render-pipelines.high-definition": "unity=2021.3" + } + Tags { "RenderType"="Opaque" "RenderPipeline" = "HDRenderPipeline"} + + Pass + { + Name "G3DViewGeneration" + + ZWrite Off + ZTest Always + Blend Off + Cull Off + + HLSLPROGRAM + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + + #pragma vertex vert + #pragma fragment fragHDRP + + Texture2D DepthMap; + SamplerState samplerDepthMap; + + struct VertAttributes + { + uint vertexID : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct v2f + { + float2 uv : TEXCOORD0; + float4 screenPos : SV_POSITION; + }; + + v2f vert(VertAttributes input) + { + v2f output; + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + output.uv = GetFullScreenTriangleTexCoord(input.vertexID); + output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + + return output; + + } + + float4 fragHDRP (v2f i) : SV_Target + { + // float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) + // float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); + // int3 viewIndices = getSubPixelViewIndices(computedScreenPos); + + float2 uvCoords = i.uv; + + // // mirror the image if necessary + // if (mirror != 0) { + // uvCoords.x = 1.0 - uvCoords.x; + // } + + float4 depthMap = DepthMap.Sample(samplerDepthMap, uvCoords); + return float4(depthMap.x, depthMap.x, depthMap.x, 1.f); + } + ENDHLSL + } + } +} + + +// Shader "G3D/ViewGeneration" +// { +// HLSLINCLUDE +// #pragma target 4.5 +// #pragma only_renderers d3d11 playstation xboxone vulkan metal switch + + +// // #if defined (SHADER_API_GAMECORE) +// // #include "Packages/com.unity.render-pipelines.gamecore/ShaderLibrary/API/GameCore.hlsl" +// // #elif defined(SHADER_API_XBOXONE) +// // #include "Packages/com.unity.render-pipelines.xboxone/ShaderLibrary/API/XBoxOne.hlsl" +// // #elif defined(SHADER_API_PS4) +// // #include "Packages/com.unity.render-pipelines.ps4/ShaderLibrary/API/PSSL.hlsl" +// // #elif defined(SHADER_API_PS5) +// // #include "Packages/com.unity.render-pipelines.ps5/ShaderLibrary/API/PSSL.hlsl" +// // #elif defined(SHADER_API_D3D11) +// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/D3D11.hlsl" +// // #elif defined(SHADER_API_METAL) +// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/Metal.hlsl" +// // #elif defined(SHADER_API_VULKAN) +// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/Vulkan.hlsl" +// // #elif defined(SHADER_API_SWITCH) +// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/Switch.hlsl" +// // #elif defined(SHADER_API_GLCORE) +// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/GLCore.hlsl" +// // #elif defined(SHADER_API_GLES3) +// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/GLES3.hlsl" +// // #elif defined(SHADER_API_GLES) +// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/GLES2.hlsl" +// // #else +// // #error unsupported shader api +// // #endif + +// // #if defined(SHADER_API_D3D11) +// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/D3D11.hlsl" +// // #elif defined(SHADER_API_METAL) +// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/Metal.hlsl" +// // #elif defined(SHADER_API_VULKAN) +// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/Vulkan.hlsl" +// // #else +// // #error unsupported shader api +// // #endif + +// // #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/TextureXR.hlsl" + + +// struct v2f +// { +// float2 uv : TEXCOORD0; +// float4 screenPos : SV_POSITION; +// }; + + +// // Texture2D DepthMap; +// // SamplerState samplerDepthMap; + +// // TEXTURE2D_X(_CameraDepthTexture); +// // SAMPLER(sampler_CameraDepthTexture); + + + +// float4 frag (v2f i) : SV_Target +// { +// // float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) +// // float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); +// // int3 viewIndices = getSubPixelViewIndices(computedScreenPos); + +// float2 uvCoords = i.uv; + +// // // mirror the image if necessary +// // if (mirror != 0) { +// // uvCoords.x = 1.0 - uvCoords.x; +// // } + +// // float4 depthMap = _CameraDepthTexture.Sample(sampler_CameraDepthTexture, uvCoords); +// // float depth = LOAD_TEXTURE2D_X_LOD(_CameraDepthTexture, uvCoords, 0).r; +// return float4(1, 0, 0, 1.f); +// } +// ENDHLSL + +// // URP Shader +// SubShader +// { +// PackageRequirements +// { +// "com.unity.render-pipelines.universal": "unity=2021.3" +// } + +// Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"} +// LOD 100 +// Cull Off + +// Pass +// { +// ZTest Always +// Blend Off +// Cull Off + +// HLSLPROGRAM +// #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" + +// #pragma vertex vert +// #pragma fragment frag + +// struct VertAttributes +// { +// uint vertexID : SV_VertexID; +// UNITY_VERTEX_INPUT_INSTANCE_ID +// }; + +// v2f vert(VertAttributes input) +// { +// v2f output; +// UNITY_SETUP_INSTANCE_ID(input); +// UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); +// output.uv = GetFullScreenTriangleTexCoord(input.vertexID); +// output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + +// return output; + +// } +// ENDHLSL +// } +// } + +// // HDRP Shader +// SubShader +// { +// PackageRequirements +// { +// "com.unity.render-pipelines.high-definition": "unity=2021.3" +// } + +// Tags { "RenderType"="Opaque" "RenderPipeline" = "HDRenderPipeline"} + +// Pass +// { +// Name "G3DFullScreen3D" + +// ZTest Always +// Blend Off +// Cull Off + +// HLSLPROGRAM +// #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" +// #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + +// #pragma vertex vert +// #pragma fragment fragHDRP + +// Texture2D DepthMap; +// SamplerState samplerDepthMap; + +// struct VertAttributes +// { +// uint vertexID : SV_VertexID; +// UNITY_VERTEX_INPUT_INSTANCE_ID +// }; + +// v2f vert(VertAttributes input) +// { +// v2f output; +// UNITY_SETUP_INSTANCE_ID(input); +// UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); +// output.uv = GetFullScreenTriangleTexCoord(input.vertexID); +// output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + +// return output; + +// } + +// float4 fragHDRP (v2f i) : SV_Target +// { +// // float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) +// // float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); +// // int3 viewIndices = getSubPixelViewIndices(computedScreenPos); + +// float2 uvCoords = i.uv; + +// // // mirror the image if necessary +// // if (mirror != 0) { +// // uvCoords.x = 1.0 - uvCoords.x; +// // } + +// float4 depthMap = DepthMap.Sample(samplerDepthMap, uvCoords); +// float depth = LoadCameraDepth(uvCoords); +// return float4(depth, 0, 0, 1.f); +// } +// ENDHLSL +// } +// } + +// // Built-in Render Pipeline +// SubShader +// { +// // No culling or depth +// Cull Off ZTest Always + +// Pass +// { +// HLSLPROGRAM +// #pragma vertex vert +// #pragma fragment fragSRP + +// #include "UnityCG.cginc" + +// v2f vert(float4 vertex : POSITION, float2 uv : TEXCOORD0) +// { +// v2f o; +// o.uv = uv; +// o.screenPos = UnityObjectToClipPos(vertex); +// return o; +// } + + +// float4 fragSRP(v2f i) : SV_Target +// { +// // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) +// // The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity. +// // i.screenPos.y = viewportHeight - i.screenPos.y; +// return frag(i); +// } + +// ENDHLSL +// } +// } +// } diff --git a/Resources/G3DViewGeneration.shader.meta b/Resources/G3DViewGeneration.shader.meta new file mode 100644 index 0000000..d99dbd9 --- /dev/null +++ b/Resources/G3DViewGeneration.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 52a9e24af426f784b861b69ac96ba67a +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/G3DHDRPCustomPass.cs b/Scripts/G3DHDRPCustomPass.cs index d53e8e5..5244e13 100644 --- a/Scripts/G3DHDRPCustomPass.cs +++ b/Scripts/G3DHDRPCustomPass.cs @@ -1,16 +1,20 @@ #if G3D_HDRP +using Codice.Client.BaseCommands; +using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; internal class G3DHDRPCustomPass : FullScreenCustomPass { + public Material viewGenerationMaterial; + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } protected override void Execute(CustomPassContext ctx) { var camera = ctx.hdCamera.camera; - if (performBlit(camera)) + if (shouldPerformBlit(camera)) { CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); CoreUtils.DrawFullScreen( @@ -28,7 +32,7 @@ protected override void Execute(CustomPassContext ctx) /// /// /// - static bool performBlit(Camera camera) + static bool shouldPerformBlit(Camera camera) { if (camera.cameraType != CameraType.Game) return false; diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs new file mode 100644 index 0000000..ea83c41 --- /dev/null +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -0,0 +1,68 @@ +#if G3D_HDRP +using Codice.Client.BaseCommands; +using UnityEditor; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.HighDefinition; + +internal class G3DHDRPViewGenerationPass : FullScreenCustomPass +{ + public RTHandle depthMapHandle; + + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } + + protected override void Execute(CustomPassContext ctx) + { + var camera = ctx.hdCamera.camera; + if (shouldPerformBlit(camera)) { } + else + { + CustomPassUtils.Copy(ctx, ctx.cameraDepthBuffer, depthMapHandle); + // Blitter.BlitCameraTexture(ctx.cmd, ctx.cameraDepthBuffer, depthMapHandle); + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + + for (int i = 0; i < 1; i++) + { + CoreUtils.DrawFullScreen( + ctx.cmd, + fullscreenPassMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); + } + } + } + + /// + /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. + /// If so returns true, otherwise returns false. + /// + /// + /// + static bool shouldPerformBlit(Camera camera) + { + if (camera.cameraType != CameraType.Game) + return false; + bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); + bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; + + bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent( + out var mosaicCamera + ); + bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; + + if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) + return false; + + // skip all cameras created by the G3D Camera script + if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) + { + return false; + } + + return true; + } + + protected override void Cleanup() { } +} +#endif diff --git a/Scripts/G3DHDRPViewGenerationPass.cs.meta b/Scripts/G3DHDRPViewGenerationPass.cs.meta new file mode 100644 index 0000000..a99b181 --- /dev/null +++ b/Scripts/G3DHDRPViewGenerationPass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 13b3a6c080406d34996f8de3e92897cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From ae3ee68e65b6dcd28b6f43a15567f9a3050015d7 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 10 Jul 2025 16:38:20 +0200 Subject: [PATCH 002/149] wip; work on view generation shader --- G3DCamera.cs | 55 ++++++++++-- Resources/G3DViewGeneration.shader | 126 ++++++++++++++++++++++++++- Scripts/G3DHDRPViewGenerationPass.cs | 38 ++++++-- 3 files changed, 201 insertions(+), 18 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index ee8b2b2..26e5531 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -251,6 +251,7 @@ private float scaledHalfCameraWidthAtStart #endregion public bool generateViews = true; + private Material viewGenerationMaterial; // TODO Handle viewport resizing/ moving @@ -334,8 +335,30 @@ void Start() viewGenerationPass = customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationPass)) as G3DHDRPViewGenerationPass; - Material viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); - RenderTexture depthMap = new RenderTexture(mainCamera.pixelWidth, mainCamera.pixelHeight, 0) + viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); + RenderTexture depthMapLeft = new RenderTexture( + mainCamera.pixelWidth, + mainCamera.pixelHeight, + 0 + ) + { + format = RenderTextureFormat.Depth, + depthStencilFormat = UnityEngine + .Experimental + .Rendering + .GraphicsFormat + .D32_SFloat_S8_UInt, + }; + viewGenerationMaterial.SetTexture( + "DepthMapLeft", + depthMapLeft, + RenderTextureSubElement.Depth + ); + RenderTexture depthMapRight = new RenderTexture( + mainCamera.pixelWidth, + mainCamera.pixelHeight, + 0 + ) { format = RenderTextureFormat.Depth, depthStencilFormat = UnityEngine @@ -344,16 +367,27 @@ void Start() .GraphicsFormat .D32_SFloat_S8_UInt, }; - viewGenerationMaterial.SetTexture("DepthMap", depthMap, RenderTextureSubElement.Depth); + viewGenerationMaterial.SetTexture( + "depthMapRight", + depthMapRight, + RenderTextureSubElement.Depth + ); + viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 3); + viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 3); + viewGenerationMaterial.SetInt(Shader.PropertyToID("depth_layer_discretization"), 1024); + viewGenerationMaterial.SetFloat(Shader.PropertyToID("crop"), 0.01f); + viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; viewGenerationPass.materialPassName = "G3DViewGeneration"; - RTHandle rtHandle = RTHandles.Alloc(depthMap); - viewGenerationPass.depthMapHandle = rtHandle; + RTHandle rtHandleDepthLeft = RTHandles.Alloc(depthMapLeft); + RTHandle rtHandleDepthRight = RTHandles.Alloc(depthMapRight); + viewGenerationPass.leftDepthMapHandle = rtHandleDepthLeft; + viewGenerationPass.rightDepthMapHandle = rtHandleDepthRight; // add autostereo generation pass - customPass = customPassVolume.AddPassOfType(typeof(G3DHDRPCustomPass)) as G3DHDRPCustomPass; - customPass.fullscreenPassMaterial = material; - customPass.materialPassName = "G3DFullScreen3D"; + // customPass = customPassVolume.AddPassOfType(typeof(G3DHDRPCustomPass)) as G3DHDRPCustomPass; + // customPass.fullscreenPassMaterial = material; + // customPass.materialPassName = "G3DFullScreen3D"; antialiasingMode = mainCamera.GetComponent().antialiasing; #endif @@ -1098,6 +1132,11 @@ public void updateShaderRenderTextures() }; cameras[i].targetTexture = renderTextures[i]; material.SetTexture("texture" + i, renderTextures[i], RenderTextureSubElement.Color); + viewGenerationMaterial.SetTexture( + "texture" + i, + renderTextures[i], + RenderTextureSubElement.Color + ); } } diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index 1e4bb05..00b438f 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -31,6 +31,24 @@ Shader "G3D/ViewGeneration" Texture2D DepthMap; SamplerState samplerDepthMap; + Texture2D cameraImage; + SamplerState samplerCameraImage; + + float layer; // range 0..1 + + int depth_layer_discretization; // max layer amount -> 1024 + + float disparity; // range 0..2 + float crop; // range 0..1 percentage of image to crop from each side + float normalization_min = 0; // scene min depth value + float normalization_max = 1; // scene max depth value + + int debug_mode; + int grid_size_x; + int grid_size_y; + + float focus_plane = 0.5; // range 0..1 + struct VertAttributes { uint vertexID : SV_VertexID; @@ -55,21 +73,123 @@ Shader "G3D/ViewGeneration" } + float linearNormalize(float value, float minV, float maxV) { + return clamp((value - minV) / (maxV - minV), 0.0, 1.0); + } + float4 fragHDRP (v2f i) : SV_Target { // float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) // float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); // int3 viewIndices = getSubPixelViewIndices(computedScreenPos); - float2 uvCoords = i.uv; + float2 texCoord = i.uv; // // mirror the image if necessary // if (mirror != 0) { - // uvCoords.x = 1.0 - uvCoords.x; + // texCoord.x = 1.0 - texCoord.x; // } - float4 depthMap = DepthMap.Sample(samplerDepthMap, uvCoords); + float4 depthMap = DepthMap.Sample(samplerDepthMap, texCoord); return float4(depthMap.x, depthMap.x, depthMap.x, 1.f); + + + //-------------------------------------------------- + //-------------------------------------------------- + //-------------------------------------------------- + //-------------------------------------------------- + float4 outputColor = float4(0.0, 0.0, 0.0, 1.0); + + float focusPlane_ = focus_plane; + // float2 gridSize = float2(3,3); + float2 gridSize = float2(grid_size_x, grid_size_y); + int gridCount = (gridSize.x * gridSize.y); + float maxDisparity = disparity; + float disparityStep = + maxDisparity / gridCount * (1.0); // flip (1.0) to invert view order + + float2 texCoordTransformed = float2(texCoord.x, 1.0 - texCoord.y); + texCoordTransformed = float2(texCoordTransformed.x * gridSize.x, + texCoordTransformed.y * gridSize.y); + + int viewIndex = + int(texCoordTransformed.x) + gridSize.x * int(texCoordTransformed.y); + + texCoordTransformed.x = + texCoordTransformed.x - float(int(texCoordTransformed.x)); + texCoordTransformed.y = + texCoordTransformed.y - float(int(texCoordTransformed.y)); + + // Crop image to avoid image artefacts at border + texCoordTransformed *= (1.0 - crop); + texCoordTransformed += crop * 0.5; + + if (gridCount % 2 == 1 && viewIndex == gridCount / 2) { // CENTER VIEW + + outputColor = cameraImage.Sample(samplerCameraImage, texCoordTransformed); + + outputColor.a = 0.0; + return outputColor; + } + + // Disparity calculation A: + // linear shift + // + float dynamicViewOffset = + (float(viewIndex) - float(gridCount / 2) + 0.5) * disparityStep; + + float focusPlaneDistance = (focusPlane_ - (1.0 - layer)); + + float2 texCoordShifted = texCoordTransformed; + texCoordShifted.x += focusPlaneDistance * dynamicViewOffset * 0.1; + + float texDepthOriginal = DepthMap.Sample(samplerDepthMap, texCoordTransformed).r; + texDepthOriginal = + linearNormalize(texDepthOriginal, normalization_min, normalization_max); + + float texDepth = DepthMap.Sample(samplerDepthMap, texCoordShifted).r; + texDepth = linearNormalize(texDepth, normalization_min, normalization_max); + // layer 0 = back + // layer 1 = front + + // texDepth 0 = back + + if ((layer - texDepth) > (1.0 / float(depth_layer_discretization))) { + // from back to front + // discard fragments that are in front of current render + // layer + discard; + } + + float hole_marker = 0.0; + // FragColor.b >0.5 -> hole + if ((layer - texDepth) < -(1.0 / float(depth_layer_discretization))) { + // from back to front + // discard fragments that are in front of current render + // layer + hole_marker = 1.0; + // discard; + } + + // float dx = linearNormalize(sobel_x(texCoordShifted), normalization_min, + // normalization_max); + // float dxy = linearNormalize(sobel(texCoordShifted), normalization_min, + // normalization_max); + + outputColor = cameraImage.Sample(samplerCameraImage, texCoordShifted); + + if (debug_mode == 4) { // DEBUG OUTPUT + outputColor.r = (1.0 - texDepth); // 0.0=front | 1.0=back + outputColor.g = 0.0; + outputColor.b = 0.0; + outputColor.a = 1.0; + return outputColor; + } + + + outputColor = clamp(outputColor, 0.0, 1.0); + // outputColor.a = (dxy + dx) * (0.5); // TODO: set this bias as parameter + return outputColor; } ENDHLSL } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index ea83c41..752531e 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -7,22 +7,26 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass { - public RTHandle depthMapHandle; + public RTHandle leftDepthMapHandle; + public RTHandle rightDepthMapHandle; + + public RTHandle leftColorMapHandle; + public RTHandle rightColorMapHandle; protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } protected override void Execute(CustomPassContext ctx) { var camera = ctx.hdCamera.camera; - if (shouldPerformBlit(camera)) { } - else + if (isMainG3DCamera(camera)) { - CustomPassUtils.Copy(ctx, ctx.cameraDepthBuffer, depthMapHandle); - // Blitter.BlitCameraTexture(ctx.cmd, ctx.cameraDepthBuffer, depthMapHandle); CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - for (int i = 0; i < 1; i++) + for (int i = 0; i < 1024; i++) { + float layer = i / 1024.0f; + ctx.propertyBlock.SetFloat(Shader.PropertyToID("layer"), layer); + CoreUtils.DrawFullScreen( ctx.cmd, fullscreenPassMaterial, @@ -30,16 +34,36 @@ protected override void Execute(CustomPassContext ctx) shaderPassId: 0 ); } + + for (int i = 0; i < 1; i++) { } + } + else if (isLeftCamera(camera)) + { + CustomPassUtils.Copy(ctx, ctx.cameraDepthBuffer, leftDepthMapHandle); + } + else if (isRightCamera(camera)) + { + CustomPassUtils.Copy(ctx, ctx.cameraDepthBuffer, rightDepthMapHandle); } } + bool isLeftCamera(Camera camera) + { + return camera.name == "g3dcam_1"; + } + + bool isRightCamera(Camera camera) + { + return camera.name == "g3dcam_0"; + } + /// /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. /// If so returns true, otherwise returns false. /// /// /// - static bool shouldPerformBlit(Camera camera) + static bool isMainG3DCamera(Camera camera) { if (camera.cameraType != CameraType.Game) return false; From 831f4a775f9bcfa5cddff6e46a1d045cafd90b50 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 11 Jul 2025 13:26:43 +0200 Subject: [PATCH 003/149] first working version of vomplete view generation pass (views generated with wrong offset and shift, etc.) --- G3DCamera.cs | 42 +++++++++++++---- Resources/G3DViewGeneration.shader | 70 ++++++++++++++++------------ Scripts/G3DHDRPCustomPass.cs | 2 - Scripts/G3DHDRPViewGenerationPass.cs | 65 +++++++++++++++++++++++++- 4 files changed, 136 insertions(+), 43 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 26e5531..8657e3e 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -193,7 +193,7 @@ public class G3DCamera private Material material; #if G3D_HDRP - private G3DHDRPCustomPass customPass; + private G3DHDRPViewGenerationMosaicPass customPass; private G3DHDRPViewGenerationPass viewGenerationPass; private HDAdditionalCameraData.AntialiasingMode antialiasingMode = HDAdditionalCameraData .AntialiasingMode @@ -350,7 +350,7 @@ void Start() .D32_SFloat_S8_UInt, }; viewGenerationMaterial.SetTexture( - "DepthMapLeft", + "_depthMapLeft", depthMapLeft, RenderTextureSubElement.Depth ); @@ -368,7 +368,7 @@ void Start() .D32_SFloat_S8_UInt, }; viewGenerationMaterial.SetTexture( - "depthMapRight", + "_depthMapRight", depthMapRight, RenderTextureSubElement.Depth ); @@ -376,6 +376,10 @@ void Start() viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 3); viewGenerationMaterial.SetInt(Shader.PropertyToID("depth_layer_discretization"), 1024); viewGenerationMaterial.SetFloat(Shader.PropertyToID("crop"), 0.01f); + viewGenerationMaterial.SetFloat(Shader.PropertyToID("disparity"), 1.9f); + viewGenerationMaterial.SetFloat(Shader.PropertyToID("focus_plane"), 0.1f); + viewGenerationMaterial.SetFloat(Shader.PropertyToID("normalization_min"), 0.0f); + viewGenerationMaterial.SetFloat(Shader.PropertyToID("normalization_max"), 10.0f); viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; viewGenerationPass.materialPassName = "G3DViewGeneration"; @@ -385,9 +389,23 @@ void Start() viewGenerationPass.rightDepthMapHandle = rtHandleDepthRight; // add autostereo generation pass - // customPass = customPassVolume.AddPassOfType(typeof(G3DHDRPCustomPass)) as G3DHDRPCustomPass; - // customPass.fullscreenPassMaterial = material; - // customPass.materialPassName = "G3DFullScreen3D"; + customPass = + customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) + as G3DHDRPViewGenerationMosaicPass; + RenderTexture mosaicTexture = new RenderTexture( + mainCamera.pixelWidth, + mainCamera.pixelHeight, + 0 + ) + { + format = RenderTextureFormat.ARGB32, + depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, + }; + RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); + material.SetTexture("mosaictexture", mosaicTexture, RenderTextureSubElement.Color); + customPass.fullscreenPassMaterial = material; + customPass.materialPassName = "G3DFullScreen3D"; + customPass.mosaicImageHandle = rtHandleMosaic; antialiasingMode = mainCamera.GetComponent().antialiasing; #endif @@ -791,7 +809,14 @@ private void reinitializeShader() { if (mode == G3DCameraMode.MULTIVIEW) { - material = new Material(Shader.Find("G3D/AutostereoMultiview")); + if (generateViews) + { + material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); + } + else + { + material = new Material(Shader.Find("G3D/AutostereoMultiview")); + } } else { @@ -957,8 +982,7 @@ private void updateShaderParameters() void updateCameras() { - Vector3 targetPosition = new Vector3(0, 0, -scaledFocusDistance); - ; // position for the camera center (base position from which all other cameras are offset) + Vector3 targetPosition = new Vector3(0, 0, -scaledFocusDistance); // position for the camera center (base position from which all other cameras are offset) float targetViewSeparation = 0.0f; // calculate the camera center position and eye separation if head tracking and the diorama effect are enabled diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index 00b438f..e3fc708 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -28,17 +28,28 @@ Shader "G3D/ViewGeneration" #pragma vertex vert #pragma fragment fragHDRP - Texture2D DepthMap; - SamplerState samplerDepthMap; + Texture2D _depthMapRight; + SamplerState sampler_depthMapRight; + + + Texture2D _depthMapLeft; + SamplerState sampler_depthMapLeft; + + Texture2D cameraImage; SamplerState samplerCameraImage; + + Texture2D texture0; + SamplerState samplertexture0; + Texture2D texture1; + SamplerState samplertexture1; float layer; // range 0..1 int depth_layer_discretization; // max layer amount -> 1024 - float disparity; // range 0..2 + float disparity = 1.5f; // range 0..2 float crop; // range 0..1 percentage of image to crop from each side float normalization_min = 0; // scene min depth value float normalization_max = 1; // scene max depth value @@ -47,7 +58,7 @@ Shader "G3D/ViewGeneration" int grid_size_x; int grid_size_y; - float focus_plane = 0.5; // range 0..1 + float focus_plane = 0.5f; // range 0..1 struct VertAttributes { @@ -74,7 +85,7 @@ Shader "G3D/ViewGeneration" } float linearNormalize(float value, float minV, float maxV) { - return clamp((value - minV) / (maxV - minV), 0.0, 1.0); + return clamp((value - minV) / (maxV - minV), 0.0f, 1.0f); } float4 fragHDRP (v2f i) : SV_Target @@ -84,31 +95,30 @@ Shader "G3D/ViewGeneration" // int3 viewIndices = getSubPixelViewIndices(computedScreenPos); float2 texCoord = i.uv; + texCoord.y = 1.0f - texCoord.y; // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) // // mirror the image if necessary // if (mirror != 0) { // texCoord.x = 1.0 - texCoord.x; // } - float4 depthMap = DepthMap.Sample(samplerDepthMap, texCoord); - return float4(depthMap.x, depthMap.x, depthMap.x, 1.f); //-------------------------------------------------- //-------------------------------------------------- //-------------------------------------------------- //-------------------------------------------------- - float4 outputColor = float4(0.0, 0.0, 0.0, 1.0); + float4 outputColor = float4(0.0f, 0.0f, 0.0f, 1.0f); + float focusPlane_ = focus_plane; // float2 gridSize = float2(3,3); float2 gridSize = float2(grid_size_x, grid_size_y); int gridCount = (gridSize.x * gridSize.y); float maxDisparity = disparity; - float disparityStep = - maxDisparity / gridCount * (1.0); // flip (1.0) to invert view order + float disparityStep = maxDisparity / gridCount * 1.0f; // flip (1.0) to invert view order - float2 texCoordTransformed = float2(texCoord.x, 1.0 - texCoord.y); + float2 texCoordTransformed = float2(texCoord.x, 1.0f - texCoord.y); texCoordTransformed = float2(texCoordTransformed.x * gridSize.x, texCoordTransformed.y * gridSize.y); @@ -121,14 +131,14 @@ Shader "G3D/ViewGeneration" texCoordTransformed.y - float(int(texCoordTransformed.y)); // Crop image to avoid image artefacts at border - texCoordTransformed *= (1.0 - crop); - texCoordTransformed += crop * 0.5; + texCoordTransformed *= (1.0f - crop); + texCoordTransformed += crop * 0.5f; if (gridCount % 2 == 1 && viewIndex == gridCount / 2) { // CENTER VIEW - outputColor = cameraImage.Sample(samplerCameraImage, texCoordTransformed); + outputColor = texture0.Sample(samplertexture0, texCoordTransformed); - outputColor.a = 0.0; + outputColor.a = 0.0f; return outputColor; } @@ -136,38 +146,38 @@ Shader "G3D/ViewGeneration" // linear shift // float dynamicViewOffset = - (float(viewIndex) - float(gridCount / 2) + 0.5) * disparityStep; + (float(viewIndex) - float(gridCount / 2) + 0.5f) * disparityStep; - float focusPlaneDistance = (focusPlane_ - (1.0 - layer)); + float focusPlaneDistance = (focusPlane_ - (1.0f - layer)); float2 texCoordShifted = texCoordTransformed; - texCoordShifted.x += focusPlaneDistance * dynamicViewOffset * 0.1; + texCoordShifted.x += focusPlaneDistance * dynamicViewOffset * 0.1f; - float texDepthOriginal = DepthMap.Sample(samplerDepthMap, texCoordTransformed).r; + float texDepthOriginal = _depthMapLeft.Sample(sampler_depthMapLeft, texCoordTransformed).r; texDepthOriginal = linearNormalize(texDepthOriginal, normalization_min, normalization_max); - float texDepth = DepthMap.Sample(samplerDepthMap, texCoordShifted).r; + float texDepth = _depthMapLeft.Sample(sampler_depthMapLeft, texCoordShifted).r; texDepth = linearNormalize(texDepth, normalization_min, normalization_max); // layer 0 = back // layer 1 = front // texDepth 0 = back - if ((layer - texDepth) > (1.0 / float(depth_layer_discretization))) { + if ((layer - texDepth) > (1.0f / float(depth_layer_discretization))) { // from back to front // discard fragments that are in front of current render // layer discard; } - float hole_marker = 0.0; + float hole_marker = 0.0f; // FragColor.b >0.5 -> hole - if ((layer - texDepth) < -(1.0 / float(depth_layer_discretization))) { + if ((layer - texDepth) < -(1.0f / float(depth_layer_discretization))) { // from back to front // discard fragments that are in front of current render // layer - hole_marker = 1.0; + hole_marker = 1.0f; // discard; } @@ -176,18 +186,18 @@ Shader "G3D/ViewGeneration" // float dxy = linearNormalize(sobel(texCoordShifted), normalization_min, // normalization_max); - outputColor = cameraImage.Sample(samplerCameraImage, texCoordShifted); + outputColor = texture0.Sample(samplertexture0, texCoordShifted); if (debug_mode == 4) { // DEBUG OUTPUT - outputColor.r = (1.0 - texDepth); // 0.0=front | 1.0=back - outputColor.g = 0.0; - outputColor.b = 0.0; - outputColor.a = 1.0; + outputColor.r = (1.0f - texDepth); // 0.0=front | 1.0=back + outputColor.g = 0.0f; + outputColor.b = 0.0f; + outputColor.a = 1.0f; return outputColor; } - outputColor = clamp(outputColor, 0.0, 1.0); + outputColor = clamp(outputColor, 0.0f, 1.0f); // outputColor.a = (dxy + dx) * (0.5); // TODO: set this bias as parameter return outputColor; } diff --git a/Scripts/G3DHDRPCustomPass.cs b/Scripts/G3DHDRPCustomPass.cs index 5244e13..c9178de 100644 --- a/Scripts/G3DHDRPCustomPass.cs +++ b/Scripts/G3DHDRPCustomPass.cs @@ -7,8 +7,6 @@ internal class G3DHDRPCustomPass : FullScreenCustomPass { - public Material viewGenerationMaterial; - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } protected override void Execute(CustomPassContext ctx) diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 752531e..bbe4808 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -22,10 +22,10 @@ protected override void Execute(CustomPassContext ctx) { CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - for (int i = 0; i < 1024; i++) + for (int i = 0; i < 10; i++) { float layer = i / 1024.0f; - ctx.propertyBlock.SetFloat(Shader.PropertyToID("layer"), layer); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("layer"), i); CoreUtils.DrawFullScreen( ctx.cmd, @@ -89,4 +89,65 @@ out var mosaicCamera protected override void Cleanup() { } } + +internal class G3DHDRPViewGenerationMosaicPass : FullScreenCustomPass +{ + public RTHandle mosaicImageHandle; + + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } + + protected override void Execute(CustomPassContext ctx) + { + var camera = ctx.hdCamera.camera; + if (shouldPerformBlit(camera)) + { + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + CustomPassUtils.Copy(ctx, ctx.cameraColorBuffer, mosaicImageHandle); + + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_rows"), 3); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_columns"), 3); + CoreUtils.DrawFullScreen( + ctx.cmd, + fullscreenPassMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); + } + } + + /// + /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. + /// If so returns true, otherwise returns false. + /// + /// + /// + static bool shouldPerformBlit(Camera camera) + { + if (camera.cameraType != CameraType.Game) + return false; + bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); + bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; + + bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent( + out var mosaicCamera + ); + bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; + + if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) + return false; + + // skip all cameras created by the G3D Camera script + if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) + { + return false; + } + + return true; + } + + protected override void Cleanup() { } +} + + #endif From d6e26e3da9222f0179ae92ea2b9befd86078d8f3 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 11 Jul 2025 17:04:18 +0200 Subject: [PATCH 004/149] wip adapt shader to real world unity coordinates --- G3DCamera.cs | 56 ++-- Resources/G3DViewGeneration.shader | 372 ++++++--------------------- Scripts/G3DHDRPViewGenerationPass.cs | 16 +- 3 files changed, 124 insertions(+), 320 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 8657e3e..7bfaf3f 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -372,14 +372,20 @@ void Start() depthMapRight, RenderTextureSubElement.Depth ); - viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 3); - viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 3); + viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); + viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); viewGenerationMaterial.SetInt(Shader.PropertyToID("depth_layer_discretization"), 1024); viewGenerationMaterial.SetFloat(Shader.PropertyToID("crop"), 0.01f); - viewGenerationMaterial.SetFloat(Shader.PropertyToID("disparity"), 1.9f); + viewGenerationMaterial.SetFloat(Shader.PropertyToID("maxDisparity"), 1.9f); viewGenerationMaterial.SetFloat(Shader.PropertyToID("focus_plane"), 0.1f); - viewGenerationMaterial.SetFloat(Shader.PropertyToID("normalization_min"), 0.0f); - viewGenerationMaterial.SetFloat(Shader.PropertyToID("normalization_max"), 10.0f); + viewGenerationMaterial.SetFloat( + Shader.PropertyToID("normalization_min"), + mainCamera.nearClipPlane + ); + viewGenerationMaterial.SetFloat( + Shader.PropertyToID("normalization_max"), + mainCamera.farClipPlane + ); viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; viewGenerationPass.materialPassName = "G3DViewGeneration"; @@ -389,23 +395,23 @@ void Start() viewGenerationPass.rightDepthMapHandle = rtHandleDepthRight; // add autostereo generation pass - customPass = - customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) - as G3DHDRPViewGenerationMosaicPass; - RenderTexture mosaicTexture = new RenderTexture( - mainCamera.pixelWidth, - mainCamera.pixelHeight, - 0 - ) - { - format = RenderTextureFormat.ARGB32, - depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, - }; - RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); - material.SetTexture("mosaictexture", mosaicTexture, RenderTextureSubElement.Color); - customPass.fullscreenPassMaterial = material; - customPass.materialPassName = "G3DFullScreen3D"; - customPass.mosaicImageHandle = rtHandleMosaic; + // customPass = + // customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) + // as G3DHDRPViewGenerationMosaicPass; + // RenderTexture mosaicTexture = new RenderTexture( + // mainCamera.pixelWidth, + // mainCamera.pixelHeight, + // 0 + // ) + // { + // format = RenderTextureFormat.ARGB32, + // depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, + // }; + // RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); + // material.SetTexture("mosaictexture", mosaicTexture, RenderTextureSubElement.Color); + // customPass.fullscreenPassMaterial = material; + // customPass.materialPassName = "G3DFullScreen3D"; + // customPass.mosaicImageHandle = rtHandleMosaic; antialiasingMode = mainCamera.GetComponent().antialiasing; #endif @@ -1036,6 +1042,8 @@ void updateCameras() -currentFocusDistance ); + viewGenerationMaterial?.SetFloat(Shader.PropertyToID("focus_plane"), currentFocusDistance); + //calculate camera positions and matrices for (int i = 0; i < internalCameraCount; i++) { @@ -1540,7 +1548,9 @@ int tmpCameraCount currentView += 1; } - float offset = currentView * targetEyeSeparation; + // TODO remove the * 8 multiplier + // it is used to scale the offset to the ends for a mosaic texture where the middle textures are missing + float offset = currentView * 8 * targetEyeSeparation; // when the camera count is even, one camera is placed half the eye separation to the right of the center // same for the other to the left diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index e3fc708..601ee0c 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -35,10 +35,7 @@ Shader "G3D/ViewGeneration" Texture2D _depthMapLeft; SamplerState sampler_depthMapLeft; - - - Texture2D cameraImage; - SamplerState samplerCameraImage; + int useLeftCamera = 1; // 0 = right, 1 = left Texture2D texture0; SamplerState samplertexture0; @@ -49,16 +46,18 @@ Shader "G3D/ViewGeneration" int depth_layer_discretization; // max layer amount -> 1024 - float disparity = 1.5f; // range 0..2 + float layerDistance = 1.0f; // distance between layers in world space + + float maxDisparity = 1.5f; // range 0..2 disparity between left and right camera float crop; // range 0..1 percentage of image to crop from each side - float normalization_min = 0; // scene min depth value - float normalization_max = 1; // scene max depth value + float normalization_min = 0; // scene min depth value -> set to nearPlane + float normalization_max = 1; // scene max depth value -> set to farPlane int debug_mode; int grid_size_x; int grid_size_y; - float focus_plane = 0.5f; // range 0..1 + float focus_plane = 0.5f; // range near to far plane struct VertAttributes { @@ -87,84 +86,102 @@ Shader "G3D/ViewGeneration" float linearNormalize(float value, float minV, float maxV) { return clamp((value - minV) / (maxV - minV), 0.0f, 1.0f); } + + // convert range back from 0..1 to minV..maxV + // normalized device coordinates -> pixel coords + depth + // normalized device coordinates times inverse projecion matrix + float4 sampleDepth(float2 texCoords) { + if(useLeftCamera == 1) { + return _depthMapLeft.Sample(sampler_depthMapLeft, texCoords); + } else { + return _depthMapRight.Sample(sampler_depthMapRight, texCoords); + } + } + float4 sampleTexture(float2 texCoords) { + if(useLeftCamera == 1) { + return texture0.Sample(samplertexture0, texCoords); + } else { + return texture1.Sample(samplertexture1, texCoords); + } + } + + /// + /// creates a grid of views with a given grid size. + /// each view is offset by a given disparity value. + /// the upper left corner is the left most camera. + /// the lower right corner is the right most camera. + /// float4 fragHDRP (v2f i) : SV_Target { - // float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) - // float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); - // int3 viewIndices = getSubPixelViewIndices(computedScreenPos); - float2 texCoord = i.uv; - texCoord.y = 1.0f - texCoord.y; // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) - // // mirror the image if necessary - // if (mirror != 0) { - // texCoord.x = 1.0 - texCoord.x; - // } - - - - //-------------------------------------------------- - //-------------------------------------------------- - //-------------------------------------------------- - //-------------------------------------------------- float4 outputColor = float4(0.0f, 0.0f, 0.0f, 1.0f); - float focusPlane_ = focus_plane; - // float2 gridSize = float2(3,3); - float2 gridSize = float2(grid_size_x, grid_size_y); - int gridCount = (gridSize.x * gridSize.y); - float maxDisparity = disparity; + int gridCount = grid_size_x * grid_size_y; float disparityStep = maxDisparity / gridCount * 1.0f; // flip (1.0) to invert view order - - float2 texCoordTransformed = float2(texCoord.x, 1.0f - texCoord.y); - texCoordTransformed = float2(texCoordTransformed.x * gridSize.x, - texCoordTransformed.y * gridSize.y); - - int viewIndex = - int(texCoordTransformed.x) + gridSize.x * int(texCoordTransformed.y); - - texCoordTransformed.x = - texCoordTransformed.x - float(int(texCoordTransformed.x)); - texCoordTransformed.y = - texCoordTransformed.y - float(int(texCoordTransformed.y)); + + + // the text coords of the original left and right view are from 0 - 1 + // the tex coords of the texel we are currently rendering are also from 0 - 1 + // but we want to create a grid of views, so we need to transform the tex coords + // to the grid size. + // basically we want to figure out in which grid cell the current texel is, then convert the texel coords to the grid cell coords. + // example assuming a grid size of 3x3: + // original tex coords: 0.8, 0.5 + // step 1: transform the tex coords to the grid size by multiplying with grid size + // -> e.g. original x coord 0.8 turns to 0.8 * 3 = 2.4 + // step 2: figure out the grid cell by taking the integer part of the transformed tex coords + // -> e.g. 2.4 turns to 2 + // step 3: subtract the integer part from the transformed tex coords to get the texel coords in the grid cell + // -> e.g. 2.4 - 2 = 0.4 -> final texel coords in the grid cell are 0.4, 0.5 + float2 cellCoordinates = float2(texCoord.x, texCoord.y); + cellCoordinates = float2(cellCoordinates.x * grid_size_x, cellCoordinates.y * grid_size_y); + + int viewIndex = int(cellCoordinates.x) + grid_size_x * int(cellCoordinates.y); + + // texcoords in this texcels coordinate system (from 0 - 1) + float2 actualTexCoords = float2(cellCoordinates.x - float(int(cellCoordinates.x)), cellCoordinates.y - float(int(cellCoordinates.y))); // Crop image to avoid image artefacts at border - texCoordTransformed *= (1.0f - crop); - texCoordTransformed += crop * 0.5f; - + // actualTexCoords *= (1.0f - crop); + // actualTexCoords += crop * 0.5f; + + + // first and last image in the grid are the left and right camera if (gridCount % 2 == 1 && viewIndex == gridCount / 2) { // CENTER VIEW - outputColor = texture0.Sample(samplertexture0, texCoordTransformed); + outputColor = sampleTexture(actualTexCoords); outputColor.a = 0.0f; return outputColor; } + + // disparity has to be calculated based on the formula in https://medium.com/analytics-vidhya/distance-estimation-cf2f2fd709d8 + // Z is the depth we get from the depthmap + // D is the disparity we want to calculate + // D = ((f/d) * T) / Z // Disparity calculation A: // linear shift // - float dynamicViewOffset = - (float(viewIndex) - float(gridCount / 2) + 0.5f) * disparityStep; - - float focusPlaneDistance = (focusPlane_ - (1.0f - layer)); + float dynamicViewOffset = viewIndex * disparityStep; + + // distance to the focus plane + float focusPlaneDistance = (focus_plane - (normalization_max - layer)); - float2 texCoordShifted = texCoordTransformed; + float2 texCoordShifted = actualTexCoords; texCoordShifted.x += focusPlaneDistance * dynamicViewOffset * 0.1f; - float texDepthOriginal = _depthMapLeft.Sample(sampler_depthMapLeft, texCoordTransformed).r; - texDepthOriginal = - linearNormalize(texDepthOriginal, normalization_min, normalization_max); - - float texDepth = _depthMapLeft.Sample(sampler_depthMapLeft, texCoordShifted).r; - texDepth = linearNormalize(texDepth, normalization_min, normalization_max); + float texDepth = sampleDepth(texCoordShifted).r; // layer 0 = back // layer 1 = front // texDepth 0 = back - - if ((layer - texDepth) > (1.0f / float(depth_layer_discretization))) { + + // discard fragments with a depth smaller than the depth of the layer we are currently rendering + if ((layer - texDepth) > layerDistance) { // from back to front // discard fragments that are in front of current render // layer @@ -173,12 +190,12 @@ Shader "G3D/ViewGeneration" float hole_marker = 0.0f; // FragColor.b >0.5 -> hole - if ((layer - texDepth) < -(1.0f / float(depth_layer_discretization))) { + if ((layer - texDepth) < -layerDistance) { // from back to front // discard fragments that are in front of current render // layer - hole_marker = 1.0f; - // discard; + hole_marker = 1.0f; + discard; } // float dx = linearNormalize(sobel_x(texCoordShifted), normalization_min, @@ -186,7 +203,7 @@ Shader "G3D/ViewGeneration" // float dxy = linearNormalize(sobel(texCoordShifted), normalization_min, // normalization_max); - outputColor = texture0.Sample(samplertexture0, texCoordShifted); + outputColor = sampleTexture(texCoordShifted); if (debug_mode == 4) { // DEBUG OUTPUT outputColor.r = (1.0f - texDepth); // 0.0=front | 1.0=back @@ -204,233 +221,4 @@ Shader "G3D/ViewGeneration" ENDHLSL } } -} - - -// Shader "G3D/ViewGeneration" -// { -// HLSLINCLUDE -// #pragma target 4.5 -// #pragma only_renderers d3d11 playstation xboxone vulkan metal switch - - -// // #if defined (SHADER_API_GAMECORE) -// // #include "Packages/com.unity.render-pipelines.gamecore/ShaderLibrary/API/GameCore.hlsl" -// // #elif defined(SHADER_API_XBOXONE) -// // #include "Packages/com.unity.render-pipelines.xboxone/ShaderLibrary/API/XBoxOne.hlsl" -// // #elif defined(SHADER_API_PS4) -// // #include "Packages/com.unity.render-pipelines.ps4/ShaderLibrary/API/PSSL.hlsl" -// // #elif defined(SHADER_API_PS5) -// // #include "Packages/com.unity.render-pipelines.ps5/ShaderLibrary/API/PSSL.hlsl" -// // #elif defined(SHADER_API_D3D11) -// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/D3D11.hlsl" -// // #elif defined(SHADER_API_METAL) -// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/Metal.hlsl" -// // #elif defined(SHADER_API_VULKAN) -// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/Vulkan.hlsl" -// // #elif defined(SHADER_API_SWITCH) -// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/Switch.hlsl" -// // #elif defined(SHADER_API_GLCORE) -// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/GLCore.hlsl" -// // #elif defined(SHADER_API_GLES3) -// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/GLES3.hlsl" -// // #elif defined(SHADER_API_GLES) -// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/GLES2.hlsl" -// // #else -// // #error unsupported shader api -// // #endif - -// // #if defined(SHADER_API_D3D11) -// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/D3D11.hlsl" -// // #elif defined(SHADER_API_METAL) -// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/Metal.hlsl" -// // #elif defined(SHADER_API_VULKAN) -// // #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/API/Vulkan.hlsl" -// // #else -// // #error unsupported shader api -// // #endif - -// // #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/TextureXR.hlsl" - - -// struct v2f -// { -// float2 uv : TEXCOORD0; -// float4 screenPos : SV_POSITION; -// }; - - -// // Texture2D DepthMap; -// // SamplerState samplerDepthMap; - -// // TEXTURE2D_X(_CameraDepthTexture); -// // SAMPLER(sampler_CameraDepthTexture); - - - -// float4 frag (v2f i) : SV_Target -// { -// // float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) -// // float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); -// // int3 viewIndices = getSubPixelViewIndices(computedScreenPos); - -// float2 uvCoords = i.uv; - -// // // mirror the image if necessary -// // if (mirror != 0) { -// // uvCoords.x = 1.0 - uvCoords.x; -// // } - -// // float4 depthMap = _CameraDepthTexture.Sample(sampler_CameraDepthTexture, uvCoords); -// // float depth = LOAD_TEXTURE2D_X_LOD(_CameraDepthTexture, uvCoords, 0).r; -// return float4(1, 0, 0, 1.f); -// } -// ENDHLSL - -// // URP Shader -// SubShader -// { -// PackageRequirements -// { -// "com.unity.render-pipelines.universal": "unity=2021.3" -// } - -// Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"} -// LOD 100 -// Cull Off - -// Pass -// { -// ZTest Always -// Blend Off -// Cull Off - -// HLSLPROGRAM -// #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" - -// #pragma vertex vert -// #pragma fragment frag - -// struct VertAttributes -// { -// uint vertexID : SV_VertexID; -// UNITY_VERTEX_INPUT_INSTANCE_ID -// }; - -// v2f vert(VertAttributes input) -// { -// v2f output; -// UNITY_SETUP_INSTANCE_ID(input); -// UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); -// output.uv = GetFullScreenTriangleTexCoord(input.vertexID); -// output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); - -// return output; - -// } -// ENDHLSL -// } -// } - -// // HDRP Shader -// SubShader -// { -// PackageRequirements -// { -// "com.unity.render-pipelines.high-definition": "unity=2021.3" -// } - -// Tags { "RenderType"="Opaque" "RenderPipeline" = "HDRenderPipeline"} - -// Pass -// { -// Name "G3DFullScreen3D" - -// ZTest Always -// Blend Off -// Cull Off - -// HLSLPROGRAM -// #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" -// #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" - -// #pragma vertex vert -// #pragma fragment fragHDRP - -// Texture2D DepthMap; -// SamplerState samplerDepthMap; - -// struct VertAttributes -// { -// uint vertexID : SV_VertexID; -// UNITY_VERTEX_INPUT_INSTANCE_ID -// }; - -// v2f vert(VertAttributes input) -// { -// v2f output; -// UNITY_SETUP_INSTANCE_ID(input); -// UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); -// output.uv = GetFullScreenTriangleTexCoord(input.vertexID); -// output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); - -// return output; - -// } - -// float4 fragHDRP (v2f i) : SV_Target -// { -// // float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) -// // float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); -// // int3 viewIndices = getSubPixelViewIndices(computedScreenPos); - -// float2 uvCoords = i.uv; - -// // // mirror the image if necessary -// // if (mirror != 0) { -// // uvCoords.x = 1.0 - uvCoords.x; -// // } - -// float4 depthMap = DepthMap.Sample(samplerDepthMap, uvCoords); -// float depth = LoadCameraDepth(uvCoords); -// return float4(depth, 0, 0, 1.f); -// } -// ENDHLSL -// } -// } - -// // Built-in Render Pipeline -// SubShader -// { -// // No culling or depth -// Cull Off ZTest Always - -// Pass -// { -// HLSLPROGRAM -// #pragma vertex vert -// #pragma fragment fragSRP - -// #include "UnityCG.cginc" - -// v2f vert(float4 vertex : POSITION, float2 uv : TEXCOORD0) -// { -// v2f o; -// o.uv = uv; -// o.screenPos = UnityObjectToClipPos(vertex); -// return o; -// } - - -// float4 fragSRP(v2f i) : SV_Target -// { -// // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) -// // The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity. -// // i.screenPos.y = viewportHeight - i.screenPos.y; -// return frag(i); -// } - -// ENDHLSL -// } -// } -// } +} \ No newline at end of file diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index bbe4808..734e29c 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -22,11 +22,17 @@ protected override void Execute(CustomPassContext ctx) { CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - for (int i = 0; i < 10; i++) + float viewDistance = camera.farClipPlane - camera.nearClipPlane; + float stepSize = viewDistance / 1024.0f; + + for (int i = 1024; i > 0; i--) { - float layer = i / 1024.0f; - ctx.propertyBlock.SetFloat(Shader.PropertyToID("layer"), i); + float layer = i * stepSize + camera.nearClipPlane; + ctx.propertyBlock.SetFloat(Shader.PropertyToID("layer"), layer); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("layerDistance"), stepSize); + // sample left camera + ctx.propertyBlock.SetFloat(Shader.PropertyToID("useLeftCamera"), 1); CoreUtils.DrawFullScreen( ctx.cmd, fullscreenPassMaterial, @@ -105,8 +111,8 @@ protected override void Execute(CustomPassContext ctx) CustomPassUtils.Copy(ctx, ctx.cameraColorBuffer, mosaicImageHandle); CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_rows"), 3); - ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_columns"), 3); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_rows"), 4); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_columns"), 4); CoreUtils.DrawFullScreen( ctx.cmd, fullscreenPassMaterial, From 6541e1a168b2f573a3560e05e0b39442ff15d354 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 15 Jul 2025 15:29:56 +0200 Subject: [PATCH 005/149] wip view generation shader update (focus plane wrong) --- G3DCamera.cs | 39 ++++-- Resources/G3DViewGeneration.shader | 198 +++++++++++++++------------ Scripts/G3DHDRPViewGenerationPass.cs | 11 +- 3 files changed, 143 insertions(+), 105 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 7bfaf3f..fd9af9d 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -377,14 +377,37 @@ void Start() viewGenerationMaterial.SetInt(Shader.PropertyToID("depth_layer_discretization"), 1024); viewGenerationMaterial.SetFloat(Shader.PropertyToID("crop"), 0.01f); viewGenerationMaterial.SetFloat(Shader.PropertyToID("maxDisparity"), 1.9f); - viewGenerationMaterial.SetFloat(Shader.PropertyToID("focus_plane"), 0.1f); + float focalLengthInPixel = + mainCamera.pixelWidth + / (2.0f * Mathf.Tan(mainCamera.fieldOfView * Mathf.Deg2Rad / 2.0f) * Mathf.Rad2Deg); viewGenerationMaterial.SetFloat( - Shader.PropertyToID("normalization_min"), - mainCamera.nearClipPlane + Shader.PropertyToID("focalLengthInPixel"), + focalLengthInPixel ); - viewGenerationMaterial.SetFloat( - Shader.PropertyToID("normalization_max"), - mainCamera.farClipPlane + viewGenerationMaterial.SetFloat(Shader.PropertyToID("nearPlane"), mainCamera.nearClipPlane); + viewGenerationMaterial.SetFloat(Shader.PropertyToID("farPlane"), mainCamera.farClipPlane); + float viewRange = mainCamera.farClipPlane - mainCamera.nearClipPlane; + float stepSize = viewRange / 1024.0f; + viewGenerationMaterial.SetFloat(Shader.PropertyToID("layerDistance"), stepSize); + viewGenerationMaterial.SetInteger( + Shader.PropertyToID("cameraPixelWidth"), + mainCamera.pixelWidth + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("inverseLeftProjMatrix"), + cameras[1].projectionMatrix.inverse + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("inverseRightProjMatrix"), + cameras[0].projectionMatrix.inverse + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("leftProjMatrix"), + cameras[1].projectionMatrix + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("rightProjMatrix"), + cameras[0].projectionMatrix ); viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; @@ -433,6 +456,8 @@ void Start() cachedWindowSize = new Vector2Int(Screen.width, Screen.height); headPositionLog = new Queue(10000); + + viewGenerationPass.focusDistance = scaledFocusDistanceAndDolly; } void OnApplicationQuit() @@ -1042,8 +1067,6 @@ void updateCameras() -currentFocusDistance ); - viewGenerationMaterial?.SetFloat(Shader.PropertyToID("focus_plane"), currentFocusDistance); - //calculate camera positions and matrices for (int i = 0; i < internalCameraCount; i++) { diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index 601ee0c..ea45491 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -35,14 +35,21 @@ Shader "G3D/ViewGeneration" Texture2D _depthMapLeft; SamplerState sampler_depthMapLeft; + float4x4 rightProjMatrix; + float4x4 inverseRightProjMatrix; + float4x4 leftProjMatrix; + float4x4 inverseLeftProjMatrix; + int useLeftCamera = 1; // 0 = right, 1 = left + // 0 = right camera, 1 = left camera Texture2D texture0; SamplerState samplertexture0; Texture2D texture1; SamplerState samplertexture1; float layer; // range 0..1 + float focusDistance; // distance to focus plane in camera space int depth_layer_discretization; // max layer amount -> 1024 @@ -50,14 +57,17 @@ Shader "G3D/ViewGeneration" float maxDisparity = 1.5f; // range 0..2 disparity between left and right camera float crop; // range 0..1 percentage of image to crop from each side - float normalization_min = 0; // scene min depth value -> set to nearPlane - float normalization_max = 1; // scene max depth value -> set to farPlane + float nearPlane = 0; // scene min depth value -> set to nearPlane + float farPlane = 1; // scene max depth value -> set to farPlane int debug_mode; int grid_size_x; int grid_size_y; - float focus_plane = 0.5f; // range near to far plane + float focalLengthInPixel = 600; + + int cameraPixelWidth = 1920; + struct VertAttributes { @@ -86,26 +96,38 @@ Shader "G3D/ViewGeneration" float linearNormalize(float value, float minV, float maxV) { return clamp((value - minV) / (maxV - minV), 0.0f, 1.0f); } + + float map(float x, float in_min, float in_max, float out_min, float out_max) + { + // Convert the current value to a percentage + // 0% - min1, 100% - max1 + float perc = (x - in_min) / (in_max - in_min); + + // Do the same operation backwards with min2 and max2 + float value = perc * (out_max - out_min) + out_min; + return value; + } - // convert range back from 0..1 to minV..maxV - // normalized device coordinates -> pixel coords + depth - // normalized device coordinates times inverse projecion matrix - float4 sampleDepth(float2 texCoords) { - if(useLeftCamera == 1) { - return _depthMapLeft.Sample(sampler_depthMapLeft, texCoords); - } else { - return _depthMapRight.Sample(sampler_depthMapRight, texCoords); - } + float sampleLeftDepth(float2 uv) { + float4 depthSample = _depthMapLeft.Sample(sampler_depthMapLeft, uv); + float depth = Linear01Depth(depthSample.r, _ZBufferParams); // convert depth from logarithmic scale to linear scale + float x = uv.x * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates + float y = uv.y * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates + float4 NDC = float4(x, y, depth, 1.0f); + NDC = mul(inverseLeftProjMatrix, NDC); // apply inverse projection matrix to get clip space coordinates + return -NDC.z / NDC.w; // devide by w to get depth in view space } - float4 sampleTexture(float2 texCoords) { - if(useLeftCamera == 1) { - return texture0.Sample(samplertexture0, texCoords); - } else { - return texture1.Sample(samplertexture1, texCoords); - } + float sampleRightDepth(float2 uv) { + float4 depthSample = _depthMapRight.Sample(sampler_depthMapRight, uv); + float depth = Linear01Depth(depthSample.r, _ZBufferParams); // convert depth from logarithmic scale to linear scale + float x = uv.x * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates + float y = uv.y * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates + float4 NDC = float4(x, y, depth, 1.0f); + NDC = mul(inverseRightProjMatrix, NDC); // apply inverse projection matrix to get clip space coordinates + return -NDC.z / NDC.w; // devide by w to get depth in view space } - + /// /// creates a grid of views with a given grid size. /// each view is offset by a given disparity value. @@ -114,15 +136,11 @@ Shader "G3D/ViewGeneration" /// float4 fragHDRP (v2f i) : SV_Target { - float2 texCoord = i.uv; - - float4 outputColor = float4(0.0f, 0.0f, 0.0f, 1.0f); + // float depth = sampleLeftDepth(i.uv); + // depth = map(depth, nearPlane, farPlane, 0, 1); + // return float4(depth, depth, depth, 1.0f); // return depth value in world space - - int gridCount = grid_size_x * grid_size_y; - float disparityStep = maxDisparity / gridCount * 1.0f; // flip (1.0) to invert view order - // the text coords of the original left and right view are from 0 - 1 // the tex coords of the texel we are currently rendering are also from 0 - 1 // but we want to create a grid of views, so we need to transform the tex coords @@ -136,87 +154,85 @@ Shader "G3D/ViewGeneration" // -> e.g. 2.4 turns to 2 // step 3: subtract the integer part from the transformed tex coords to get the texel coords in the grid cell // -> e.g. 2.4 - 2 = 0.4 -> final texel coords in the grid cell are 0.4, 0.5 - float2 cellCoordinates = float2(texCoord.x, texCoord.y); + float2 cellCoordinates = float2(i.uv.x, 1.0 - i.uv.y); // flip y coordiate to have cell index 0 in upper left corner cellCoordinates = float2(cellCoordinates.x * grid_size_x, cellCoordinates.y * grid_size_y); - - int viewIndex = int(cellCoordinates.x) + grid_size_x * int(cellCoordinates.y); - + uint viewIndex = uint(cellCoordinates.x) + grid_size_x * uint(cellCoordinates.y); // texcoords in this texcels coordinate system (from 0 - 1) float2 actualTexCoords = float2(cellCoordinates.x - float(int(cellCoordinates.x)), cellCoordinates.y - float(int(cellCoordinates.y))); - + actualTexCoords.y = 1.0 - actualTexCoords.y; // flip y coordinate to match original tex coords + // Crop image to avoid image artefacts at border // actualTexCoords *= (1.0f - crop); // actualTexCoords += crop * 0.5f; - + + uint gridCount = grid_size_x * grid_size_y; // first and last image in the grid are the left and right camera - if (gridCount % 2 == 1 && viewIndex == gridCount / 2) { // CENTER VIEW - - outputColor = sampleTexture(actualTexCoords); - - outputColor.a = 0.0f; - return outputColor; + if (viewIndex == 0) { + return texture1.Sample(samplertexture1, actualTexCoords); // sample the left camera texture } + if (viewIndex == gridCount - 1) { + return texture0.Sample(samplertexture0, actualTexCoords); // sample the right camera texture + } + + float disparityStep = maxDisparity / gridCount; - // disparity has to be calculated based on the formula in https://medium.com/analytics-vidhya/distance-estimation-cf2f2fd709d8 - // Z is the depth we get from the depthmap - // D is the disparity we want to calculate - // D = ((f/d) * T) / Z - - // Disparity calculation A: - // linear shift - // - float dynamicViewOffset = viewIndex * disparityStep; + float distLayerFocus = layer - focusDistance; // distance of current layer to focus plane + + float leftOffset = viewIndex * disparityStep; // distance between the original left camera and the current view + float left = 1.f / layer; + float right = 1.f / distLayerFocus; + float DLeft = (focalLengthInPixel * 1.0f) * (left - right); // calculate offset of layer in pixel + DLeft = DLeft / cameraPixelWidth; //map(DLeft, 0.0f, cameraPixelWidth, 0.0f, 1.0f); // convert to texture coordinates + // float rightOffset = (gridCount - 1 - viewIndex) * disparityStep; // distance between the original left camera and the current view + // float DRight = (focalLengthInPixel * rightOffset) / distLayerFocus; // calculate offset of layer in pixel + // DRight = map(DRight, 0.0f, cameraPixelWidth, 0.0f, 1.0f); // convert to texture coordinates - // distance to the focus plane - float focusPlaneDistance = (focus_plane - (normalization_max - layer)); - - float2 texCoordShifted = actualTexCoords; - texCoordShifted.x += focusPlaneDistance * dynamicViewOffset * 0.1f; - - float texDepth = sampleDepth(texCoordShifted).r; - // layer 0 = back - // layer 1 = front - - // texDepth 0 = back - // discard fragments with a depth smaller than the depth of the layer we are currently rendering - if ((layer - texDepth) > layerDistance) { - // from back to front - // discard fragments that are in front of current render - // layer - discard; + float2 texCoordShiftedLeft = actualTexCoords; + texCoordShiftedLeft.x += DLeft; + float shiftedLeftDepth = sampleLeftDepth(texCoordShiftedLeft); + // float2 texCoordShiftedRight = actualTexCoords; + // texCoordShiftedRight.x -= DRight; + // float shiftedRightDepth = sampleRightDepth(texCoordShiftedRight); + + if ((layer - shiftedLeftDepth) > layerDistance) { + // from back to front + // discard fragments that are in front of current render + // layer + + // if ((layer - shiftedRightDepth) > layerDistance) { + // discard; + // } + + // if ((layer - shiftedRightDepth) < -layerDistance) { + // discard; + // } + // return texture0.Sample(samplertexture0, texCoordShiftedRight); + discard; } - - float hole_marker = 0.0f; - // FragColor.b >0.5 -> hole - if ((layer - texDepth) < -layerDistance) { - // from back to front - // discard fragments that are in front of current render - // layer - hole_marker = 1.0f; - discard; + + if ((layer - shiftedLeftDepth) < -layerDistance) { + // from back to front + // discard fragments that are in front of current render + // layer + // if ((layer - shiftedRightDepth) > layerDistance) { + // discard; + // } + + // if ((layer - shiftedRightDepth) < -layerDistance) { + // discard; + // } + // return texture0.Sample(samplertexture0, texCoordShiftedRight); + discard; } - - // float dx = linearNormalize(sobel_x(texCoordShifted), normalization_min, - // normalization_max); - // float dxy = linearNormalize(sobel(texCoordShifted), normalization_min, - // normalization_max); - - outputColor = sampleTexture(texCoordShifted); - - if (debug_mode == 4) { // DEBUG OUTPUT - outputColor.r = (1.0f - texDepth); // 0.0=front | 1.0=back - outputColor.g = 0.0f; - outputColor.b = 0.0f; - outputColor.a = 1.0f; - return outputColor; + + if (DLeft < 0.0f) { + return float4(0.0f, -DLeft, 0.0f, 1.0f); // return black if the offset is negative } - + return float4(DLeft, 0.0f, 0.0f, 1.0f); // return the offset for debugging purposes - outputColor = clamp(outputColor, 0.0f, 1.0f); - // outputColor.a = (dxy + dx) * (0.5); // TODO: set this bias as parameter - return outputColor; + return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture } ENDHLSL } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 734e29c..bf1af55 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -13,6 +13,8 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public RTHandle leftColorMapHandle; public RTHandle rightColorMapHandle; + public float focusDistance = 1.0f; + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } protected override void Execute(CustomPassContext ctx) @@ -22,17 +24,16 @@ protected override void Execute(CustomPassContext ctx) { CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - float viewDistance = camera.farClipPlane - camera.nearClipPlane; - float stepSize = viewDistance / 1024.0f; + float viewRange = camera.farClipPlane - camera.nearClipPlane; + float stepSize = viewRange / 1024.0f; for (int i = 1024; i > 0; i--) { float layer = i * stepSize + camera.nearClipPlane; ctx.propertyBlock.SetFloat(Shader.PropertyToID("layer"), layer); - ctx.propertyBlock.SetFloat(Shader.PropertyToID("layerDistance"), stepSize); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("focusDistance"), focusDistance); // sample left camera - ctx.propertyBlock.SetFloat(Shader.PropertyToID("useLeftCamera"), 1); CoreUtils.DrawFullScreen( ctx.cmd, fullscreenPassMaterial, @@ -40,8 +41,6 @@ protected override void Execute(CustomPassContext ctx) shaderPassId: 0 ); } - - for (int i = 0; i < 1; i++) { } } else if (isLeftCamera(camera)) { From cae6f2c7e54ee6c61ac89f2235eeabe2f4ab8772 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 15 Jul 2025 15:43:51 +0200 Subject: [PATCH 006/149] remove unused imports --- Scripts/G3DHDRPCustomPass.cs | 2 -- Scripts/G3DHDRPViewGenerationPass.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/Scripts/G3DHDRPCustomPass.cs b/Scripts/G3DHDRPCustomPass.cs index c9178de..87b2456 100644 --- a/Scripts/G3DHDRPCustomPass.cs +++ b/Scripts/G3DHDRPCustomPass.cs @@ -1,6 +1,4 @@ #if G3D_HDRP -using Codice.Client.BaseCommands; -using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index bf1af55..e47f939 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -1,6 +1,4 @@ #if G3D_HDRP -using Codice.Client.BaseCommands; -using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; From 9f958c87f3768c491201cf60f791b22bb29f1543 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 16 Jul 2025 16:50:02 +0200 Subject: [PATCH 007/149] shader update --- G3DCamera.cs | 6 +- Resources/G3DViewGeneration.shader | 124 +++++++++++++---------------- 2 files changed, 58 insertions(+), 72 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index fd9af9d..6dca80e 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -376,10 +376,10 @@ void Start() viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); viewGenerationMaterial.SetInt(Shader.PropertyToID("depth_layer_discretization"), 1024); viewGenerationMaterial.SetFloat(Shader.PropertyToID("crop"), 0.01f); - viewGenerationMaterial.SetFloat(Shader.PropertyToID("maxDisparity"), 1.9f); + viewGenerationMaterial.SetFloat(Shader.PropertyToID("maxDisparity"), viewSeparation * 15); // TODO do not hardcode this value float focalLengthInPixel = - mainCamera.pixelWidth - / (2.0f * Mathf.Tan(mainCamera.fieldOfView * Mathf.Deg2Rad / 2.0f) * Mathf.Rad2Deg); + (mainCamera.pixelHeight / 2) + / (Mathf.Tan(mainCamera.fieldOfView / 2 * Mathf.Deg2Rad) * Mathf.Rad2Deg); viewGenerationMaterial.SetFloat( Shader.PropertyToID("focalLengthInPixel"), focalLengthInPixel diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index ea45491..ce6e58c 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -40,8 +40,6 @@ Shader "G3D/ViewGeneration" float4x4 leftProjMatrix; float4x4 inverseLeftProjMatrix; - int useLeftCamera = 1; // 0 = right, 1 = left - // 0 = right camera, 1 = left camera Texture2D texture0; SamplerState samplertexture0; @@ -56,11 +54,9 @@ Shader "G3D/ViewGeneration" float layerDistance = 1.0f; // distance between layers in world space float maxDisparity = 1.5f; // range 0..2 disparity between left and right camera - float crop; // range 0..1 percentage of image to crop from each side float nearPlane = 0; // scene min depth value -> set to nearPlane float farPlane = 1; // scene max depth value -> set to farPlane - int debug_mode; int grid_size_x; int grid_size_y; @@ -93,10 +89,6 @@ Shader "G3D/ViewGeneration" } - float linearNormalize(float value, float minV, float maxV) { - return clamp((value - minV) / (maxV - minV), 0.0f, 1.0f); - } - float map(float x, float in_min, float in_max, float out_min, float out_max) { // Convert the current value to a percentage @@ -111,21 +103,22 @@ Shader "G3D/ViewGeneration" float sampleLeftDepth(float2 uv) { float4 depthSample = _depthMapLeft.Sample(sampler_depthMapLeft, uv); float depth = Linear01Depth(depthSample.r, _ZBufferParams); // convert depth from logarithmic scale to linear scale - float x = uv.x * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates - float y = uv.y * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates - float4 NDC = float4(x, y, depth, 1.0f); - NDC = mul(inverseLeftProjMatrix, NDC); // apply inverse projection matrix to get clip space coordinates - return -NDC.z / NDC.w; // devide by w to get depth in view space + depth = map(depth, 0.0f, 1.0f, 0, farPlane); + return depth; } float sampleRightDepth(float2 uv) { float4 depthSample = _depthMapRight.Sample(sampler_depthMapRight, uv); float depth = Linear01Depth(depthSample.r, _ZBufferParams); // convert depth from logarithmic scale to linear scale - float x = uv.x * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates - float y = uv.y * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates - float4 NDC = float4(x, y, depth, 1.0f); - NDC = mul(inverseRightProjMatrix, NDC); // apply inverse projection matrix to get clip space coordinates - return -NDC.z / NDC.w; // devide by w to get depth in view space + depth = map(depth, 0.0f, 1.0f, 0, farPlane); + return depth; + } + + float computeClipSpaceOffset(float cameraDistance, float focusDist, float distLayerFocusPlane, float4x4 projectionMatrix) { + float y = (cameraDistance / focusDist) * distLayerFocusPlane; + float4 offsetClipSpace = float4(y, 0.0f, 0.0f, 1.0f); // offset in clip space + offsetClipSpace = mul(offsetClipSpace, projectionMatrix); // apply projection matrix to get clip space coordinates + return offsetClipSpace.x; } /// @@ -137,7 +130,7 @@ Shader "G3D/ViewGeneration" float4 fragHDRP (v2f i) : SV_Target { // float depth = sampleLeftDepth(i.uv); - // depth = map(depth, nearPlane, farPlane, 0, 1); + // depth = map(depth, 0, farPlane, 0, 1); // return float4(depth, depth, depth, 1.0f); // return depth value in world space @@ -161,11 +154,6 @@ Shader "G3D/ViewGeneration" float2 actualTexCoords = float2(cellCoordinates.x - float(int(cellCoordinates.x)), cellCoordinates.y - float(int(cellCoordinates.y))); actualTexCoords.y = 1.0 - actualTexCoords.y; // flip y coordinate to match original tex coords - // Crop image to avoid image artefacts at border - // actualTexCoords *= (1.0f - crop); - // actualTexCoords += crop * 0.5f; - - uint gridCount = grid_size_x * grid_size_y; // first and last image in the grid are the left and right camera if (viewIndex == 0) { @@ -176,63 +164,61 @@ Shader "G3D/ViewGeneration" } float disparityStep = maxDisparity / gridCount; - - float distLayerFocus = layer - focusDistance; // distance of current layer to focus plane + float distLayerFocusPlane = -(layer - focusDistance); // distance between the layer and the focus plane float leftOffset = viewIndex * disparityStep; // distance between the original left camera and the current view - float left = 1.f / layer; - float right = 1.f / distLayerFocus; - float DLeft = (focalLengthInPixel * 1.0f) * (left - right); // calculate offset of layer in pixel - DLeft = DLeft / cameraPixelWidth; //map(DLeft, 0.0f, cameraPixelWidth, 0.0f, 1.0f); // convert to texture coordinates - // float rightOffset = (gridCount - 1 - viewIndex) * disparityStep; // distance between the original left camera and the current view - // float DRight = (focalLengthInPixel * rightOffset) / distLayerFocus; // calculate offset of layer in pixel - // DRight = map(DRight, 0.0f, cameraPixelWidth, 0.0f, 1.0f); // convert to texture coordinates + float DLeft = computeClipSpaceOffset(leftOffset, focusDistance, distLayerFocusPlane, leftProjMatrix); // calculate offset of layer in pixel + + float rightOffset = (gridCount - 1 - viewIndex) * disparityStep; // distance between the original left camera and the current view + float DRight = computeClipSpaceOffset(rightOffset, focusDistance, distLayerFocusPlane, rightProjMatrix); // calculate offset of layer in pixel float2 texCoordShiftedLeft = actualTexCoords; texCoordShiftedLeft.x += DLeft; float shiftedLeftDepth = sampleLeftDepth(texCoordShiftedLeft); - // float2 texCoordShiftedRight = actualTexCoords; - // texCoordShiftedRight.x -= DRight; - // float shiftedRightDepth = sampleRightDepth(texCoordShiftedRight); - if ((layer - shiftedLeftDepth) > layerDistance) { - // from back to front - // discard fragments that are in front of current render - // layer - - // if ((layer - shiftedRightDepth) > layerDistance) { - // discard; - // } - - // if ((layer - shiftedRightDepth) < -layerDistance) { - // discard; - // } - // return texture0.Sample(samplertexture0, texCoordShiftedRight); - discard; - } + float2 texCoordShiftedRight = actualTexCoords; + texCoordShiftedRight.x -= DRight; + float shiftedRightDepth = sampleRightDepth(texCoordShiftedRight); - if ((layer - shiftedLeftDepth) < -layerDistance) { - // from back to front - // discard fragments that are in front of current render - // layer - // if ((layer - shiftedRightDepth) > layerDistance) { - // discard; - // } - - // if ((layer - shiftedRightDepth) < -layerDistance) { - // discard; - // } - // return texture0.Sample(samplertexture0, texCoordShiftedRight); - discard; + int leftFills = 1; + if(texCoordShiftedLeft.x < 0 || texCoordShiftedLeft.x > 1.0f) { + leftFills = 0; // discard if the tex coord is out of bounds } - - if (DLeft < 0.0f) { - return float4(0.0f, -DLeft, 0.0f, 1.0f); // return black if the offset is negative + if (abs((layer - shiftedLeftDepth)) > layerDistance) { + leftFills = 0; // discard if the layer is too far away from the shifted left depth } - return float4(DLeft, 0.0f, 0.0f, 1.0f); // return the offset for debugging purposes + + + int rightFills = 0; + if(texCoordShiftedRight.x < 0 || texCoordShiftedRight.x > 1.0f) { + rightFills = 0; // discard if the tex coord is out of bounds + } + if (abs((layer - shiftedRightDepth)) > layerDistance) { + rightFills = 0; // discard if the layer is too far away from the shifted right depth + } + + if (leftFills == 0 && rightFills == 0) { + discard; // discard if both left and right camera do not fill the layer + } + if (leftFills == 1 && rightFills == 0) { + return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture + } + if (leftFills == 0 && rightFills == 1) { + return texture0.Sample(samplertexture0, texCoordShiftedRight); // sample the right camera texture + } + + if(shiftedLeftDepth < shiftedRightDepth) { + return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture + } + + return texture0.Sample(samplertexture0, texCoordShiftedRight); // sample the right camera texture + - return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture + // if (DLeft < 0.0f) { + // return float4(0.0f, -DLeft, 0.0f, 1.0f); + // } + // return float4(DLeft, 0.0f, 0.0f, 1.0f); } ENDHLSL } From 6b216f3e74de6a15cfbe0252703601152cc7c16c Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 18 Jul 2025 17:10:33 +0200 Subject: [PATCH 008/149] reprojection attempt based on view projection matrix --- G3DCamera.cs | 29 +++++ Resources/G3DViewGeneration.shader | 177 +++++++++++++++++++++++------ 2 files changed, 172 insertions(+), 34 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 6dca80e..65d2acf 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -916,6 +916,35 @@ void Update() oldRenderResolutionScale = renderResolutionScale; updateShaderRenderTextures(); } + + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("inverseLeftProjMatrix"), + cameras[1].projectionMatrix.inverse + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("inverseRightProjMatrix"), + cameras[0].projectionMatrix.inverse + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("leftViewMatrix"), + cameras[1].worldToCameraMatrix.inverse + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("leftProjMatrix"), + cameras[1].projectionMatrix + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("rightProjMatrix"), + cameras[0].projectionMatrix + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("mainCameraProjectionMatrix"), + mainCamera.projectionMatrix + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("inverseMainCameraViewMatrix"), + mainCamera.worldToCameraMatrix.inverse + ); } private void updateScreenViewportProperties() diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index ce6e58c..95d9e0e 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -39,6 +39,9 @@ Shader "G3D/ViewGeneration" float4x4 inverseRightProjMatrix; float4x4 leftProjMatrix; float4x4 inverseLeftProjMatrix; + float4x4 mainCameraProjectionMatrix; + float4x4 inverseMainCameraViewMatrix; + float4x4 leftViewMatrix; // 0 = right camera, 1 = left camera Texture2D texture0; @@ -65,6 +68,7 @@ Shader "G3D/ViewGeneration" int cameraPixelWidth = 1920; + struct VertAttributes { uint vertexID : SV_VertexID; @@ -89,6 +93,7 @@ Shader "G3D/ViewGeneration" } + float map(float x, float in_min, float in_max, float out_min, float out_max) { // Convert the current value to a percentage @@ -100,18 +105,14 @@ Shader "G3D/ViewGeneration" return value; } - float sampleLeftDepth(float2 uv) { - float4 depthSample = _depthMapLeft.Sample(sampler_depthMapLeft, uv); - float depth = Linear01Depth(depthSample.r, _ZBufferParams); // convert depth from logarithmic scale to linear scale - depth = map(depth, 0.0f, 1.0f, 0, farPlane); - return depth; - } - float sampleRightDepth(float2 uv) { - float4 depthSample = _depthMapRight.Sample(sampler_depthMapRight, uv); + float4 depthSample = _depthMapRight.Sample(sampler_depthMapRight, uv);; float depth = Linear01Depth(depthSample.r, _ZBufferParams); // convert depth from logarithmic scale to linear scale - depth = map(depth, 0.0f, 1.0f, 0, farPlane); - return depth; + float x = uv.x * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates + float y = uv.y * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates + float4 NDC = float4(x, y, depth, 1.0f); + NDC = mul(inverseRightProjMatrix, NDC); // apply inverse projection matrix to get clip space coordinates + return -NDC.z / NDC.w; // devide by w to get depth in view space } float computeClipSpaceOffset(float cameraDistance, float focusDist, float distLayerFocusPlane, float4x4 projectionMatrix) { @@ -121,6 +122,105 @@ Shader "G3D/ViewGeneration" return offsetClipSpace.x; } + + float sampleLeftDepthProjection(float2 uv) { + float4 depthSample = _depthMapLeft.Sample(sampler_depthMapLeft, uv); + float depth = Linear01Depth(depthSample.r, _ZBufferParams); // convert depth from logarithmic scale to linear scale + float x = uv.x * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates + float y = uv.y * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates + float4 NDC = float4(x, y, depth, 1.0f); + NDC = mul(inverseLeftProjMatrix, NDC); // apply inverse projection matrix to get clip space coordinates + return -NDC.z / NDC.w; // devide by w to get depth in view space + } + + float4x4 getIdentityMatrix() { + return float4x4( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + } + float4x4 inverse(float4x4 m) { + float n11 = m[0][0], n12 = m[1][0], n13 = m[2][0], n14 = m[3][0]; + float n21 = m[0][1], n22 = m[1][1], n23 = m[2][1], n24 = m[3][1]; + float n31 = m[0][2], n32 = m[1][2], n33 = m[2][2], n34 = m[3][2]; + float n41 = m[0][3], n42 = m[1][3], n43 = m[2][3], n44 = m[3][3]; + + float t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44; + float t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44; + float t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44; + float t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + + float det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; + float idet = 1.0f / det; + + float4x4 ret; + + ret[0][0] = t11 * idet; + ret[0][1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * idet; + ret[0][2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * idet; + ret[0][3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * idet; + + ret[1][0] = t12 * idet; + ret[1][1] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * idet; + ret[1][2] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * idet; + ret[1][3] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * idet; + + ret[2][0] = t13 * idet; + ret[2][1] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * idet; + ret[2][2] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * idet; + ret[2][3] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * idet; + + ret[3][0] = t14 * idet; + ret[3][1] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * idet; + ret[3][2] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * idet; + ret[3][3] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * idet; + + return ret; + } + + float4x4 calculateProjectionMatrix(float localCameraOffset) { + // horizontal obliqueness + float horizontalObl = -localCameraOffset / focusDistance; + + // focus distance is in view space. Writing directly into projection matrix would require focus distance to be in projection space + float4x4 shearMatrix = getIdentityMatrix(); + shearMatrix[0, 2] = horizontalObl; + // apply new projection matrix + return mul(mainCameraProjectionMatrix, shearMatrix); + } + + float calculateOffset(float layerDist, float cameraOffset, float2 uv) { + // erst punkt in clip space berechnen + // dann mit inverse projection matrix in view space umwandeln + // dann mit inverse view matrix in world space umwandeln + // dann mit view matrix der linken kamera in view space umwandeln + // dann mit projection matrix der linken kamera in clip space umwandeln + // dann die x koordinaten der beiden punkte vergleichen und den unterschied nehmen + + float tmp = map(layerDist, 0.0f, farPlane, 0.0f, 1.0f); // convert layer distance from [nearPlane, farPlane] to [0,1] + float4 p = float4(0.0f, 0.0f, tmp, 1.0f); // point in view space + float4x4 cameraProjectionMatrix = calculateProjectionMatrix(cameraOffset); + float4x4 inverseCameraProjectionMatrix = inverse(cameraProjectionMatrix); // inverse projection matrix to convert from clip space to view space + float4x4 inverseCameraViewMatrix = inverseMainCameraViewMatrix; // inverse view matrix to convert from view space to world space + // shift view inverse view matrix to the left + inverseMainCameraViewMatrix[0][3] += cameraOffset; // shift the view matrix to the left by the camera offset + p = mul(inverseCameraProjectionMatrix, p); // convert from clip space to view space + p = mul(inverseCameraViewMatrix, p); // convert from view space to world space + + // p now in world space + + p = mul(leftViewMatrix, p); // apply left view matrix to get shifted point in view space + p = mul(leftProjMatrix, p); // apply main camera projection matrix to get clip space coordinates + + float clipSpaceX = p.x / p.w; // convert to clip space by dividing by w + clipSpaceX = clipSpaceX * 0.5f + 0.5f; // convert from [-1,1] to [0,1] to get texture coordinates + // clipSpaceX = map(clipSpaceX, -1.0f, 1.0f, 0.0f, 1.0f); // convert from [-1,1] to [0,1] to get texture coordinates + + return clipSpaceX; // return the difference between the shifted and original x coordinate + } + /// /// creates a grid of views with a given grid size. /// each view is offset by a given disparity value. @@ -129,54 +229,63 @@ Shader "G3D/ViewGeneration" /// float4 fragHDRP (v2f i) : SV_Target { - // float depth = sampleLeftDepth(i.uv); - // depth = map(depth, 0, farPlane, 0, 1); + // float depth = sampleLeftDepthProjection(i.uv); + // depth = map(depth, nearPlane, farPlane, 0, 1); // return float4(depth, depth, depth, 1.0f); // return depth value in world space - - // the text coords of the original left and right view are from 0 - 1 - // the tex coords of the texel we are currently rendering are also from 0 - 1 - // but we want to create a grid of views, so we need to transform the tex coords - // to the grid size. - // basically we want to figure out in which grid cell the current texel is, then convert the texel coords to the grid cell coords. - // example assuming a grid size of 3x3: - // original tex coords: 0.8, 0.5 - // step 1: transform the tex coords to the grid size by multiplying with grid size - // -> e.g. original x coord 0.8 turns to 0.8 * 3 = 2.4 - // step 2: figure out the grid cell by taking the integer part of the transformed tex coords - // -> e.g. 2.4 turns to 2 - // step 3: subtract the integer part from the transformed tex coords to get the texel coords in the grid cell - // -> e.g. 2.4 - 2 = 0.4 -> final texel coords in the grid cell are 0.4, 0.5 + { + // the text coords of the original left and right view are from 0 - 1 + // the tex coords of the texel we are currently rendering are also from 0 - 1 + // but we want to create a grid of views, so we need to transform the tex coords + // to the grid size. + // basically we want to figure out in which grid cell the current texel is, then convert the texel coords to the grid cell coords. + // example assuming a grid size of 3x3: + // original tex coords: 0.8, 0.5 + // step 1: transform the tex coords to the grid size by multiplying with grid size + // -> e.g. original x coord 0.8 turns to 0.8 * 3 = 2.4 + // step 2: figure out the grid cell by taking the integer part of the transformed tex coords + // -> e.g. 2.4 turns to 2 + // step 3: subtract the integer part from the transformed tex coords to get the texel coords in the grid cell + // -> e.g. 2.4 - 2 = 0.4 -> final texel coords in the grid cell are 0.4, 0.5 + } float2 cellCoordinates = float2(i.uv.x, 1.0 - i.uv.y); // flip y coordiate to have cell index 0 in upper left corner cellCoordinates = float2(cellCoordinates.x * grid_size_x, cellCoordinates.y * grid_size_y); uint viewIndex = uint(cellCoordinates.x) + grid_size_x * uint(cellCoordinates.y); // texcoords in this texcels coordinate system (from 0 - 1) float2 actualTexCoords = float2(cellCoordinates.x - float(int(cellCoordinates.x)), cellCoordinates.y - float(int(cellCoordinates.y))); actualTexCoords.y = 1.0 - actualTexCoords.y; // flip y coordinate to match original tex coords - - uint gridCount = grid_size_x * grid_size_y; + // first and last image in the grid are the left and right camera + uint gridCount = grid_size_x * grid_size_y; if (viewIndex == 0) { return texture1.Sample(samplertexture1, actualTexCoords); // sample the left camera texture } if (viewIndex == gridCount - 1) { return texture0.Sample(samplertexture0, actualTexCoords); // sample the right camera texture } + + // ----------------------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------------------- + // ----------------------------------------------------------------------------------------------------------- float disparityStep = maxDisparity / gridCount; + + float actualDepth = sampleLeftDepthProjection(actualTexCoords); // sample the depth of the current texel in the left camera float distLayerFocusPlane = -(layer - focusDistance); // distance between the layer and the focus plane float leftOffset = viewIndex * disparityStep; // distance between the original left camera and the current view - float DLeft = computeClipSpaceOffset(leftOffset, focusDistance, distLayerFocusPlane, leftProjMatrix); // calculate offset of layer in pixel - - float rightOffset = (gridCount - 1 - viewIndex) * disparityStep; // distance between the original left camera and the current view - float DRight = computeClipSpaceOffset(rightOffset, focusDistance, distLayerFocusPlane, rightProjMatrix); // calculate offset of layer in pixel - + float DLeft = calculateOffset(layer, leftOffset, actualTexCoords); // convert to pixel space + float2 texCoordShiftedLeft = actualTexCoords; texCoordShiftedLeft.x += DLeft; - float shiftedLeftDepth = sampleLeftDepth(texCoordShiftedLeft); + float shiftedLeftDepth = sampleLeftDepthProjection(texCoordShiftedLeft); + + + float rightOffset = (gridCount - 1 - viewIndex) * disparityStep; // distance between the original left camera and the current view + float DRight = computeClipSpaceOffset(rightOffset, focusDistance, distLayerFocusPlane, rightProjMatrix); // calculate offset of layer in pixel float2 texCoordShiftedRight = actualTexCoords; texCoordShiftedRight.x -= DRight; float shiftedRightDepth = sampleRightDepth(texCoordShiftedRight); From f08395d3c65c845df483afe14c6c65cc791c7c49 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 22 Jul 2025 15:34:01 +0200 Subject: [PATCH 009/149] try to generate disparity based on depth map --- G3DCamera.cs | 190 ++++++------- Resources/G3DViewGeneration.shader | 392 ++++++++++++++++----------- Scripts/G3DHDRPViewGenerationPass.cs | 147 +++++++++- 3 files changed, 455 insertions(+), 274 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 65d2acf..c6e0b85 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -374,41 +374,9 @@ void Start() ); viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); - viewGenerationMaterial.SetInt(Shader.PropertyToID("depth_layer_discretization"), 1024); - viewGenerationMaterial.SetFloat(Shader.PropertyToID("crop"), 0.01f); viewGenerationMaterial.SetFloat(Shader.PropertyToID("maxDisparity"), viewSeparation * 15); // TODO do not hardcode this value - float focalLengthInPixel = - (mainCamera.pixelHeight / 2) - / (Mathf.Tan(mainCamera.fieldOfView / 2 * Mathf.Deg2Rad) * Mathf.Rad2Deg); - viewGenerationMaterial.SetFloat( - Shader.PropertyToID("focalLengthInPixel"), - focalLengthInPixel - ); viewGenerationMaterial.SetFloat(Shader.PropertyToID("nearPlane"), mainCamera.nearClipPlane); viewGenerationMaterial.SetFloat(Shader.PropertyToID("farPlane"), mainCamera.farClipPlane); - float viewRange = mainCamera.farClipPlane - mainCamera.nearClipPlane; - float stepSize = viewRange / 1024.0f; - viewGenerationMaterial.SetFloat(Shader.PropertyToID("layerDistance"), stepSize); - viewGenerationMaterial.SetInteger( - Shader.PropertyToID("cameraPixelWidth"), - mainCamera.pixelWidth - ); - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("inverseLeftProjMatrix"), - cameras[1].projectionMatrix.inverse - ); - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("inverseRightProjMatrix"), - cameras[0].projectionMatrix.inverse - ); - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("leftProjMatrix"), - cameras[1].projectionMatrix - ); - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("rightProjMatrix"), - cameras[0].projectionMatrix - ); viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; viewGenerationPass.materialPassName = "G3DViewGeneration"; @@ -416,25 +384,8 @@ void Start() RTHandle rtHandleDepthRight = RTHandles.Alloc(depthMapRight); viewGenerationPass.leftDepthMapHandle = rtHandleDepthLeft; viewGenerationPass.rightDepthMapHandle = rtHandleDepthRight; - - // add autostereo generation pass - // customPass = - // customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) - // as G3DHDRPViewGenerationMosaicPass; - // RenderTexture mosaicTexture = new RenderTexture( - // mainCamera.pixelWidth, - // mainCamera.pixelHeight, - // 0 - // ) - // { - // format = RenderTextureFormat.ARGB32, - // depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, - // }; - // RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); - // material.SetTexture("mosaictexture", mosaicTexture, RenderTextureSubElement.Color); - // customPass.fullscreenPassMaterial = material; - // customPass.materialPassName = "G3DFullScreen3D"; - // customPass.mosaicImageHandle = rtHandleMosaic; + viewGenerationPass.cameras = cameras; + viewGenerationPass.internalCameraCount = internalCameraCount; antialiasingMode = mainCamera.GetComponent().antialiasing; #endif @@ -501,7 +452,9 @@ void OnValidate() CalibrationProvider calibration = CalibrationProvider.getFromString( calibrationFile.text ); - internalCameraCount = getCameraCountFromCalibrationFile(calibration); + // internalCameraCount = getCameraCountFromCalibrationFile(calibration); + // TODO DO NOT HARD CODE THIS VALUE! + internalCameraCount = 16; loadMultiviewViewSeparationFromCalibration(calibration); } else @@ -632,7 +585,9 @@ public void setupCameras() if (mode == G3DCameraMode.MULTIVIEW) { loadMultiviewViewSeparationFromCalibration(calibration); - internalCameraCount = NativeViewcount; + // TODO DO NOT HARD CODE THIS VALUE! + internalCameraCount = 16; // default value for multiview mode + // internalCameraCount = NativeViewcount; } else { @@ -917,33 +872,26 @@ void Update() updateShaderRenderTextures(); } - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("inverseLeftProjMatrix"), - cameras[1].projectionMatrix.inverse - ); viewGenerationMaterial.SetMatrix( Shader.PropertyToID("inverseRightProjMatrix"), cameras[0].projectionMatrix.inverse ); - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("leftViewMatrix"), - cameras[1].worldToCameraMatrix.inverse - ); - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("leftProjMatrix"), - cameras[1].projectionMatrix - ); viewGenerationMaterial.SetMatrix( Shader.PropertyToID("rightProjMatrix"), cameras[0].projectionMatrix ); + viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("mainCameraProjectionMatrix"), - mainCamera.projectionMatrix + Shader.PropertyToID("leftViewMatrix"), + cameras[internalCameraCount - 1].worldToCameraMatrix ); viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("inverseMainCameraViewMatrix"), - mainCamera.worldToCameraMatrix.inverse + Shader.PropertyToID("leftProjMatrix"), + cameras[internalCameraCount - 1].projectionMatrix + ); + viewGenerationMaterial.SetMatrix( + Shader.PropertyToID("inverseLeftProjMatrix"), + cameras[internalCameraCount - 1].projectionMatrix.inverse ); } @@ -1135,7 +1083,23 @@ void updateCameras() camera.transform.localPosition = new Vector3(localCameraOffset, 0, 0); - camera.gameObject.SetActive(true); + // if generate views only enable the leftmost and rightmost camera + if (generateViews) + { + if (i == 0 || i == internalCameraCount - 1) + { + camera.gameObject.SetActive(true); + } + else + { + camera.gameObject.SetActive(false); + } + } + else + { + // enable all cameras + camera.gameObject.SetActive(true); + } } //disable all the other cameras, we are not using them with this cameracount @@ -1158,17 +1122,13 @@ private bool updateCameraCountBasedOnMode() } else if (mode == G3DCameraMode.MULTIVIEW) { - internalCameraCount = getCameraCountFromCalibrationFile(); + // internalCameraCount = getCameraCountFromCalibrationFile(); + // TODO DO NOT HARD CODE THIS VALUE! + internalCameraCount = 16; if (internalCameraCount > MAX_CAMERAS) { internalCameraCount = MAX_CAMERAS; } - - // TODO check if it is a good idea to simply set this to one. what if someone wants only one view? - if (generateViews) - { - internalCameraCount = 2; - } } if (internalCameraCount != previousCameraCount) @@ -1179,6 +1139,44 @@ private bool updateCameraCountBasedOnMode() return false; } + private void addRenderTextureToCamera( + RenderTexture[] renderTextures, + int renderTextureIndex, + int cameraIndex + ) + { + int width = Screen.width; + int height = Screen.height; + + if (adaptRenderResolutionToViews) + { + // TODO: This is a temporary fix for the resolution scaling issue. Use an actually correct formula here. + width = width / internalCameraCount; + } + else + { + width = (int)(width * ((float)renderResolutionScale / 100f)); + height = (int)(height * ((float)renderResolutionScale / 100f)); + } + + renderTextures[renderTextureIndex] = new RenderTexture(width, height, 0) + { + format = RenderTextureFormat.ARGB32, + depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, + }; + cameras[cameraIndex].targetTexture = renderTextures[renderTextureIndex]; + material.SetTexture( + "texture" + renderTextureIndex, + renderTextures[renderTextureIndex], + RenderTextureSubElement.Color + ); + viewGenerationMaterial.SetTexture( + "texture" + renderTextureIndex, + renderTextures[renderTextureIndex], + RenderTextureSubElement.Color + ); + } + public void updateShaderRenderTextures() { if (material == null) @@ -1192,35 +1190,18 @@ public void updateShaderRenderTextures() RenderTexture[] renderTextures = new RenderTexture[internalCameraCount]; - //set only those we need - for (int i = 0; i < internalCameraCount; i++) + if (generateViews) { - int width = Screen.width; - int height = Screen.height; - - if (adaptRenderResolutionToViews) - { - // TODO: This is a temporary fix for the resolution scaling issue. Use an actually correct formula here. - width = width / internalCameraCount; - } - else + addRenderTextureToCamera(renderTextures, 0, 0); // right camera + addRenderTextureToCamera(renderTextures, 1, internalCameraCount - 1); // left camera + } + else + { + //set only those we need + for (int i = 0; i < internalCameraCount; i++) { - width = (int)(width * ((float)renderResolutionScale / 100f)); - height = (int)(height * ((float)renderResolutionScale / 100f)); + addRenderTextureToCamera(renderTextures, i, i); } - - renderTextures[i] = new RenderTexture(width, height, 0) - { - format = RenderTextureFormat.ARGB32, - depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, - }; - cameras[i].targetTexture = renderTextures[i]; - material.SetTexture("texture" + i, renderTextures[i], RenderTextureSubElement.Color); - viewGenerationMaterial.SetTexture( - "texture" + i, - renderTextures[i], - RenderTextureSubElement.Color - ); } } @@ -1600,9 +1581,8 @@ int tmpCameraCount currentView += 1; } - // TODO remove the * 8 multiplier // it is used to scale the offset to the ends for a mosaic texture where the middle textures are missing - float offset = currentView * 8 * targetEyeSeparation; + float offset = currentView * targetEyeSeparation; // when the camera count is even, one camera is placed half the eye separation to the right of the center // same for the other to the left diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index 95d9e0e..507714f 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -1,9 +1,5 @@ Shader "G3D/ViewGeneration" { - Properties - { - DepthMap ("Texture", 2D) = "green" {} - } SubShader { PackageRequirements @@ -28,20 +24,78 @@ Shader "G3D/ViewGeneration" #pragma vertex vert #pragma fragment fragHDRP + #pragma require 2darray + + Texture2DArray _DepthMaps; + SamplerState sampler_DepthMaps; + Texture2D _depthMapRight; SamplerState sampler_depthMapRight; + Texture2D _depthMap0; + SamplerState sampler_depthMap0; + Texture2D _depthMap1; + SamplerState sampler_depthMap1; + Texture2D _depthMap2; + SamplerState sampler_depthMap2; + Texture2D _depthMap3; + SamplerState sampler_depthMap3; + Texture2D _depthMap4; + SamplerState sampler_depthMap4; + Texture2D _depthMap5; + SamplerState sampler_depthMap5; + Texture2D _depthMap6; + SamplerState sampler_depthMap6; + Texture2D _depthMap7; + SamplerState sampler_depthMap7; + Texture2D _depthMap8; + SamplerState sampler_depthMap8; + Texture2D _depthMap9; + SamplerState sampler_depthMap9; + + Texture2D _depthMapLeft; SamplerState sampler_depthMapLeft; - float4x4 rightProjMatrix; float4x4 inverseRightProjMatrix; - float4x4 leftProjMatrix; + float4x4 rightProjMatrix; + float4x4 inverseLeftProjMatrix; - float4x4 mainCameraProjectionMatrix; - float4x4 inverseMainCameraViewMatrix; float4x4 leftViewMatrix; + float4x4 leftProjMatrix; + + float4x4 inverseProjMatrix1; + float4x4 inverseViewMatrix1; + float4x4 inverseProjMatrix2; + float4x4 inverseViewMatrix2; + float4x4 inverseProjMatrix3; + float4x4 inverseViewMatrix3; + float4x4 inverseProjMatrix4; + float4x4 inverseViewMatrix4; + float4x4 inverseProjMatrix5; + float4x4 inverseViewMatrix5; + float4x4 inverseProjMatrix6; + float4x4 inverseViewMatrix6; + float4x4 inverseProjMatrix7; + float4x4 inverseViewMatrix7; + float4x4 inverseProjMatrix8; + float4x4 inverseViewMatrix8; + float4x4 inverseProjMatrix9; + float4x4 inverseViewMatrix9; + float4x4 inverseProjMatrix10; + float4x4 inverseViewMatrix10; + float4x4 inverseProjMatrix11; + float4x4 inverseViewMatrix11; + float4x4 inverseProjMatrix12; + float4x4 inverseViewMatrix12; + float4x4 inverseProjMatrix13; + float4x4 inverseViewMatrix13; + float4x4 inverseProjMatrix14; + float4x4 inverseViewMatrix14; + float4x4 inverseProjMatrix15; + float4x4 inverseViewMatrix15; + // 0 = right camera, 1 = left camera Texture2D texture0; @@ -49,13 +103,6 @@ Shader "G3D/ViewGeneration" Texture2D texture1; SamplerState samplertexture1; - float layer; // range 0..1 - float focusDistance; // distance to focus plane in camera space - - int depth_layer_discretization; // max layer amount -> 1024 - - float layerDistance = 1.0f; // distance between layers in world space - float maxDisparity = 1.5f; // range 0..2 disparity between left and right camera float nearPlane = 0; // scene min depth value -> set to nearPlane float farPlane = 1; // scene max depth value -> set to farPlane @@ -63,12 +110,6 @@ Shader "G3D/ViewGeneration" int grid_size_x; int grid_size_y; - float focalLengthInPixel = 600; - - int cameraPixelWidth = 1920; - - - struct VertAttributes { uint vertexID : SV_VertexID; @@ -115,109 +156,129 @@ Shader "G3D/ViewGeneration" return -NDC.z / NDC.w; // devide by w to get depth in view space } - float computeClipSpaceOffset(float cameraDistance, float focusDist, float distLayerFocusPlane, float4x4 projectionMatrix) { - float y = (cameraDistance / focusDist) * distLayerFocusPlane; - float4 offsetClipSpace = float4(y, 0.0f, 0.0f, 1.0f); // offset in clip space - offsetClipSpace = mul(offsetClipSpace, projectionMatrix); // apply projection matrix to get clip space coordinates - return offsetClipSpace.x; - } - - - float sampleLeftDepthProjection(float2 uv) { + float sampleLeftDepth(float2 uv) { float4 depthSample = _depthMapLeft.Sample(sampler_depthMapLeft, uv); - float depth = Linear01Depth(depthSample.r, _ZBufferParams); // convert depth from logarithmic scale to linear scale - float x = uv.x * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates - float y = uv.y * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates - float4 NDC = float4(x, y, depth, 1.0f); - NDC = mul(inverseLeftProjMatrix, NDC); // apply inverse projection matrix to get clip space coordinates - return -NDC.z / NDC.w; // devide by w to get depth in view space + float test = -1.0f + farPlane / nearPlane; + float4 myZBufferParams = float4(test, 1.0f, test / farPlane, 1.0f / farPlane); + float depth = LinearEyeDepth(depthSample.r, myZBufferParams); // convert depth from logarithmic scale to linear scale + // float centerDepth = depth * 2.0f - 1.0f; + // float x = uv.x * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates + // float y = uv.y * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates + // float4 NDC = float4(x, y, centerDepth, 1.0f); + // NDC = mul(inverseLeftProjMatrix, NDC); // apply inverse projection matrix to get clip space coordinates + + // float finalDepth = abs(NDC.z / NDC.w); // devide by w to get depth in view space + return depth; // devide by w to get depth in view space } - float4x4 getIdentityMatrix() { - return float4x4( - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - ); + float4x4 getInverseProjectionMatrix(int cameraIndex) { + switch (cameraIndex) { + case 1: return inverseProjMatrix1; + case 2: return inverseProjMatrix2; + case 3: return inverseProjMatrix3; + case 4: return inverseProjMatrix4; + case 5: return inverseProjMatrix5; + case 6: return inverseProjMatrix6; + case 7: return inverseProjMatrix7; + case 8: return inverseProjMatrix8; + case 9: return inverseProjMatrix9; + case 10: return inverseProjMatrix10; + case 11: return inverseProjMatrix11; + case 12: return inverseProjMatrix12; + case 13: return inverseProjMatrix13; + case 14: return inverseProjMatrix14; + case 15: return inverseProjMatrix15; + default: return float4x4(1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f); + } } - float4x4 inverse(float4x4 m) { - float n11 = m[0][0], n12 = m[1][0], n13 = m[2][0], n14 = m[3][0]; - float n21 = m[0][1], n22 = m[1][1], n23 = m[2][1], n24 = m[3][1]; - float n31 = m[0][2], n32 = m[1][2], n33 = m[2][2], n34 = m[3][2]; - float n41 = m[0][3], n42 = m[1][3], n43 = m[2][3], n44 = m[3][3]; - - float t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44; - float t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44; - float t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44; - float t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; - - float det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; - float idet = 1.0f / det; - - float4x4 ret; - - ret[0][0] = t11 * idet; - ret[0][1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * idet; - ret[0][2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * idet; - ret[0][3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * idet; - - ret[1][0] = t12 * idet; - ret[1][1] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * idet; - ret[1][2] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * idet; - ret[1][3] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * idet; - - ret[2][0] = t13 * idet; - ret[2][1] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * idet; - ret[2][2] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * idet; - ret[2][3] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * idet; - - ret[3][0] = t14 * idet; - ret[3][1] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * idet; - ret[3][2] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * idet; - ret[3][3] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * idet; - - return ret; + float4x4 getInverseViewMatrix(int cameraIndex) { + switch (cameraIndex) { + case 1: return inverseViewMatrix1; + case 2: return inverseViewMatrix2; + case 3: return inverseViewMatrix3; + case 4: return inverseViewMatrix4; + case 5: return inverseViewMatrix5; + case 6: return inverseViewMatrix6; + case 7: return inverseViewMatrix7; + case 8: return inverseViewMatrix8; + case 9: return inverseViewMatrix9; + case 10: return inverseViewMatrix10; + case 11: return inverseViewMatrix11; + case 12: return inverseViewMatrix12; + case 13: return inverseViewMatrix13; + case 14: return inverseViewMatrix14; + case 15: return inverseViewMatrix15; + default: return float4x4(1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f); + } } - float4x4 calculateProjectionMatrix(float localCameraOffset) { - // horizontal obliqueness - float horizontalObl = -localCameraOffset / focusDistance; + // Set the texture array as a shader input + float getCameraDepth(float2 uv, int cameraIndex) { + float zIndex = 1.0f/ 16.0f * cameraIndex; // calculate the z index for the texture array + + float4 depthSample; + switch(cameraIndex) { + case 0: + depthSample = _depthMap0.Sample( sampler_depthMap0, uv); + break; + case 1: + depthSample = _depthMap1.Sample( sampler_depthMap1, uv); + break; + case 2: + depthSample = _depthMap2.Sample( sampler_depthMap2, uv); + break; + case 3: + depthSample = _depthMap3.Sample( sampler_depthMap3, uv); + break; + case 4: + depthSample = _depthMap4.Sample( sampler_depthMap4, uv); + break; + case 5: + depthSample = _depthMap5.Sample( sampler_depthMap5, uv); + break; + case 6: + depthSample = _depthMap6.Sample( sampler_depthMap6, uv); + break; + case 7: + depthSample = _depthMap7.Sample( sampler_depthMap7, uv); + break; + case 8: + depthSample = _depthMap8.Sample( sampler_depthMap8, uv); + break; + case 9: + depthSample = _depthMap9.Sample( sampler_depthMap9, uv); + break; + default: + depthSample = _depthMapLeft.Sample(sampler_depthMapLeft, uv); // use left camera depth map as default + break; + } - // focus distance is in view space. Writing directly into projection matrix would require focus distance to be in projection space - float4x4 shearMatrix = getIdentityMatrix(); - shearMatrix[0, 2] = horizontalObl; - // apply new projection matrix - return mul(mainCameraProjectionMatrix, shearMatrix); + float test = -1.0f + farPlane / nearPlane; + float4 myZBufferParams = float4(test, 1.0f, test / farPlane, 1.0f / farPlane); + return LinearEyeDepth(depthSample.r, myZBufferParams); // convert depth from logarithmic scale to linear scale } - float calculateOffset(float layerDist, float cameraOffset, float2 uv) { - // erst punkt in clip space berechnen - // dann mit inverse projection matrix in view space umwandeln - // dann mit inverse view matrix in world space umwandeln - // dann mit view matrix der linken kamera in view space umwandeln - // dann mit projection matrix der linken kamera in clip space umwandeln - // dann die x koordinaten der beiden punkte vergleichen und den unterschied nehmen - - float tmp = map(layerDist, 0.0f, farPlane, 0.0f, 1.0f); // convert layer distance from [nearPlane, farPlane] to [0,1] - float4 p = float4(0.0f, 0.0f, tmp, 1.0f); // point in view space - float4x4 cameraProjectionMatrix = calculateProjectionMatrix(cameraOffset); - float4x4 inverseCameraProjectionMatrix = inverse(cameraProjectionMatrix); // inverse projection matrix to convert from clip space to view space - float4x4 inverseCameraViewMatrix = inverseMainCameraViewMatrix; // inverse view matrix to convert from view space to world space - // shift view inverse view matrix to the left - inverseMainCameraViewMatrix[0][3] += cameraOffset; // shift the view matrix to the left by the camera offset - p = mul(inverseCameraProjectionMatrix, p); // convert from clip space to view space - p = mul(inverseCameraViewMatrix, p); // convert from view space to world space + float calculateOffset(float2 uv, int cameraIndex) { + float depth = getCameraDepth(uv, cameraIndex); + + float4 p = float4(0.0f, 0.0f, depth, 1.0f); // point in view space + p = mul(getInverseProjectionMatrix(cameraIndex), p); // convert from clip space to view space + p *= depth; // multiply by layer depth to get correct depth value in view space (layer is the correct w component) + p = mul(getInverseViewMatrix(cameraIndex), p); // convert from view space to world space // p now in world space p = mul(leftViewMatrix, p); // apply left view matrix to get shifted point in view space p = mul(leftProjMatrix, p); // apply main camera projection matrix to get clip space coordinates - float clipSpaceX = p.x / p.w; // convert to clip space by dividing by w - clipSpaceX = clipSpaceX * 0.5f + 0.5f; // convert from [-1,1] to [0,1] to get texture coordinates - // clipSpaceX = map(clipSpaceX, -1.0f, 1.0f, 0.0f, 1.0f); // convert from [-1,1] to [0,1] to get texture coordinates - + float clipSpaceX = -p.x / p.w; // convert to clip space by dividing by w + clipSpaceX = clipSpaceX / 2.0f; // devide by 2 to get from clip space [-1, 1] to texture coordinates [0, 1] return clipSpaceX; // return the difference between the shifted and original x coordinate } @@ -229,8 +290,10 @@ Shader "G3D/ViewGeneration" /// float4 fragHDRP (v2f i) : SV_Target { - // float depth = sampleLeftDepthProjection(i.uv); - // depth = map(depth, nearPlane, farPlane, 0, 1); + // float depth = sampleLeftDepth(i.uv); + // float depth = getCameraDepth(i.uv, 0); + // depth = depth/ 10.0f; // scale depth to a smaller range for visualization + // // depth = map(depth, 0, farPlane, 0, 1); // return float4(depth, depth, depth, 1.0f); // return depth value in world space { @@ -264,6 +327,12 @@ Shader "G3D/ViewGeneration" return texture0.Sample(samplertexture0, actualTexCoords); // sample the right camera texture } + // float depth = getCameraDepth(actualTexCoords, viewIndex); + // depth = depth/ 10.0f; // scale depth to a smaller range for visualization + // // depth = map(depth, 0, farPlane, 0, 1); + // return float4(depth, depth, depth, 1.0f); // return depth value in world space + + // ----------------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------------- @@ -271,63 +340,74 @@ Shader "G3D/ViewGeneration" float disparityStep = maxDisparity / gridCount; - float actualDepth = sampleLeftDepthProjection(actualTexCoords); // sample the depth of the current texel in the left camera - - float distLayerFocusPlane = -(layer - focusDistance); // distance between the layer and the focus plane - float leftOffset = viewIndex * disparityStep; // distance between the original left camera and the current view - float DLeft = calculateOffset(layer, leftOffset, actualTexCoords); // convert to pixel space + float DLeft = calculateOffset(actualTexCoords, viewIndex); // convert to pixel space - float2 texCoordShiftedLeft = actualTexCoords; - texCoordShiftedLeft.x += DLeft; - float shiftedLeftDepth = sampleLeftDepthProjection(texCoordShiftedLeft); - - + texCoordShiftedLeft.x -= DLeft; - float rightOffset = (gridCount - 1 - viewIndex) * disparityStep; // distance between the original left camera and the current view - float DRight = computeClipSpaceOffset(rightOffset, focusDistance, distLayerFocusPlane, rightProjMatrix); // calculate offset of layer in pixel - float2 texCoordShiftedRight = actualTexCoords; - texCoordShiftedRight.x -= DRight; - float shiftedRightDepth = sampleRightDepth(texCoordShiftedRight); - - int leftFills = 1; - if(texCoordShiftedLeft.x < 0 || texCoordShiftedLeft.x > 1.0f) { - leftFills = 0; // discard if the tex coord is out of bounds - } - if (abs((layer - shiftedLeftDepth)) > layerDistance) { - leftFills = 0; // discard if the layer is too far away from the shifted left depth - } - + // if(texCoordShiftedLeft.x < 0 || texCoordShiftedLeft.x > 1.0f) { + // discard; // discard if the tex coord is out of bounds + // } - int rightFills = 0; - if(texCoordShiftedRight.x < 0 || texCoordShiftedRight.x > 1.0f) { - rightFills = 0; // discard if the tex coord is out of bounds - } - if (abs((layer - shiftedRightDepth)) > layerDistance) { - rightFills = 0; // discard if the layer is too far away from the shifted right depth - } + float shiftedLeftDepth = sampleLeftDepth(texCoordShiftedLeft); + + float actualDepth = sampleLeftDepth(actualTexCoords); // get the actual depth of the current texel - if (leftFills == 0 && rightFills == 0) { - discard; // discard if both left and right camera do not fill the layer - } - if (leftFills == 1 && rightFills == 0) { - return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture + // if(abs((actualDepth - shiftedLeftDepth)) > 0.1f) { + // discard; // discard if the layer is too far away from the shifted left depth + // } + if( DLeft < 0.0f) { + return float4(0.0f, -DLeft, 0.0f, 1.0f); // return a debug value if the offset is negative } - if (leftFills == 0 && rightFills == 1) { - return texture0.Sample(samplertexture0, texCoordShiftedRight); // sample the right camera texture + else { + return float4(DLeft, 0.0f, 0.0f, 1.0f); // return a debug value if the offset is too large } - if(shiftedLeftDepth < shiftedRightDepth) { - return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture - } + return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture - return texture0.Sample(samplertexture0, texCoordShiftedRight); // sample the right camera texture + + // ----------------- + // handle left and right cameras + // ----------------- + // float rightOffset = (gridCount - 1 - viewIndex) * disparityStep; // distance between the original left camera and the current view + // float DRight = calculateOffset(rightOffset, focusDistance, distLayerFocusPlane, rightProjMatrix); // calculate offset of layer in pixel + // float2 texCoordShiftedRight = actualTexCoords; + // texCoordShiftedRight.x -= DRight; + // float shiftedRightDepth = sampleRightDepth(texCoordShiftedRight); - // if (DLeft < 0.0f) { - // return float4(0.0f, -DLeft, 0.0f, 1.0f); + // int leftFills = 1; + // if(texCoordShiftedLeft.x < 0 || texCoordShiftedLeft.x > 1.0f) { + // leftFills = 0; // discard if the tex coord is out of bounds + // } + // if (abs((layer - shiftedLeftDepth)) > layerDistance) { + // leftFills = 0; // discard if the layer is too far away from the shifted left depth + // } + + + // int rightFills = 0; + // if(texCoordShiftedRight.x < 0 || texCoordShiftedRight.x > 1.0f) { + // rightFills = 0; // discard if the tex coord is out of bounds + // } + // if (abs((layer - shiftedRightDepth)) > layerDistance) { + // rightFills = 0; // discard if the layer is too far away from the shifted right depth + // } + + // if (leftFills == 0 && rightFills == 0) { + // discard; // discard if both left and right camera do not fill the layer + // } + // if (leftFills == 1 && rightFills == 0) { + // return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture // } - // return float4(DLeft, 0.0f, 0.0f, 1.0f); + // if (leftFills == 0 && rightFills == 1) { + // return texture0.Sample(samplertexture0, texCoordShiftedRight); // sample the right camera texture + // } + + // if(shiftedLeftDepth < shiftedRightDepth) { + // return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture + // } + + // return texture0.Sample(samplertexture0, texCoordShiftedRight); // sample the right camera texture } ENDHLSL } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index e47f939..6f0b638 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -1,4 +1,5 @@ #if G3D_HDRP +using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; @@ -13,7 +14,82 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public float focusDistance = 1.0f; - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } + public List cameras; + public int internalCameraCount = 16; + + RenderTexture depthTexturesArray; + + RenderTexture[] indivDepthTextures; + + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) + { + // Initialize the depth textures + // depthTexturesArray = new RenderTexture(1024, 1024, 24, RenderTextureFormat.Depth); + // depthTexturesArray.dimension = TextureDimension.Tex2DArray; + // depthTexturesArray.volumeDepth = internalCameraCount; + // depthTexturesArray.Create(); + + // fullscreenPassMaterial.SetTexture( + // "_DepthMaps", + // depthTexturesArray, + // RenderTextureSubElement.Depth + // ); + + indivDepthTextures = new RenderTexture[10]; + for (int i = 0; i < 10; i++) + { + RenderTexture depthTexture = new RenderTexture( + 1024, + 1024, + 24, + RenderTextureFormat.Depth + ); + depthTexture.dimension = TextureDimension.Tex2D; + depthTexture.Create(); + indivDepthTextures[i] = depthTexture; + fullscreenPassMaterial.SetTexture( + "_depthMap" + i, + depthTexture, + RenderTextureSubElement.Depth + ); + } + } + + float map(float x, float in_min, float in_max, float out_min, float out_max) + { + // Convert the current value to a percentage + // 0% - min1, 100% - max1 + float perc = (x - in_min) / (in_max - in_min); + + // Do the same operation backwards with min2 and max2 + float value = perc * (out_max - out_min) + out_min; + return value; + } + + private float layerOffset(float layer, float farPlane) + { + float tmp = map(layer, 0.0f, farPlane, 0, 1.0f); // convert layer distance from [nearPlane, farPlane] to [0,1] + Vector4 p = new Vector4(0.0f, 0.0f, tmp, layer); // point in view space + p = cameras[internalCameraCount - 2].projectionMatrix.inverse * p; // convert from clip space to view space + // p.x = p.x * layer; // convert from clip space to view space + // p.y = p.y * layer; // convert from clip space to view space + // p.z = p.z * layer; // convert from clip space to view space + p = cameras[internalCameraCount - 2].worldToCameraMatrix.inverse * p; // convert from view space to world space + + // p now in world space + + p = cameras[internalCameraCount - 1].worldToCameraMatrix * p; // apply left view matrix to get shifted point in view space + p = cameras[internalCameraCount - 1].projectionMatrix * p; // apply main camera projection matrix to get clip space coordinates + + p.x = p.x / p.w; // convert from clip space to view space + p.y = p.y / p.w; // convert from clip space to view space + p.z = p.z / p.w; // convert from clip space to view space + float clipSpaceX = -p.x / p.w / 2.0f; // convert to clip space by dividing by w + // clipSpaceX = clipSpaceX * 0.5f + 0.5f; // convert from [-1,1] to [0,1] to get texture coordinates + // clipSpaceX = map(clipSpaceX, -1.0f, 1.0f, 0.0f, 1.0f); // convert from [-1,1] to [0,1] to get texture coordinates + + return clipSpaceX; + } protected override void Execute(CustomPassContext ctx) { @@ -25,20 +101,65 @@ protected override void Execute(CustomPassContext ctx) float viewRange = camera.farClipPlane - camera.nearClipPlane; float stepSize = viewRange / 1024.0f; - for (int i = 1024; i > 0; i--) + // set the inverse projection matrix + ctx.propertyBlock.SetMatrix( + Shader.PropertyToID("inverseProjMatrix1"), + cameras[internalCameraCount - 2].projectionMatrix.inverse + ); + ctx.propertyBlock.SetMatrix( + Shader.PropertyToID("inverseViewMatrix1"), + cameras[internalCameraCount - 2].cameraToWorldMatrix + ); + + for (int i = 0; i < 10; i++) { - float layer = i * stepSize + camera.nearClipPlane; - ctx.propertyBlock.SetFloat(Shader.PropertyToID("layer"), layer); - ctx.propertyBlock.SetFloat(Shader.PropertyToID("focusDistance"), focusDistance); - - // sample left camera - CoreUtils.DrawFullScreen( - ctx.cmd, - fullscreenPassMaterial, - ctx.propertyBlock, - shaderPassId: 0 + Camera bakingCamera = cameras[15 - i]; + + // We need to be careful about the aspect ratio of render textures when doing the culling, otherwise it could result in objects poping: + bakingCamera.aspect = Mathf.Max( + bakingCamera.aspect, + indivDepthTextures[i].width / (float)indivDepthTextures[i].height + ); + bakingCamera.TryGetCullingParameters(out var cullingParams); + cullingParams.cullingOptions = CullingOptions.None; + + // Assign the custom culling result to the context + // so it'll be used for the following operations + ctx.cullingResults = ctx.renderContext.Cull(ref cullingParams); + var overrideDepthTest = new RenderStateBlock(RenderStateMask.Depth) + { + depthState = new DepthState(true, CompareFunction.LessEqual) + }; + CustomPassUtils.RenderDepthFromCamera( + ctx, + bakingCamera, + indivDepthTextures[i], + ClearFlag.Depth, + bakingCamera.cullingMask, + overrideRenderState: overrideDepthTest ); } + + for (int i = internalCameraCount - 2; i > 0; i--) + { + int idx = internalCameraCount - i - 1; + ctx.propertyBlock.SetMatrix( + Shader.PropertyToID("inverseProjMatrix" + idx), + cameras[i].projectionMatrix.inverse + ); + ctx.propertyBlock.SetMatrix( + Shader.PropertyToID("inverseViewMatrix" + idx), + cameras[i].cameraToWorldMatrix + ); + } + + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + CoreUtils.DrawFullScreen( + ctx.cmd, + fullscreenPassMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); } else if (isLeftCamera(camera)) { @@ -52,7 +173,7 @@ protected override void Execute(CustomPassContext ctx) bool isLeftCamera(Camera camera) { - return camera.name == "g3dcam_1"; + return camera.name == "g3dcam_" + (internalCameraCount - 1).ToString(); } bool isRightCamera(Camera camera) From a8630e404f6e99520e9dd09ce0e9a4a3dc0910b9 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 25 Jul 2025 16:05:00 +0200 Subject: [PATCH 010/149] first working version of view generation --- G3DCamera.cs | 212 +++++++++-------- Resources/G3DDepthMosaic.shader | 162 +++++++++++++ Resources/G3DDepthMosaic.shader.meta | 9 + Resources/G3DShaderMultiview.shader | 6 + Resources/G3DViewGeneration.shader | 304 +++++++------------------ Scripts/G3DHDRPDepthMapPrePass.cs | 120 ++++++++++ Scripts/G3DHDRPDepthMapPrePass.cs.meta | 11 + Scripts/G3DHDRPViewGenerationPass.cs | 190 ++++------------ 8 files changed, 538 insertions(+), 476 deletions(-) create mode 100644 Resources/G3DDepthMosaic.shader create mode 100644 Resources/G3DDepthMosaic.shader.meta create mode 100644 Scripts/G3DHDRPDepthMapPrePass.cs create mode 100644 Scripts/G3DHDRPDepthMapPrePass.cs.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index c6e0b85..27daeb6 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing.Drawing2D; using System.IO; using UnityEditor; using UnityEngine; @@ -193,8 +194,6 @@ public class G3DCamera private Material material; #if G3D_HDRP - private G3DHDRPViewGenerationMosaicPass customPass; - private G3DHDRPViewGenerationPass viewGenerationPass; private HDAdditionalCameraData.AntialiasingMode antialiasingMode = HDAdditionalCameraData .AntialiasingMode .None; @@ -250,7 +249,7 @@ private float scaledHalfCameraWidthAtStart #endregion - public bool generateViews = true; + private bool generateViews = true; private Material viewGenerationMaterial; // TODO Handle viewport resizing/ moving @@ -271,6 +270,19 @@ void Start() mainCamera.cullingMask = 0; //disable rendering of the main camera mainCamera.clearFlags = CameraClearFlags.Color; + reinitializeShader(); +#if G3D_HDRP + initCustomPass(); + + antialiasingMode = mainCamera.GetComponent().antialiasing; +#endif + +#if G3D_URP + customPass = new G3DUrpScriptableRenderPass(material); + antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing; + mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None; +#endif + shaderHandles = new ShaderHandles() { leftViewportPosition = Shader.PropertyToID("v_pos_x"), @@ -305,7 +317,6 @@ void Start() { initLibrary(); } - reinitializeShader(); updateScreenViewportProperties(); loadShaderParametersFromCalibrationFile(); @@ -323,79 +334,6 @@ void Start() } } -#if G3D_HDRP - // init fullscreen postprocessing for hd render pipeline - var customPassVolume = gameObject.AddComponent(); - customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; - customPassVolume.isGlobal = true; - // Make the volume invisible in the inspector - customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; - - // add multiview generation pass - viewGenerationPass = - customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationPass)) - as G3DHDRPViewGenerationPass; - viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); - RenderTexture depthMapLeft = new RenderTexture( - mainCamera.pixelWidth, - mainCamera.pixelHeight, - 0 - ) - { - format = RenderTextureFormat.Depth, - depthStencilFormat = UnityEngine - .Experimental - .Rendering - .GraphicsFormat - .D32_SFloat_S8_UInt, - }; - viewGenerationMaterial.SetTexture( - "_depthMapLeft", - depthMapLeft, - RenderTextureSubElement.Depth - ); - RenderTexture depthMapRight = new RenderTexture( - mainCamera.pixelWidth, - mainCamera.pixelHeight, - 0 - ) - { - format = RenderTextureFormat.Depth, - depthStencilFormat = UnityEngine - .Experimental - .Rendering - .GraphicsFormat - .D32_SFloat_S8_UInt, - }; - viewGenerationMaterial.SetTexture( - "_depthMapRight", - depthMapRight, - RenderTextureSubElement.Depth - ); - viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); - viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); - viewGenerationMaterial.SetFloat(Shader.PropertyToID("maxDisparity"), viewSeparation * 15); // TODO do not hardcode this value - viewGenerationMaterial.SetFloat(Shader.PropertyToID("nearPlane"), mainCamera.nearClipPlane); - viewGenerationMaterial.SetFloat(Shader.PropertyToID("farPlane"), mainCamera.farClipPlane); - - viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; - viewGenerationPass.materialPassName = "G3DViewGeneration"; - RTHandle rtHandleDepthLeft = RTHandles.Alloc(depthMapLeft); - RTHandle rtHandleDepthRight = RTHandles.Alloc(depthMapRight); - viewGenerationPass.leftDepthMapHandle = rtHandleDepthLeft; - viewGenerationPass.rightDepthMapHandle = rtHandleDepthRight; - viewGenerationPass.cameras = cameras; - viewGenerationPass.internalCameraCount = internalCameraCount; - - antialiasingMode = mainCamera.GetComponent().antialiasing; -#endif - -#if G3D_URP - customPass = new G3DUrpScriptableRenderPass(material); - antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing; - mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None; -#endif - updateCameras(); updateShaderRenderTextures(); @@ -407,9 +345,87 @@ void Start() cachedWindowSize = new Vector2Int(Screen.width, Screen.height); headPositionLog = new Queue(10000); + } + +#if G3D_HDRP + private void initCustomPass() + { + // init fullscreen postprocessing for hd render pipeline + CustomPassVolume customPassVolume = gameObject.AddComponent(); + customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; + customPassVolume.isGlobal = true; + // Make the volume invisible in the inspector + customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; - viewGenerationPass.focusDistance = scaledFocusDistanceAndDolly; + if (generateViews) + { + // add depth mosaic generation pass + G3DHDRPDepthMapPrePass depthMosaicPass = + customPassVolume.AddPassOfType(typeof(G3DHDRPDepthMapPrePass)) + as G3DHDRPDepthMapPrePass; + Material depthMosaicMaterial = new Material(Shader.Find("G3D/DepthMosaic")); + depthMosaicMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); + depthMosaicMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); + + depthMosaicPass.fullscreenPassMaterial = depthMosaicMaterial; + depthMosaicPass.materialPassName = "G3DDepthMosaic"; + depthMosaicPass.cameras = cameras; + depthMosaicPass.internalCameraCount = internalCameraCount; + + RenderTexture depthMosaicTexture = new RenderTexture( + mainCamera.pixelWidth * 4, + mainCamera.pixelHeight * 4, + 0 + ); + RTHandle depthMosaicHandle = RTHandles.Alloc(depthMosaicTexture); + depthMosaicPass.depthMosaicHandle = depthMosaicHandle; + + // add multiview generation pass + G3DHDRPViewGenerationPass viewGenerationPass = + customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationPass)) + as G3DHDRPViewGenerationPass; + viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); + viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); + viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); + viewGenerationMaterial.SetTexture( + Shader.PropertyToID("_depthMosaic"), + depthMosaicHandle + ); + + viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; + viewGenerationPass.materialPassName = "G3DViewGeneration"; + viewGenerationPass.cameras = cameras; + viewGenerationPass.internalCameraCount = internalCameraCount; + + // add autostereo mosaic generation pass + G3DHDRPViewGenerationMosaicPass customPass = + customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) + as G3DHDRPViewGenerationMosaicPass; + RenderTexture mosaicTexture = new RenderTexture( + mainCamera.pixelWidth, + mainCamera.pixelHeight, + 0 + ) + { + format = RenderTextureFormat.ARGB32, + depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, + }; + RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); + material.SetTexture("mosaictexture", rtHandleMosaic); + customPass.fullscreenPassMaterial = material; + customPass.materialPassName = "G3DFullScreen3D"; + + viewGenerationPass.mosaicImageHandle = rtHandleMosaic; + } + else + { + G3DHDRPCustomPass customPass = + customPassVolume.AddPassOfType(typeof(G3DHDRPCustomPass)) as G3DHDRPCustomPass; + customPass.fullscreenPassMaterial = material; + customPass.materialPassName = "G3DFullScreen3D"; + } } +#endif void OnApplicationQuit() { @@ -871,28 +887,6 @@ void Update() oldRenderResolutionScale = renderResolutionScale; updateShaderRenderTextures(); } - - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("inverseRightProjMatrix"), - cameras[0].projectionMatrix.inverse - ); - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("rightProjMatrix"), - cameras[0].projectionMatrix - ); - - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("leftViewMatrix"), - cameras[internalCameraCount - 1].worldToCameraMatrix - ); - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("leftProjMatrix"), - cameras[internalCameraCount - 1].projectionMatrix - ); - viewGenerationMaterial.SetMatrix( - Shader.PropertyToID("inverseLeftProjMatrix"), - cameras[internalCameraCount - 1].projectionMatrix.inverse - ); } private void updateScreenViewportProperties() @@ -1170,11 +1164,15 @@ int cameraIndex renderTextures[renderTextureIndex], RenderTextureSubElement.Color ); - viewGenerationMaterial.SetTexture( - "texture" + renderTextureIndex, - renderTextures[renderTextureIndex], - RenderTextureSubElement.Color - ); + + if (generateViews) + { + viewGenerationMaterial.SetTexture( + "texture" + renderTextureIndex, + renderTextures[renderTextureIndex], + RenderTextureSubElement.Color + ); + } } public void updateShaderRenderTextures() @@ -1595,10 +1593,10 @@ int tmpCameraCount { correctionTerm *= -1; } - return (offset + correctionTerm) * -1; + offset = offset + correctionTerm; } - int flip = mirrorViews ? 1 : -1; + int flip = mirrorViews ? -1 : 1; return offset * flip; } diff --git a/Resources/G3DDepthMosaic.shader b/Resources/G3DDepthMosaic.shader new file mode 100644 index 0000000..2514e68 --- /dev/null +++ b/Resources/G3DDepthMosaic.shader @@ -0,0 +1,162 @@ +Shader "G3D/DepthMosaic" +{ + SubShader + { + PackageRequirements + { + "com.unity.render-pipelines.high-definition": "unity=2021.3" + } + Tags { "RenderType"="Opaque" "RenderPipeline" = "HDRenderPipeline"} + + Pass + { + Name "G3DDepthMosaic" + + ZWrite Off + ZTest Always + Blend Off + Cull Off + + HLSLPROGRAM + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + + #pragma vertex vert + #pragma fragment fragHDRP + + + Texture2D _depthMap0; + SamplerState sampler_depthMap0; + Texture2D _depthMap1; + SamplerState sampler_depthMap1; + Texture2D _depthMap2; + SamplerState sampler_depthMap2; + Texture2D _depthMap3; + SamplerState sampler_depthMap3; + Texture2D _depthMap4; + SamplerState sampler_depthMap4; + Texture2D _depthMap5; + SamplerState sampler_depthMap5; + Texture2D _depthMap6; + SamplerState sampler_depthMap6; + Texture2D _depthMap7; + SamplerState sampler_depthMap7; + Texture2D _depthMap8; + SamplerState sampler_depthMap8; + Texture2D _depthMap9; + SamplerState sampler_depthMap9; + Texture2D _depthMap10; + SamplerState sampler_depthMap10; + Texture2D _depthMap11; + SamplerState sampler_depthMap11; + Texture2D _depthMap12; + SamplerState sampler_depthMap12; + Texture2D _depthMap13; + SamplerState sampler_depthMap13; + Texture2D _depthMap14; + SamplerState sampler_depthMap14; + Texture2D _depthMap15; + SamplerState sampler_depthMap15; + + int grid_size_x; + int grid_size_y; + + struct VertAttributes + { + uint vertexID : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct v2f + { + float2 uv : TEXCOORD0; + float4 screenPos : SV_POSITION; + }; + + v2f vert(VertAttributes input) + { + v2f output; + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + output.uv = GetFullScreenTriangleTexCoord(input.vertexID); + output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + + return output; + + } + + // Set the texture array as a shader input + float getCameraLogDepth(float2 uv, int cameraIndex) { + switch(cameraIndex) { + case 0: + return _depthMap0.Sample( sampler_depthMap0, uv).r; + case 1: + return _depthMap1.Sample( sampler_depthMap1, uv).r; + case 2: + return _depthMap2.Sample( sampler_depthMap2, uv).r; + case 3: + return _depthMap3.Sample( sampler_depthMap3, uv).r; + case 4: + return _depthMap4.Sample( sampler_depthMap4, uv).r; + case 5: + return _depthMap5.Sample( sampler_depthMap5, uv).r; + case 6: + return _depthMap6.Sample( sampler_depthMap6, uv).r; + case 7: + return _depthMap7.Sample( sampler_depthMap7, uv).r; + case 8: + return _depthMap8.Sample( sampler_depthMap8, uv).r; + case 9: + return _depthMap9.Sample( sampler_depthMap9, uv).r; + case 10: + return _depthMap10.Sample( sampler_depthMap10, uv).r; + case 11: + return _depthMap11.Sample( sampler_depthMap11, uv).r; + case 12: + return _depthMap12.Sample( sampler_depthMap12, uv).r; + case 13: + return _depthMap13.Sample( sampler_depthMap13, uv).r; + case 14: + return _depthMap14.Sample( sampler_depthMap14, uv).r; + case 15: + return _depthMap15.Sample( sampler_depthMap15, uv).r; + default: + return _depthMap0.Sample( sampler_depthMap0, uv).r; // use left camera depth map as default + } + + } + + /// + /// combines the original depthMaps into one depth map that contains all views + /// + float4 fragHDRP (v2f i) : SV_Target + { + { + // the text coords of the original left and right view are from 0 - 1 + // the tex coords of the texel we are currently rendering are also from 0 - 1 + // but we want to create a grid of views, so we need to transform the tex coords + // to the grid size. + // basically we want to figure out in which grid cell the current texel is, then convert the texel coords to the grid cell coords. + // example assuming a grid size of 3x3: + // original tex coords: 0.8, 0.5 + // step 1: transform the tex coords to the grid size by multiplying with grid size + // -> e.g. original x coord 0.8 turns to 0.8 * 3 = 2.4 + // step 2: figure out the grid cell by taking the integer part of the transformed tex coords + // -> e.g. 2.4 turns to 2 + // step 3: subtract the integer part from the transformed tex coords to get the texel coords in the grid cell + // -> e.g. 2.4 - 2 = 0.4 -> final texel coords in the grid cell are 0.4, 0.5 + } + float2 cellCoordinates = float2(i.uv.x, 1.0 - i.uv.y); // flip y coordiate to have cell index 0 in upper left corner + cellCoordinates = float2(cellCoordinates.x * grid_size_x, cellCoordinates.y * grid_size_y); + uint viewIndex = uint(cellCoordinates.x) + grid_size_x * uint(cellCoordinates.y); + // texcoords in this texcels coordinate system (from 0 - 1) + float2 actualTexCoords = float2(cellCoordinates.x - float(int(cellCoordinates.x)), cellCoordinates.y - float(int(cellCoordinates.y))); + actualTexCoords.y = 1.0 - actualTexCoords.y; // flip y coordinate to match original tex coords + + float depth = getCameraLogDepth(actualTexCoords, viewIndex); + return float4(depth, depth, depth, 1.0f); // return the depth value as color + } + ENDHLSL + } + } +} \ No newline at end of file diff --git a/Resources/G3DDepthMosaic.shader.meta b/Resources/G3DDepthMosaic.shader.meta new file mode 100644 index 0000000..e585f93 --- /dev/null +++ b/Resources/G3DDepthMosaic.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: dd136f9af67cd0149a6bfbee9674b42e +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/G3DShaderMultiview.shader b/Resources/G3DShaderMultiview.shader index e9da540..33e2a40 100644 --- a/Resources/G3DShaderMultiview.shader +++ b/Resources/G3DShaderMultiview.shader @@ -142,11 +142,17 @@ Shader "G3D/AutostereoMultiview" viewIndices.y = viewIndices.y % nativeViewCount; viewIndices.z = viewIndices.z % nativeViewCount; + // invert view indices cause the camera order was inverted + viewIndices.x = nativeViewCount - viewIndices.x - 1; + viewIndices.y = nativeViewCount - viewIndices.y - 1; + viewIndices.z = nativeViewCount - viewIndices.z - 1; return viewIndices; } float4 frag (v2f i) : SV_Target { + return sampleFromView(nativeViewCount - 0 - 1, i.uv); + float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index 507714f..1523957 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -24,89 +24,37 @@ Shader "G3D/ViewGeneration" #pragma vertex vert #pragma fragment fragHDRP - #pragma require 2darray - - Texture2DArray _DepthMaps; - SamplerState sampler_DepthMaps; - - Texture2D _depthMapRight; - SamplerState sampler_depthMapRight; - - - Texture2D _depthMap0; - SamplerState sampler_depthMap0; - Texture2D _depthMap1; - SamplerState sampler_depthMap1; - Texture2D _depthMap2; - SamplerState sampler_depthMap2; - Texture2D _depthMap3; - SamplerState sampler_depthMap3; - Texture2D _depthMap4; - SamplerState sampler_depthMap4; - Texture2D _depthMap5; - SamplerState sampler_depthMap5; - Texture2D _depthMap6; - SamplerState sampler_depthMap6; - Texture2D _depthMap7; - SamplerState sampler_depthMap7; - Texture2D _depthMap8; - SamplerState sampler_depthMap8; - Texture2D _depthMap9; - SamplerState sampler_depthMap9; - - - Texture2D _depthMapLeft; - SamplerState sampler_depthMapLeft; - - float4x4 inverseRightProjMatrix; - float4x4 rightProjMatrix; - - float4x4 inverseLeftProjMatrix; - float4x4 leftViewMatrix; - float4x4 leftProjMatrix; + // 0 = left camera, 1 = right camera + Texture2D texture0; + SamplerState samplertexture0; + Texture2D texture1; + SamplerState samplertexture1; + + Texture2D _depthMosaic; + SamplerState sampler_depthMosaic; + + + float4x4 rightViewProjMatrix; + float4x4 leftViewProjMatrix; + float4x4 inverseProjMatrix0; float4x4 inverseProjMatrix1; - float4x4 inverseViewMatrix1; float4x4 inverseProjMatrix2; - float4x4 inverseViewMatrix2; float4x4 inverseProjMatrix3; - float4x4 inverseViewMatrix3; float4x4 inverseProjMatrix4; - float4x4 inverseViewMatrix4; float4x4 inverseProjMatrix5; - float4x4 inverseViewMatrix5; float4x4 inverseProjMatrix6; - float4x4 inverseViewMatrix6; float4x4 inverseProjMatrix7; - float4x4 inverseViewMatrix7; float4x4 inverseProjMatrix8; - float4x4 inverseViewMatrix8; float4x4 inverseProjMatrix9; - float4x4 inverseViewMatrix9; float4x4 inverseProjMatrix10; - float4x4 inverseViewMatrix10; float4x4 inverseProjMatrix11; - float4x4 inverseViewMatrix11; float4x4 inverseProjMatrix12; - float4x4 inverseViewMatrix12; float4x4 inverseProjMatrix13; - float4x4 inverseViewMatrix13; float4x4 inverseProjMatrix14; - float4x4 inverseViewMatrix14; float4x4 inverseProjMatrix15; - float4x4 inverseViewMatrix15; - // 0 = right camera, 1 = left camera - Texture2D texture0; - SamplerState samplertexture0; - Texture2D texture1; - SamplerState samplertexture1; - - float maxDisparity = 1.5f; // range 0..2 disparity between left and right camera - float nearPlane = 0; // scene min depth value -> set to nearPlane - float farPlane = 1; // scene max depth value -> set to farPlane - int grid_size_x; int grid_size_y; @@ -135,44 +83,9 @@ Shader "G3D/ViewGeneration" } - float map(float x, float in_min, float in_max, float out_min, float out_max) - { - // Convert the current value to a percentage - // 0% - min1, 100% - max1 - float perc = (x - in_min) / (in_max - in_min); - - // Do the same operation backwards with min2 and max2 - float value = perc * (out_max - out_min) + out_min; - return value; - } - - float sampleRightDepth(float2 uv) { - float4 depthSample = _depthMapRight.Sample(sampler_depthMapRight, uv);; - float depth = Linear01Depth(depthSample.r, _ZBufferParams); // convert depth from logarithmic scale to linear scale - float x = uv.x * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates - float y = uv.y * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates - float4 NDC = float4(x, y, depth, 1.0f); - NDC = mul(inverseRightProjMatrix, NDC); // apply inverse projection matrix to get clip space coordinates - return -NDC.z / NDC.w; // devide by w to get depth in view space - } - - float sampleLeftDepth(float2 uv) { - float4 depthSample = _depthMapLeft.Sample(sampler_depthMapLeft, uv); - float test = -1.0f + farPlane / nearPlane; - float4 myZBufferParams = float4(test, 1.0f, test / farPlane, 1.0f / farPlane); - float depth = LinearEyeDepth(depthSample.r, myZBufferParams); // convert depth from logarithmic scale to linear scale - // float centerDepth = depth * 2.0f - 1.0f; - // float x = uv.x * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates - // float y = uv.y * 2.0f - 1.0f; // convert from [0,1] to [-1,1] to get NDC coordinates - // float4 NDC = float4(x, y, centerDepth, 1.0f); - // NDC = mul(inverseLeftProjMatrix, NDC); // apply inverse projection matrix to get clip space coordinates - - // float finalDepth = abs(NDC.z / NDC.w); // devide by w to get depth in view space - return depth; // devide by w to get depth in view space - } - - float4x4 getInverseProjectionMatrix(int cameraIndex) { - switch (cameraIndex) { + float4x4 getInverseViewProjectionMatrix(int viewIndex) { + switch (viewIndex) { + case 0: return inverseProjMatrix0; case 1: return inverseProjMatrix1; case 2: return inverseProjMatrix2; case 3: return inverseProjMatrix3; @@ -194,92 +107,41 @@ Shader "G3D/ViewGeneration" 1.0f, 1.0f, 1.0f, 1.0f); } } - float4x4 getInverseViewMatrix(int cameraIndex) { - switch (cameraIndex) { - case 1: return inverseViewMatrix1; - case 2: return inverseViewMatrix2; - case 3: return inverseViewMatrix3; - case 4: return inverseViewMatrix4; - case 5: return inverseViewMatrix5; - case 6: return inverseViewMatrix6; - case 7: return inverseViewMatrix7; - case 8: return inverseViewMatrix8; - case 9: return inverseViewMatrix9; - case 10: return inverseViewMatrix10; - case 11: return inverseViewMatrix11; - case 12: return inverseViewMatrix12; - case 13: return inverseViewMatrix13; - case 14: return inverseViewMatrix14; - case 15: return inverseViewMatrix15; - default: return float4x4(1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f); - } - } - // Set the texture array as a shader input - float getCameraDepth(float2 uv, int cameraIndex) { - float zIndex = 1.0f/ 16.0f * cameraIndex; // calculate the z index for the texture array - - float4 depthSample; - switch(cameraIndex) { - case 0: - depthSample = _depthMap0.Sample( sampler_depthMap0, uv); - break; - case 1: - depthSample = _depthMap1.Sample( sampler_depthMap1, uv); - break; - case 2: - depthSample = _depthMap2.Sample( sampler_depthMap2, uv); - break; - case 3: - depthSample = _depthMap3.Sample( sampler_depthMap3, uv); - break; - case 4: - depthSample = _depthMap4.Sample( sampler_depthMap4, uv); - break; - case 5: - depthSample = _depthMap5.Sample( sampler_depthMap5, uv); - break; - case 6: - depthSample = _depthMap6.Sample( sampler_depthMap6, uv); - break; - case 7: - depthSample = _depthMap7.Sample( sampler_depthMap7, uv); - break; - case 8: - depthSample = _depthMap8.Sample( sampler_depthMap8, uv); - break; - case 9: - depthSample = _depthMap9.Sample( sampler_depthMap9, uv); - break; - default: - depthSample = _depthMapLeft.Sample(sampler_depthMapLeft, uv); // use left camera depth map as default - break; + float2 calculateUVForMosaic(int viewIndex, float2 fullScreenUV, int mosaic_rows = 4, int mosaic_columns = 4) { + if(viewIndex < 0 ) + { + viewIndex = 0; } - - float test = -1.0f + farPlane / nearPlane; - float4 myZBufferParams = float4(test, 1.0f, test / farPlane, 1.0f / farPlane); - return LinearEyeDepth(depthSample.r, myZBufferParams); // convert depth from logarithmic scale to linear scale + int xAxis = viewIndex % mosaic_columns; + int yAxis = viewIndex / mosaic_columns; + // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) + // The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity. + yAxis = mosaic_rows - 1 - yAxis; + int2 moasicIndex = int2(xAxis, yAxis); + float2 scaledUV = float2(fullScreenUV.x / mosaic_columns, fullScreenUV.y / mosaic_rows); + float2 cellSize = float2(1.0 / mosaic_columns, 1.0 / mosaic_rows); + return scaledUV + cellSize * moasicIndex; } - float calculateOffset(float2 uv, int cameraIndex) { - float depth = getCameraDepth(uv, cameraIndex); - - float4 p = float4(0.0f, 0.0f, depth, 1.0f); // point in view space - p = mul(getInverseProjectionMatrix(cameraIndex), p); // convert from clip space to view space - p *= depth; // multiply by layer depth to get correct depth value in view space (layer is the correct w component) - p = mul(getInverseViewMatrix(cameraIndex), p); // convert from view space to world space + // Set the texture array as a shader input + float getCameraLogDepth(float2 fullScreenUV, int viewIndex) { + float2 fragementUV = calculateUVForMosaic(viewIndex, fullScreenUV, grid_size_y, grid_size_x); + return _depthMosaic.Sample(sampler_depthMosaic, fragementUV).r; + } - // p now in world space + // here UV is treated as a full screen UV coordinate + float2 calculateProjectedFragmentPosition(float2 uv, int viewIndex, float4x4 viewProjectionMatrix) { + float logDepth = getCameraLogDepth(uv, viewIndex); + // Sample the depth from the Camera depth texture. + float deviceDepth = logDepth; - p = mul(leftViewMatrix, p); // apply left view matrix to get shifted point in view space - p = mul(leftProjMatrix, p); // apply main camera projection matrix to get clip space coordinates + // Reconstruct the world space positions. + float3 worldPos = ComputeWorldSpacePosition(uv, deviceDepth, getInverseViewProjectionMatrix(viewIndex)); - float clipSpaceX = -p.x / p.w; // convert to clip space by dividing by w - clipSpaceX = clipSpaceX / 2.0f; // devide by 2 to get from clip space [-1, 1] to texture coordinates [0, 1] - return clipSpaceX; // return the difference between the shifted and original x coordinate + float3 NDC = ComputeNormalizedDeviceCoordinatesWithZ(worldPos, viewProjectionMatrix); // convert from clip space to NDC coordinates + + return float2(NDC.xy); // return the difference between the shifted and original x coordinate } /// @@ -290,12 +152,6 @@ Shader "G3D/ViewGeneration" /// float4 fragHDRP (v2f i) : SV_Target { - // float depth = sampleLeftDepth(i.uv); - // float depth = getCameraDepth(i.uv, 0); - // depth = depth/ 10.0f; // scale depth to a smaller range for visualization - // // depth = map(depth, 0, farPlane, 0, 1); - // return float4(depth, depth, depth, 1.0f); // return depth value in world space - { // the text coords of the original left and right view are from 0 - 1 // the tex coords of the texel we are currently rendering are also from 0 - 1 @@ -317,54 +173,66 @@ Shader "G3D/ViewGeneration" // texcoords in this texcels coordinate system (from 0 - 1) float2 actualTexCoords = float2(cellCoordinates.x - float(int(cellCoordinates.x)), cellCoordinates.y - float(int(cellCoordinates.y))); actualTexCoords.y = 1.0 - actualTexCoords.y; // flip y coordinate to match original tex coords - + // first and last image in the grid are the left and right camera uint gridCount = grid_size_x * grid_size_y; if (viewIndex == 0) { - return texture1.Sample(samplertexture1, actualTexCoords); // sample the left camera texture + return texture0.Sample(samplertexture0, actualTexCoords); // sample the left camera texture } if (viewIndex == gridCount - 1) { - return texture0.Sample(samplertexture0, actualTexCoords); // sample the right camera texture + return texture1.Sample(samplertexture1, actualTexCoords); // sample the right camera texture } - // float depth = getCameraDepth(actualTexCoords, viewIndex); - // depth = depth/ 10.0f; // scale depth to a smaller range for visualization - // // depth = map(depth, 0, farPlane, 0, 1); - // return float4(depth, depth, depth, 1.0f); // return depth value in world space - - - // ----------------------------------------------------------------------------------------------------------- - // ----------------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------------- - float disparityStep = maxDisparity / gridCount; + float2 shiftedLeftTexcoords = calculateProjectedFragmentPosition(actualTexCoords, viewIndex, leftViewProjMatrix); // convert to pixel space + float originalLeftDepth = getCameraLogDepth(shiftedLeftTexcoords, 0); // sample the depth of the original left camera + originalLeftDepth = LinearEyeDepth(originalLeftDepth, _ZBufferParams); // convert from log depth to linear depth + float leftDepth = getCameraLogDepth(actualTexCoords, viewIndex); // sample the depth of the shifted left camera + leftDepth = LinearEyeDepth(leftDepth, _ZBufferParams); // convert from log depth to linear depth + + float2 shiftedRightTexcoords = calculateProjectedFragmentPosition(actualTexCoords, viewIndex, rightViewProjMatrix); // convert to pixel space + float originalRightDepth = getCameraLogDepth(shiftedRightTexcoords, gridCount - 1); // sample the depth of the original right camera + originalRightDepth = LinearEyeDepth(originalRightDepth, _ZBufferParams); // convert from log depth to linear depth + float rightDepth = getCameraLogDepth(actualTexCoords, viewIndex); // sample the depth of the shifted right camera + rightDepth = LinearEyeDepth(rightDepth, _ZBufferParams); // convert from log depth to linear depth + + uint discardFragmentLeft = 0; + if(abs(originalLeftDepth - leftDepth) > 0.1) { + discardFragmentLeft = 1; // discard if the depth of the shifted left camera is too far away from the original left camera depth + } - float DLeft = calculateOffset(actualTexCoords, viewIndex); // convert to pixel space + if(shiftedLeftTexcoords.x < 0 || shiftedLeftTexcoords.x > 1.0f) { + discardFragmentLeft = 1; // discard if the tex coord is out of bounds + } - float2 texCoordShiftedLeft = actualTexCoords; - texCoordShiftedLeft.x -= DLeft; - - // if(texCoordShiftedLeft.x < 0 || texCoordShiftedLeft.x > 1.0f) { - // discard; // discard if the tex coord is out of bounds - // } - float shiftedLeftDepth = sampleLeftDepth(texCoordShiftedLeft); - - float actualDepth = sampleLeftDepth(actualTexCoords); // get the actual depth of the current texel + uint discardFragmentRight = 0; + if(abs(originalRightDepth - rightDepth) > 0.1) { + discardFragmentRight = 1; // discard if the depth of the shifted right camera is too far away from the original right camera depth + } - // if(abs((actualDepth - shiftedLeftDepth)) > 0.1f) { - // discard; // discard if the layer is too far away from the shifted left depth - // } - if( DLeft < 0.0f) { - return float4(0.0f, -DLeft, 0.0f, 1.0f); // return a debug value if the offset is negative + if(shiftedRightTexcoords.x < 0 || shiftedRightTexcoords.x > 1.0f) { + discardFragmentRight = 1; // discard if the tex coord is out of bounds + } + + + if (discardFragmentLeft == 1 && discardFragmentRight == 1) { + discard; + } + if (discardFragmentLeft == 1 && discardFragmentRight == 0) { + return texture1.Sample(samplertexture1, shiftedRightTexcoords); // sample the right camera texture + } + if (discardFragmentLeft == 0 && discardFragmentRight == 1) { + return texture0.Sample(samplertexture0, shiftedLeftTexcoords); // sample the left camera texture } - else { - return float4(DLeft, 0.0f, 0.0f, 1.0f); // return a debug value if the offset is too large + if (leftDepth < rightDepth) { + return texture0.Sample(samplertexture0, shiftedLeftTexcoords); // sample the left camera texture } - return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture + return texture1.Sample(samplertexture1, shiftedRightTexcoords); // ----------------- diff --git a/Scripts/G3DHDRPDepthMapPrePass.cs b/Scripts/G3DHDRPDepthMapPrePass.cs new file mode 100644 index 0000000..b226b7d --- /dev/null +++ b/Scripts/G3DHDRPDepthMapPrePass.cs @@ -0,0 +1,120 @@ +#if G3D_HDRP +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.HighDefinition; + +internal class G3DHDRPDepthMapPrePass : FullScreenCustomPass +{ + public List cameras; + public int internalCameraCount = 16; + + RenderTexture[] indivDepthTextures; + + public RTHandle depthMosaicHandle; + + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) + { + indivDepthTextures = new RenderTexture[internalCameraCount]; + for (int i = 0; i < internalCameraCount; i++) + { + RenderTexture depthTexture = new RenderTexture( + cameras[0].pixelWidth, + cameras[0].pixelHeight, + 24, + RenderTextureFormat.Depth + ); + depthTexture.dimension = TextureDimension.Tex2D; + depthTexture.Create(); + indivDepthTextures[i] = depthTexture; + fullscreenPassMaterial.SetTexture( + "_depthMap" + i, + depthTexture, + RenderTextureSubElement.Depth + ); + } + } + + protected override void Execute(CustomPassContext ctx) + { + var camera = ctx.hdCamera.camera; + if (isMainG3DCamera(camera) == false) + { + return; + } + + // render depth maps + for (int i = 0; i < internalCameraCount; i++) + { + Camera bakingCamera = cameras[i]; + + // We need to be careful about the aspect ratio of render textures when doing the culling, otherwise it could result in objects poping: + bakingCamera.aspect = Mathf.Max( + bakingCamera.aspect, + indivDepthTextures[i].width / (float)indivDepthTextures[i].height + ); + bakingCamera.TryGetCullingParameters(out var cullingParams); + cullingParams.cullingOptions = CullingOptions.None; + + // Assign the custom culling result to the context + // so it'll be used for the following operations + ctx.cullingResults = ctx.renderContext.Cull(ref cullingParams); + var overrideDepthTest = new RenderStateBlock(RenderStateMask.Depth) + { + depthState = new DepthState(true, CompareFunction.LessEqual) + }; + CustomPassUtils.RenderDepthFromCamera( + ctx, + bakingCamera, + indivDepthTextures[i], + ClearFlag.Depth, + bakingCamera.cullingMask, + overrideRenderState: overrideDepthTest + ); + } + + // combine depthmaps into mosaic depth map + CoreUtils.SetRenderTarget(ctx.cmd, depthMosaicHandle, ClearFlag.None); + CoreUtils.DrawFullScreen( + ctx.cmd, + fullscreenPassMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); + } + + /// + /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. + /// If so returns true, otherwise returns false. + /// + /// + /// + static bool isMainG3DCamera(Camera camera) + { + if (camera.cameraType != CameraType.Game) + return false; + bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); + bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; + + bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent( + out var mosaicCamera + ); + bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; + + if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) + return false; + + // skip all cameras created by the G3D Camera script + if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) + { + return false; + } + + return true; + } + + protected override void Cleanup() { } +} + + +#endif diff --git a/Scripts/G3DHDRPDepthMapPrePass.cs.meta b/Scripts/G3DHDRPDepthMapPrePass.cs.meta new file mode 100644 index 0000000..c1d1fe1 --- /dev/null +++ b/Scripts/G3DHDRPDepthMapPrePass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9251e827cd11c42489ea1e530df8d551 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 6f0b638..bbfa5bf 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -6,90 +6,15 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass { - public RTHandle leftDepthMapHandle; - public RTHandle rightDepthMapHandle; - public RTHandle leftColorMapHandle; public RTHandle rightColorMapHandle; - public float focusDistance = 1.0f; - public List cameras; public int internalCameraCount = 16; - RenderTexture depthTexturesArray; - - RenderTexture[] indivDepthTextures; - - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) - { - // Initialize the depth textures - // depthTexturesArray = new RenderTexture(1024, 1024, 24, RenderTextureFormat.Depth); - // depthTexturesArray.dimension = TextureDimension.Tex2DArray; - // depthTexturesArray.volumeDepth = internalCameraCount; - // depthTexturesArray.Create(); - - // fullscreenPassMaterial.SetTexture( - // "_DepthMaps", - // depthTexturesArray, - // RenderTextureSubElement.Depth - // ); - - indivDepthTextures = new RenderTexture[10]; - for (int i = 0; i < 10; i++) - { - RenderTexture depthTexture = new RenderTexture( - 1024, - 1024, - 24, - RenderTextureFormat.Depth - ); - depthTexture.dimension = TextureDimension.Tex2D; - depthTexture.Create(); - indivDepthTextures[i] = depthTexture; - fullscreenPassMaterial.SetTexture( - "_depthMap" + i, - depthTexture, - RenderTextureSubElement.Depth - ); - } - } - - float map(float x, float in_min, float in_max, float out_min, float out_max) - { - // Convert the current value to a percentage - // 0% - min1, 100% - max1 - float perc = (x - in_min) / (in_max - in_min); - - // Do the same operation backwards with min2 and max2 - float value = perc * (out_max - out_min) + out_min; - return value; - } - - private float layerOffset(float layer, float farPlane) - { - float tmp = map(layer, 0.0f, farPlane, 0, 1.0f); // convert layer distance from [nearPlane, farPlane] to [0,1] - Vector4 p = new Vector4(0.0f, 0.0f, tmp, layer); // point in view space - p = cameras[internalCameraCount - 2].projectionMatrix.inverse * p; // convert from clip space to view space - // p.x = p.x * layer; // convert from clip space to view space - // p.y = p.y * layer; // convert from clip space to view space - // p.z = p.z * layer; // convert from clip space to view space - p = cameras[internalCameraCount - 2].worldToCameraMatrix.inverse * p; // convert from view space to world space - - // p now in world space - - p = cameras[internalCameraCount - 1].worldToCameraMatrix * p; // apply left view matrix to get shifted point in view space - p = cameras[internalCameraCount - 1].projectionMatrix * p; // apply main camera projection matrix to get clip space coordinates - - p.x = p.x / p.w; // convert from clip space to view space - p.y = p.y / p.w; // convert from clip space to view space - p.z = p.z / p.w; // convert from clip space to view space - float clipSpaceX = -p.x / p.w / 2.0f; // convert to clip space by dividing by w - // clipSpaceX = clipSpaceX * 0.5f + 0.5f; // convert from [-1,1] to [0,1] to get texture coordinates - // clipSpaceX = map(clipSpaceX, -1.0f, 1.0f, 0.0f, 1.0f); // convert from [-1,1] to [0,1] to get texture coordinates + public RTHandle mosaicImageHandle; - return clipSpaceX; - } + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } protected override void Execute(CustomPassContext ctx) { @@ -98,62 +23,48 @@ protected override void Execute(CustomPassContext ctx) { CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - float viewRange = camera.farClipPlane - camera.nearClipPlane; - float stepSize = viewRange / 1024.0f; - - // set the inverse projection matrix - ctx.propertyBlock.SetMatrix( - Shader.PropertyToID("inverseProjMatrix1"), - cameras[internalCameraCount - 2].projectionMatrix.inverse - ); - ctx.propertyBlock.SetMatrix( - Shader.PropertyToID("inverseViewMatrix1"), - cameras[internalCameraCount - 2].cameraToWorldMatrix - ); - - for (int i = 0; i < 10; i++) + // upload all inv view projection matrices + for (int i = 0; i < internalCameraCount - 1; i++) { - Camera bakingCamera = cameras[15 - i]; - - // We need to be careful about the aspect ratio of render textures when doing the culling, otherwise it could result in objects poping: - bakingCamera.aspect = Mathf.Max( - bakingCamera.aspect, - indivDepthTextures[i].width / (float)indivDepthTextures[i].height + Matrix4x4 projectionMatrixInner = GL.GetGPUProjectionMatrix( + cameras[i].projectionMatrix, + false ); - bakingCamera.TryGetCullingParameters(out var cullingParams); - cullingParams.cullingOptions = CullingOptions.None; + Matrix4x4 viewMatrixInner = cameras[i].worldToCameraMatrix; - // Assign the custom culling result to the context - // so it'll be used for the following operations - ctx.cullingResults = ctx.renderContext.Cull(ref cullingParams); - var overrideDepthTest = new RenderStateBlock(RenderStateMask.Depth) - { - depthState = new DepthState(true, CompareFunction.LessEqual) - }; - CustomPassUtils.RenderDepthFromCamera( - ctx, - bakingCamera, - indivDepthTextures[i], - ClearFlag.Depth, - bakingCamera.cullingMask, - overrideRenderState: overrideDepthTest - ); - } - - for (int i = internalCameraCount - 2; i > 0; i--) - { - int idx = internalCameraCount - i - 1; - ctx.propertyBlock.SetMatrix( - Shader.PropertyToID("inverseProjMatrix" + idx), - cameras[i].projectionMatrix.inverse - ); + Matrix4x4 viewProjectionMatrixInner = projectionMatrixInner * viewMatrixInner; + Matrix4x4 invGPUProjMatrix = viewProjectionMatrixInner.inverse; ctx.propertyBlock.SetMatrix( - Shader.PropertyToID("inverseViewMatrix" + idx), - cameras[i].cameraToWorldMatrix + Shader.PropertyToID("inverseProjMatrix" + i), + invGPUProjMatrix ); } - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + // upload left view projection matrix + Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix( + cameras[0].projectionMatrix, + false + ); + Matrix4x4 viewMatrix = cameras[0].worldToCameraMatrix; + Matrix4x4 viewProjectionMatrix = projectionMatrix * viewMatrix; + ctx.propertyBlock.SetMatrix( + Shader.PropertyToID("leftViewProjMatrix"), + viewProjectionMatrix + ); + + // upload right view projection matrix + Matrix4x4 rightProjectionMatrix = GL.GetGPUProjectionMatrix( + cameras[internalCameraCount - 1].projectionMatrix, + false + ); + Matrix4x4 rightViewMatrix = cameras[internalCameraCount - 1].worldToCameraMatrix; + Matrix4x4 rightViewProjectionMatrix = rightProjectionMatrix * rightViewMatrix; + ctx.propertyBlock.SetMatrix( + Shader.PropertyToID("rightViewProjMatrix"), + rightViewProjectionMatrix + ); + + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); CoreUtils.DrawFullScreen( ctx.cmd, fullscreenPassMaterial, @@ -161,24 +72,6 @@ protected override void Execute(CustomPassContext ctx) shaderPassId: 0 ); } - else if (isLeftCamera(camera)) - { - CustomPassUtils.Copy(ctx, ctx.cameraDepthBuffer, leftDepthMapHandle); - } - else if (isRightCamera(camera)) - { - CustomPassUtils.Copy(ctx, ctx.cameraDepthBuffer, rightDepthMapHandle); - } - } - - bool isLeftCamera(Camera camera) - { - return camera.name == "g3dcam_" + (internalCameraCount - 1).ToString(); - } - - bool isRightCamera(Camera camera) - { - return camera.name == "g3dcam_0"; } /// @@ -216,18 +109,13 @@ protected override void Cleanup() { } internal class G3DHDRPViewGenerationMosaicPass : FullScreenCustomPass { - public RTHandle mosaicImageHandle; - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } protected override void Execute(CustomPassContext ctx) { var camera = ctx.hdCamera.camera; - if (shouldPerformBlit(camera)) + if (isMainG3DCamera(camera)) { - CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); - CustomPassUtils.Copy(ctx, ctx.cameraColorBuffer, mosaicImageHandle); - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_rows"), 4); ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_columns"), 4); @@ -246,7 +134,7 @@ protected override void Execute(CustomPassContext ctx) /// /// /// - static bool shouldPerformBlit(Camera camera) + static bool isMainG3DCamera(Camera camera) { if (camera.cameraType != CameraType.Game) return false; From aa45621c857b42d53278eb72de249d04168cb012 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 30 Jul 2025 12:22:33 +0200 Subject: [PATCH 011/149] changes o meta files --- Scripts/InspectorG3DCamera.cs.meta | 11 ++++++++++- Scripts/PackageManagerInteractor.cs.meta | 11 ++++++++++- .../ini-parser-dotnet/src/Configuration/Dummy.cs.meta | 11 ++++++++++- .../ini-parser-dotnet/src/Exceptions/Dummy.cs.meta | 11 ++++++++++- .../src/Exceptions/ParsingException.cs.meta | 11 ++++++++++- Scripts/ini-parser-dotnet/src/FileIniParser.cs.meta | 11 ++++++++++- Scripts/ini-parser-dotnet/src/Helpers/Assert.cs.meta | 11 ++++++++++- ...tenateDuplicatedKeysIniParserConfiguration.cs.meta | 11 ++++++++++- .../src/Model/Configuration/Dummy.cs.meta | 11 ++++++++++- .../Configuration/IniParserConfiguration.cs.meta | 11 ++++++++++- .../Model/Formatting/DefaultIniDataFormatter.cs.meta | 11 ++++++++++- .../src/Model/Formatting/IIniDataFormatter.cs.meta | 11 ++++++++++- Scripts/ini-parser-dotnet/src/Model/IniData.cs.meta | 11 ++++++++++- .../src/Model/IniDataCaseInsensitive.cs.meta | 11 ++++++++++- Scripts/ini-parser-dotnet/src/Model/KeyData.cs.meta | 11 ++++++++++- .../src/Model/KeyDataCollection.cs.meta | 11 ++++++++++- .../ini-parser-dotnet/src/Model/SectionData.cs.meta | 11 ++++++++++- .../src/Model/SectionDataCollection.cs.meta | 11 ++++++++++- .../ConcatenateDuplicatedKeysIniDataParser.cs.meta | 11 ++++++++++- .../src/Parser/IniDataParser.cs.meta | 11 ++++++++++- .../src/Properties/AssemblyInfo.cs.meta | 11 ++++++++++- .../ini-parser-dotnet/src/StreamIniDataParser.cs.meta | 11 ++++++++++- Scripts/ini-parser-dotnet/src/StringIniParser.cs.meta | 11 ++++++++++- 23 files changed, 230 insertions(+), 23 deletions(-) diff --git a/Scripts/InspectorG3DCamera.cs.meta b/Scripts/InspectorG3DCamera.cs.meta index 2de28ab..f156ccb 100644 --- a/Scripts/InspectorG3DCamera.cs.meta +++ b/Scripts/InspectorG3DCamera.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 053382540b8eef24cb87fc41eb207db5 \ No newline at end of file +guid: 053382540b8eef24cb87fc41eb207db5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/PackageManagerInteractor.cs.meta b/Scripts/PackageManagerInteractor.cs.meta index e40ba0f..3649258 100644 --- a/Scripts/PackageManagerInteractor.cs.meta +++ b/Scripts/PackageManagerInteractor.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: feb0893baf68c864c98e9de4db386eed \ No newline at end of file +guid: feb0893baf68c864c98e9de4db386eed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Configuration/Dummy.cs.meta b/Scripts/ini-parser-dotnet/src/Configuration/Dummy.cs.meta index 2edf078..8c73c66 100644 --- a/Scripts/ini-parser-dotnet/src/Configuration/Dummy.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Configuration/Dummy.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 0a7102f046f89834293de3073ebe97ff \ No newline at end of file +guid: 0a7102f046f89834293de3073ebe97ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Exceptions/Dummy.cs.meta b/Scripts/ini-parser-dotnet/src/Exceptions/Dummy.cs.meta index c95de98..3d7bc80 100644 --- a/Scripts/ini-parser-dotnet/src/Exceptions/Dummy.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Exceptions/Dummy.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 71b40e04cee54af47ab0eb49c268db01 \ No newline at end of file +guid: 71b40e04cee54af47ab0eb49c268db01 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Exceptions/ParsingException.cs.meta b/Scripts/ini-parser-dotnet/src/Exceptions/ParsingException.cs.meta index f468065..8a7837b 100644 --- a/Scripts/ini-parser-dotnet/src/Exceptions/ParsingException.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Exceptions/ParsingException.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: c55671e69e153da47bc9a4fb6bfb7334 \ No newline at end of file +guid: c55671e69e153da47bc9a4fb6bfb7334 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/FileIniParser.cs.meta b/Scripts/ini-parser-dotnet/src/FileIniParser.cs.meta index 1873149..9ffd187 100644 --- a/Scripts/ini-parser-dotnet/src/FileIniParser.cs.meta +++ b/Scripts/ini-parser-dotnet/src/FileIniParser.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: bc95f99dcc4db2d4faa5cdc6d6843670 \ No newline at end of file +guid: bc95f99dcc4db2d4faa5cdc6d6843670 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Helpers/Assert.cs.meta b/Scripts/ini-parser-dotnet/src/Helpers/Assert.cs.meta index 710adcf..d939edc 100644 --- a/Scripts/ini-parser-dotnet/src/Helpers/Assert.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Helpers/Assert.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 17ccc2d0b51c91f4088dee9076acccf7 \ No newline at end of file +guid: 17ccc2d0b51c91f4088dee9076acccf7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/Configuration/ConcatenateDuplicatedKeysIniParserConfiguration.cs.meta b/Scripts/ini-parser-dotnet/src/Model/Configuration/ConcatenateDuplicatedKeysIniParserConfiguration.cs.meta index b41e4c8..067b667 100644 --- a/Scripts/ini-parser-dotnet/src/Model/Configuration/ConcatenateDuplicatedKeysIniParserConfiguration.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/Configuration/ConcatenateDuplicatedKeysIniParserConfiguration.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 06b79b40924cc5440a0c14e12942aa8b \ No newline at end of file +guid: 06b79b40924cc5440a0c14e12942aa8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/Configuration/Dummy.cs.meta b/Scripts/ini-parser-dotnet/src/Model/Configuration/Dummy.cs.meta index 650c3ee..1f350eb 100644 --- a/Scripts/ini-parser-dotnet/src/Model/Configuration/Dummy.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/Configuration/Dummy.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 9e9fffd4ffb9c0a47b86b458be654499 \ No newline at end of file +guid: 9e9fffd4ffb9c0a47b86b458be654499 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/Configuration/IniParserConfiguration.cs.meta b/Scripts/ini-parser-dotnet/src/Model/Configuration/IniParserConfiguration.cs.meta index 9d40097..0c7d66c 100644 --- a/Scripts/ini-parser-dotnet/src/Model/Configuration/IniParserConfiguration.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/Configuration/IniParserConfiguration.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 9a00758ff420a364da797184ef588b4d \ No newline at end of file +guid: 9a00758ff420a364da797184ef588b4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/Formatting/DefaultIniDataFormatter.cs.meta b/Scripts/ini-parser-dotnet/src/Model/Formatting/DefaultIniDataFormatter.cs.meta index 4f68160..4e5a681 100644 --- a/Scripts/ini-parser-dotnet/src/Model/Formatting/DefaultIniDataFormatter.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/Formatting/DefaultIniDataFormatter.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 246af77d0ababcd40824ccd6398f0281 \ No newline at end of file +guid: 246af77d0ababcd40824ccd6398f0281 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/Formatting/IIniDataFormatter.cs.meta b/Scripts/ini-parser-dotnet/src/Model/Formatting/IIniDataFormatter.cs.meta index 2626638..24b3d93 100644 --- a/Scripts/ini-parser-dotnet/src/Model/Formatting/IIniDataFormatter.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/Formatting/IIniDataFormatter.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 2532b858711a73b4b9ed4a536baa89e5 \ No newline at end of file +guid: 2532b858711a73b4b9ed4a536baa89e5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/IniData.cs.meta b/Scripts/ini-parser-dotnet/src/Model/IniData.cs.meta index 9ae59b5..f619a06 100644 --- a/Scripts/ini-parser-dotnet/src/Model/IniData.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/IniData.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 01f2883d8448e4145ad33c05aa250575 \ No newline at end of file +guid: 01f2883d8448e4145ad33c05aa250575 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/IniDataCaseInsensitive.cs.meta b/Scripts/ini-parser-dotnet/src/Model/IniDataCaseInsensitive.cs.meta index 9c19cf5..57e70b5 100644 --- a/Scripts/ini-parser-dotnet/src/Model/IniDataCaseInsensitive.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/IniDataCaseInsensitive.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 5e44f39827088c84783c1aeeb124b562 \ No newline at end of file +guid: 5e44f39827088c84783c1aeeb124b562 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/KeyData.cs.meta b/Scripts/ini-parser-dotnet/src/Model/KeyData.cs.meta index 0463416..067c6fb 100644 --- a/Scripts/ini-parser-dotnet/src/Model/KeyData.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/KeyData.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 3bb7c523aeb78ee4f8f118f7a373c7ed \ No newline at end of file +guid: 3bb7c523aeb78ee4f8f118f7a373c7ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/KeyDataCollection.cs.meta b/Scripts/ini-parser-dotnet/src/Model/KeyDataCollection.cs.meta index 2c56a7c..e20490b 100644 --- a/Scripts/ini-parser-dotnet/src/Model/KeyDataCollection.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/KeyDataCollection.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: c7fc4d318601bf442aa5c996f96e2df2 \ No newline at end of file +guid: c7fc4d318601bf442aa5c996f96e2df2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/SectionData.cs.meta b/Scripts/ini-parser-dotnet/src/Model/SectionData.cs.meta index d2597dc..5f30d7c 100644 --- a/Scripts/ini-parser-dotnet/src/Model/SectionData.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/SectionData.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: eb02f9a821159a94db67b114057b51a2 \ No newline at end of file +guid: eb02f9a821159a94db67b114057b51a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Model/SectionDataCollection.cs.meta b/Scripts/ini-parser-dotnet/src/Model/SectionDataCollection.cs.meta index f11c7e4..4736b0b 100644 --- a/Scripts/ini-parser-dotnet/src/Model/SectionDataCollection.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Model/SectionDataCollection.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 5025115b7671c2845bebbafd4100f4b2 \ No newline at end of file +guid: 5025115b7671c2845bebbafd4100f4b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Parser/ConcatenateDuplicatedKeysIniDataParser.cs.meta b/Scripts/ini-parser-dotnet/src/Parser/ConcatenateDuplicatedKeysIniDataParser.cs.meta index d72f25c..8cab11c 100644 --- a/Scripts/ini-parser-dotnet/src/Parser/ConcatenateDuplicatedKeysIniDataParser.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Parser/ConcatenateDuplicatedKeysIniDataParser.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: b00ce0edfe214a844acbaf55efa8a1f2 \ No newline at end of file +guid: b00ce0edfe214a844acbaf55efa8a1f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Parser/IniDataParser.cs.meta b/Scripts/ini-parser-dotnet/src/Parser/IniDataParser.cs.meta index 5e4898b..ec29b89 100644 --- a/Scripts/ini-parser-dotnet/src/Parser/IniDataParser.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Parser/IniDataParser.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: fe17cfc6ed3292b4db14a0fad7a7f0bc \ No newline at end of file +guid: fe17cfc6ed3292b4db14a0fad7a7f0bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/Properties/AssemblyInfo.cs.meta b/Scripts/ini-parser-dotnet/src/Properties/AssemblyInfo.cs.meta index ac28703..e4dbc54 100644 --- a/Scripts/ini-parser-dotnet/src/Properties/AssemblyInfo.cs.meta +++ b/Scripts/ini-parser-dotnet/src/Properties/AssemblyInfo.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 2574c1b9bd6c7ca4fa4ebc87da272ec6 \ No newline at end of file +guid: 2574c1b9bd6c7ca4fa4ebc87da272ec6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/StreamIniDataParser.cs.meta b/Scripts/ini-parser-dotnet/src/StreamIniDataParser.cs.meta index f773f23..0b3f450 100644 --- a/Scripts/ini-parser-dotnet/src/StreamIniDataParser.cs.meta +++ b/Scripts/ini-parser-dotnet/src/StreamIniDataParser.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 08d664cf7c6d9bf4d99907585ad1ed62 \ No newline at end of file +guid: 08d664cf7c6d9bf4d99907585ad1ed62 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ini-parser-dotnet/src/StringIniParser.cs.meta b/Scripts/ini-parser-dotnet/src/StringIniParser.cs.meta index 4222c36..08c6a71 100644 --- a/Scripts/ini-parser-dotnet/src/StringIniParser.cs.meta +++ b/Scripts/ini-parser-dotnet/src/StringIniParser.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: f4b0185c441e784469b1cf74d4203166 \ No newline at end of file +guid: f4b0185c441e784469b1cf74d4203166 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 19600cb53a2c1057f24bec1e53715318d3ed1d0b Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 30 Jul 2025 12:23:00 +0200 Subject: [PATCH 012/149] render depth maps in original resolution --- G3DCamera.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 27daeb6..0e1135b 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -373,10 +373,12 @@ private void initCustomPass() depthMosaicPass.internalCameraCount = internalCameraCount; RenderTexture depthMosaicTexture = new RenderTexture( - mainCamera.pixelWidth * 4, - mainCamera.pixelHeight * 4, - 0 + mainCamera.pixelWidth, + mainCamera.pixelHeight, + 32, + RenderTextureFormat.Depth ); + depthMosaicTexture.Create(); RTHandle depthMosaicHandle = RTHandles.Alloc(depthMosaicTexture); depthMosaicPass.depthMosaicHandle = depthMosaicHandle; From 1da88025a9e43db0e4cedbbb7d3e439e9da0ac5b Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 30 Jul 2025 12:39:02 +0200 Subject: [PATCH 013/149] cleanup --- G3DCamera.cs | 4 +- Resources/G3DDepthMosaic.shader | 51 +---- Resources/G3DHLSLCommonFunctions.hlsl | 71 +++++++ Resources/G3DHLSLCommonFunctions.hlsl.meta | 7 + Resources/G3DViewGeneration.shader | 216 +++++++++------------ Scripts/G3DHDRPDepthMapPrePass.cs | 3 +- Scripts/G3DHDRPViewGenerationPass.cs | 7 +- 7 files changed, 183 insertions(+), 176 deletions(-) create mode 100644 Resources/G3DHLSLCommonFunctions.hlsl create mode 100644 Resources/G3DHLSLCommonFunctions.hlsl.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index 0e1135b..f30da60 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Drawing.Drawing2D; using System.IO; using UnityEditor; using UnityEngine; @@ -375,8 +374,7 @@ private void initCustomPass() RenderTexture depthMosaicTexture = new RenderTexture( mainCamera.pixelWidth, mainCamera.pixelHeight, - 32, - RenderTextureFormat.Depth + 0 ); depthMosaicTexture.Create(); RTHandle depthMosaicHandle = RTHandles.Alloc(depthMosaicTexture); diff --git a/Resources/G3DDepthMosaic.shader b/Resources/G3DDepthMosaic.shader index 2514e68..7260e4d 100644 --- a/Resources/G3DDepthMosaic.shader +++ b/Resources/G3DDepthMosaic.shader @@ -20,6 +20,7 @@ Shader "G3D/DepthMosaic" HLSLPROGRAM #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + #include "G3DHLSLCommonFunctions.hlsl" #pragma vertex vert #pragma fragment fragHDRP @@ -61,30 +62,6 @@ Shader "G3D/DepthMosaic" int grid_size_x; int grid_size_y; - struct VertAttributes - { - uint vertexID : SV_VertexID; - UNITY_VERTEX_INPUT_INSTANCE_ID - }; - - struct v2f - { - float2 uv : TEXCOORD0; - float4 screenPos : SV_POSITION; - }; - - v2f vert(VertAttributes input) - { - v2f output; - UNITY_SETUP_INSTANCE_ID(input); - UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); - output.uv = GetFullScreenTriangleTexCoord(input.vertexID); - output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); - - return output; - - } - // Set the texture array as a shader input float getCameraLogDepth(float2 uv, int cameraIndex) { switch(cameraIndex) { @@ -131,29 +108,11 @@ Shader "G3D/DepthMosaic" /// float4 fragHDRP (v2f i) : SV_Target { - { - // the text coords of the original left and right view are from 0 - 1 - // the tex coords of the texel we are currently rendering are also from 0 - 1 - // but we want to create a grid of views, so we need to transform the tex coords - // to the grid size. - // basically we want to figure out in which grid cell the current texel is, then convert the texel coords to the grid cell coords. - // example assuming a grid size of 3x3: - // original tex coords: 0.8, 0.5 - // step 1: transform the tex coords to the grid size by multiplying with grid size - // -> e.g. original x coord 0.8 turns to 0.8 * 3 = 2.4 - // step 2: figure out the grid cell by taking the integer part of the transformed tex coords - // -> e.g. 2.4 turns to 2 - // step 3: subtract the integer part from the transformed tex coords to get the texel coords in the grid cell - // -> e.g. 2.4 - 2 = 0.4 -> final texel coords in the grid cell are 0.4, 0.5 - } - float2 cellCoordinates = float2(i.uv.x, 1.0 - i.uv.y); // flip y coordiate to have cell index 0 in upper left corner - cellCoordinates = float2(cellCoordinates.x * grid_size_x, cellCoordinates.y * grid_size_y); - uint viewIndex = uint(cellCoordinates.x) + grid_size_x * uint(cellCoordinates.y); - // texcoords in this texcels coordinate system (from 0 - 1) - float2 actualTexCoords = float2(cellCoordinates.x - float(int(cellCoordinates.x)), cellCoordinates.y - float(int(cellCoordinates.y))); - actualTexCoords.y = 1.0 - actualTexCoords.y; // flip y coordinate to match original tex coords + float2 cellCoordinates = getCellCoordinates(i.uv, grid_size_x, grid_size_y); + uint viewIndex = getViewIndex(cellCoordinates, grid_size_x, grid_size_y); + float2 cellTexCoords = getCellTexCoords(cellCoordinates); - float depth = getCameraLogDepth(actualTexCoords, viewIndex); + float depth = getCameraLogDepth(cellTexCoords, viewIndex); return float4(depth, depth, depth, 1.0f); // return the depth value as color } ENDHLSL diff --git a/Resources/G3DHLSLCommonFunctions.hlsl b/Resources/G3DHLSLCommonFunctions.hlsl new file mode 100644 index 0000000..8d24a53 --- /dev/null +++ b/Resources/G3DHLSLCommonFunctions.hlsl @@ -0,0 +1,71 @@ +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + +struct VertAttributes +{ + uint vertexID : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; + +struct v2f +{ + float2 uv : TEXCOORD0; + float4 screenPos : SV_POSITION; +}; + +v2f vert(VertAttributes input) +{ + v2f output; + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + output.uv = GetFullScreenTriangleTexCoord(input.vertexID); + output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + + return output; + +} + +float2 calculateUVForMosaic(int viewIndex, float2 fullScreenUV, int mosaic_rows = 4, int mosaic_columns = 4) { + if(viewIndex < 0 ) + { + viewIndex = 0; + } + int xAxis = viewIndex % mosaic_columns; + int yAxis = viewIndex / mosaic_columns; + // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) + // The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity. + yAxis = mosaic_rows - 1 - yAxis; + int2 moasicIndex = int2(xAxis, yAxis); + float2 scaledUV = float2(fullScreenUV.x / mosaic_columns, fullScreenUV.y / mosaic_rows); + float2 cellSize = float2(1.0 / mosaic_columns, 1.0 / mosaic_rows); + return scaledUV + cellSize * moasicIndex; +} + + +// the text coords of the original left and right view are from 0 - 1 +// the tex coords of the texel we are currently rendering are also from 0 - 1 +// but we want to create a grid of views, so we need to transform the tex coords +// to the grid size. +// basically we want to figure out in which grid cell the current texel is, then convert the texel coords to the grid cell coords. +// example assuming a grid size of 3x3: +// original tex coords: 0.8, 0.5 +// step 1: transform the tex coords to the grid size by multiplying with grid size +// -> e.g. original x coord 0.8 turns to 0.8 * 3 = 2.4 +// step 2: figure out the grid cell by taking the integer part of the transformed tex coords +// -> e.g. 2.4 turns to 2 +// step 3: subtract the integer part from the transformed tex coords to get the texel coords in the grid cell +// -> e.g. 2.4 - 2 = 0.4 -> final texel coords in the grid cell are 0.4, 0.5 +float2 getCellCoordinates(float2 uv, int gridSizeX, int gridSizeY) { + float2 cellCoordinates = float2(uv.x, 1.0 - uv.y); // flip y coordiate to have cell index 0 in upper left corner + cellCoordinates = float2(cellCoordinates.x * gridSizeX, cellCoordinates.y * gridSizeY); + return cellCoordinates; +} +uint getViewIndex(float2 cellCoordinates, int gridSizeX, int gridSizeY) { + uint viewIndex = uint(cellCoordinates.x) + gridSizeX * uint(cellCoordinates.y); + return viewIndex; +} +float2 getCellTexCoords(float2 cellCoordinates) { + float2 cellTexCoords = float2(cellCoordinates.x - float(int(cellCoordinates.x)), cellCoordinates.y - float(int(cellCoordinates.y))); + cellTexCoords.y = 1.0 - cellTexCoords.y; // flip y coordinate to match original tex coords + return cellTexCoords; +} \ No newline at end of file diff --git a/Resources/G3DHLSLCommonFunctions.hlsl.meta b/Resources/G3DHLSLCommonFunctions.hlsl.meta new file mode 100644 index 0000000..3655b91 --- /dev/null +++ b/Resources/G3DHLSLCommonFunctions.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0bd9fd86aa17fd3488c5bce2a26b8204 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index 1523957..0693662 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -20,6 +20,7 @@ Shader "G3D/ViewGeneration" HLSLPROGRAM #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + #include "G3DHLSLCommonFunctions.hlsl" #pragma vertex vert #pragma fragment fragHDRP @@ -54,34 +55,27 @@ Shader "G3D/ViewGeneration" float4x4 inverseProjMatrix14; float4x4 inverseProjMatrix15; + float4x4 viewMatrix0; + float4x4 viewMatrix1; + float4x4 viewMatrix2; + float4x4 viewMatrix3; + float4x4 viewMatrix4; + float4x4 viewMatrix5; + float4x4 viewMatrix6; + float4x4 viewMatrix7; + float4x4 viewMatrix8; + float4x4 viewMatrix9; + float4x4 viewMatrix10; + float4x4 viewMatrix11; + float4x4 viewMatrix12; + float4x4 viewMatrix13; + float4x4 viewMatrix14; + float4x4 viewMatrix15; - int grid_size_x; - int grid_size_y; - - struct VertAttributes - { - uint vertexID : SV_VertexID; - UNITY_VERTEX_INPUT_INSTANCE_ID - }; - - struct v2f - { - float2 uv : TEXCOORD0; - float4 screenPos : SV_POSITION; - }; - - v2f vert(VertAttributes input) - { - v2f output; - UNITY_SETUP_INSTANCE_ID(input); - UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); - output.uv = GetFullScreenTriangleTexCoord(input.vertexID); - output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); - - return output; - } + int grid_size_x; + int grid_size_y; float4x4 getInverseViewProjectionMatrix(int viewIndex) { switch (viewIndex) { @@ -108,26 +102,60 @@ Shader "G3D/ViewGeneration" } } - float2 calculateUVForMosaic(int viewIndex, float2 fullScreenUV, int mosaic_rows = 4, int mosaic_columns = 4) { - if(viewIndex < 0 ) - { - viewIndex = 0; + float4x4 getViewMatrix(int viewIndex) { + switch (viewIndex) { + case 0: return viewMatrix0; + case 1: return viewMatrix1; + case 2: return viewMatrix2; + case 3: return viewMatrix3; + case 4: return viewMatrix4; + case 5: return viewMatrix5; + case 6: return viewMatrix6; + case 7: return viewMatrix7; + case 8: return viewMatrix8; + case 9: return viewMatrix9; + case 10: return viewMatrix10; + case 11: return viewMatrix11; + case 12: return viewMatrix12; + case 13: return viewMatrix13; + case 14: return viewMatrix14; + case 15: return viewMatrix15; + default: return float4x4(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); } - int xAxis = viewIndex % mosaic_columns; - int yAxis = viewIndex / mosaic_columns; - // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) - // The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity. - yAxis = mosaic_rows - 1 - yAxis; - int2 moasicIndex = int2(xAxis, yAxis); - float2 scaledUV = float2(fullScreenUV.x / mosaic_columns, fullScreenUV.y / mosaic_rows); - float2 cellSize = float2(1.0 / mosaic_columns, 1.0 / mosaic_rows); - return scaledUV + cellSize * moasicIndex; } // Set the texture array as a shader input float getCameraLogDepth(float2 fullScreenUV, int viewIndex) { - float2 fragementUV = calculateUVForMosaic(viewIndex, fullScreenUV, grid_size_y, grid_size_x); - return _depthMosaic.Sample(sampler_depthMosaic, fragementUV).r; + float2 fragmentUV = calculateUVForMosaic(viewIndex, fullScreenUV, grid_size_y, grid_size_x); + return _depthMosaic.Sample(sampler_depthMosaic, fragmentUV).r; + + { + + // Test if bluring the depth map removes the banding artifacts + // const float offset[] = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; + // const float weight[] = {0.191, 0.15, 0.092, 0.044, 0.017, 0.005, 0.001}; + // float4 FragmentColor = float4(0.0f, 0.0f, 0.0f, 0.0f); + // // 2619 x 1387 + // //(1.0, 0.0) -> horizontal blur + // //(0.0, 1.0) -> vertical blur + + // float hstep = 1.0 / 2619; + // float vstep = 1.0 / 1387; + + // for (int i = 1; i < 5; i++) { + // FragmentColor += + // _depthMosaic.Sample(sampler_depthMosaic, fragmentUV + float2(hstep*offset[i], vstep*offset[i]))*weight[i] + + // _depthMosaic.Sample(sampler_depthMosaic, fragmentUV - float2(hstep*offset[i], vstep*offset[i]))*weight[i]; + // } + // float4 ppColour = _depthMosaic.Sample(sampler_depthMosaic, fragmentUV) * weight[0]; + // ppColour += FragmentColor; + + + // return ppColour.r;//_depthMosaic.Sample(sampler_depthMosaic, fragmentUV).r; + } } // here UV is treated as a full screen UV coordinate @@ -143,6 +171,16 @@ Shader "G3D/ViewGeneration" return float2(NDC.xy); // return the difference between the shifted and original x coordinate } + + float LinearEyeDepthViewBased(float2 uv, int viewIndex) + { + float logDepth = getCameraLogDepth(uv, viewIndex); + // Sample the depth from the Camera depth texture. + float deviceDepth = logDepth; + float3 worldPos = ComputeWorldSpacePosition(uv, deviceDepth, getInverseViewProjectionMatrix(viewIndex)); + float eyeDepth = LinearEyeDepth(worldPos, getViewMatrix(viewIndex)); + return eyeDepth; + } /// /// creates a grid of views with a given grid size. @@ -152,54 +190,32 @@ Shader "G3D/ViewGeneration" /// float4 fragHDRP (v2f i) : SV_Target { - { - // the text coords of the original left and right view are from 0 - 1 - // the tex coords of the texel we are currently rendering are also from 0 - 1 - // but we want to create a grid of views, so we need to transform the tex coords - // to the grid size. - // basically we want to figure out in which grid cell the current texel is, then convert the texel coords to the grid cell coords. - // example assuming a grid size of 3x3: - // original tex coords: 0.8, 0.5 - // step 1: transform the tex coords to the grid size by multiplying with grid size - // -> e.g. original x coord 0.8 turns to 0.8 * 3 = 2.4 - // step 2: figure out the grid cell by taking the integer part of the transformed tex coords - // -> e.g. 2.4 turns to 2 - // step 3: subtract the integer part from the transformed tex coords to get the texel coords in the grid cell - // -> e.g. 2.4 - 2 = 0.4 -> final texel coords in the grid cell are 0.4, 0.5 - } - float2 cellCoordinates = float2(i.uv.x, 1.0 - i.uv.y); // flip y coordiate to have cell index 0 in upper left corner - cellCoordinates = float2(cellCoordinates.x * grid_size_x, cellCoordinates.y * grid_size_y); - uint viewIndex = uint(cellCoordinates.x) + grid_size_x * uint(cellCoordinates.y); - // texcoords in this texcels coordinate system (from 0 - 1) - float2 actualTexCoords = float2(cellCoordinates.x - float(int(cellCoordinates.x)), cellCoordinates.y - float(int(cellCoordinates.y))); - actualTexCoords.y = 1.0 - actualTexCoords.y; // flip y coordinate to match original tex coords + float2 cellCoordinates = getCellCoordinates(i.uv, grid_size_x, grid_size_y); + uint viewIndex = getViewIndex(cellCoordinates, grid_size_x, grid_size_y); + float2 cellTexCoords = getCellTexCoords(cellCoordinates); // first and last image in the grid are the left and right camera uint gridCount = grid_size_x * grid_size_y; if (viewIndex == 0) { - return texture0.Sample(samplertexture0, actualTexCoords); // sample the left camera texture + return texture0.Sample(samplertexture0, cellTexCoords); // sample the left camera texture } if (viewIndex == gridCount - 1) { - return texture1.Sample(samplertexture1, actualTexCoords); // sample the right camera texture + return texture1.Sample(samplertexture1, cellTexCoords); // sample the right camera texture } // ----------------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------------- - float2 shiftedLeftTexcoords = calculateProjectedFragmentPosition(actualTexCoords, viewIndex, leftViewProjMatrix); // convert to pixel space - float originalLeftDepth = getCameraLogDepth(shiftedLeftTexcoords, 0); // sample the depth of the original left camera - originalLeftDepth = LinearEyeDepth(originalLeftDepth, _ZBufferParams); // convert from log depth to linear depth - float leftDepth = getCameraLogDepth(actualTexCoords, viewIndex); // sample the depth of the shifted left camera - leftDepth = LinearEyeDepth(leftDepth, _ZBufferParams); // convert from log depth to linear depth - - float2 shiftedRightTexcoords = calculateProjectedFragmentPosition(actualTexCoords, viewIndex, rightViewProjMatrix); // convert to pixel space - float originalRightDepth = getCameraLogDepth(shiftedRightTexcoords, gridCount - 1); // sample the depth of the original right camera - originalRightDepth = LinearEyeDepth(originalRightDepth, _ZBufferParams); // convert from log depth to linear depth - float rightDepth = getCameraLogDepth(actualTexCoords, viewIndex); // sample the depth of the shifted right camera - rightDepth = LinearEyeDepth(rightDepth, _ZBufferParams); // convert from log depth to linear depth + float2 shiftedLeftTexcoords = calculateProjectedFragmentPosition(cellTexCoords, viewIndex, leftViewProjMatrix); // convert to pixel space + float originalLeftDepth = LinearEyeDepthViewBased(shiftedLeftTexcoords, 0); // sample the depth of the original left camera + float actualDepth = LinearEyeDepthViewBased(cellTexCoords, viewIndex); // sample the depth of the shifted left camera + + float2 shiftedRightTexcoords = calculateProjectedFragmentPosition(cellTexCoords, viewIndex, rightViewProjMatrix); // convert to pixel space + float originalRightDepth = LinearEyeDepthViewBased(shiftedRightTexcoords, gridCount - 1); // sample the depth of the original right camera + uint discardFragmentLeft = 0; - if(abs(originalLeftDepth - leftDepth) > 0.1) { + if(abs(originalLeftDepth - actualDepth) > 0.1) { discardFragmentLeft = 1; // discard if the depth of the shifted left camera is too far away from the original left camera depth } @@ -209,7 +225,7 @@ Shader "G3D/ViewGeneration" uint discardFragmentRight = 0; - if(abs(originalRightDepth - rightDepth) > 0.1) { + if(abs(originalRightDepth - actualDepth) > 0.1) { discardFragmentRight = 1; // discard if the depth of the shifted right camera is too far away from the original right camera depth } @@ -217,7 +233,6 @@ Shader "G3D/ViewGeneration" discardFragmentRight = 1; // discard if the tex coord is out of bounds } - if (discardFragmentLeft == 1 && discardFragmentRight == 1) { discard; } @@ -227,55 +242,8 @@ Shader "G3D/ViewGeneration" if (discardFragmentLeft == 0 && discardFragmentRight == 1) { return texture0.Sample(samplertexture0, shiftedLeftTexcoords); // sample the left camera texture } - if (leftDepth < rightDepth) { - return texture0.Sample(samplertexture0, shiftedLeftTexcoords); // sample the left camera texture - } - return texture1.Sample(samplertexture1, shiftedRightTexcoords); - - - // ----------------- - // handle left and right cameras - // ----------------- - // float rightOffset = (gridCount - 1 - viewIndex) * disparityStep; // distance between the original left camera and the current view - // float DRight = calculateOffset(rightOffset, focusDistance, distLayerFocusPlane, rightProjMatrix); // calculate offset of layer in pixel - // float2 texCoordShiftedRight = actualTexCoords; - // texCoordShiftedRight.x -= DRight; - // float shiftedRightDepth = sampleRightDepth(texCoordShiftedRight); - - // int leftFills = 1; - // if(texCoordShiftedLeft.x < 0 || texCoordShiftedLeft.x > 1.0f) { - // leftFills = 0; // discard if the tex coord is out of bounds - // } - // if (abs((layer - shiftedLeftDepth)) > layerDistance) { - // leftFills = 0; // discard if the layer is too far away from the shifted left depth - // } - - - // int rightFills = 0; - // if(texCoordShiftedRight.x < 0 || texCoordShiftedRight.x > 1.0f) { - // rightFills = 0; // discard if the tex coord is out of bounds - // } - // if (abs((layer - shiftedRightDepth)) > layerDistance) { - // rightFills = 0; // discard if the layer is too far away from the shifted right depth - // } - - // if (leftFills == 0 && rightFills == 0) { - // discard; // discard if both left and right camera do not fill the layer - // } - // if (leftFills == 1 && rightFills == 0) { - // return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture - // } - // if (leftFills == 0 && rightFills == 1) { - // return texture0.Sample(samplertexture0, texCoordShiftedRight); // sample the right camera texture - // } - - // if(shiftedLeftDepth < shiftedRightDepth) { - // return texture1.Sample(samplertexture1, texCoordShiftedLeft); // sample the left camera texture - // } - - // return texture0.Sample(samplertexture0, texCoordShiftedRight); // sample the right camera texture } ENDHLSL } diff --git a/Scripts/G3DHDRPDepthMapPrePass.cs b/Scripts/G3DHDRPDepthMapPrePass.cs index b226b7d..ee626a2 100644 --- a/Scripts/G3DHDRPDepthMapPrePass.cs +++ b/Scripts/G3DHDRPDepthMapPrePass.cs @@ -21,10 +21,9 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff RenderTexture depthTexture = new RenderTexture( cameras[0].pixelWidth, cameras[0].pixelHeight, - 24, + 32, RenderTextureFormat.Depth ); - depthTexture.dimension = TextureDimension.Tex2D; depthTexture.Create(); indivDepthTextures[i] = depthTexture; fullscreenPassMaterial.SetTexture( diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index bbfa5bf..8f74193 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -24,7 +24,7 @@ protected override void Execute(CustomPassContext ctx) CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); // upload all inv view projection matrices - for (int i = 0; i < internalCameraCount - 1; i++) + for (int i = 0; i < internalCameraCount; i++) { Matrix4x4 projectionMatrixInner = GL.GetGPUProjectionMatrix( cameras[i].projectionMatrix, @@ -38,6 +38,11 @@ protected override void Execute(CustomPassContext ctx) Shader.PropertyToID("inverseProjMatrix" + i), invGPUProjMatrix ); + + ctx.propertyBlock.SetMatrix( + Shader.PropertyToID("viewMatrix" + i), + cameras[i].worldToCameraMatrix + ); } // upload left view projection matrix From 81bfcdc421751bfd044b9a7669efe470af098a33 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 30 Jul 2025 17:05:05 +0200 Subject: [PATCH 014/149] wip hole filling --- G3DCamera.cs | 15 +- Resources/G3DDepthMosaic.shader | 1 + Resources/G3DHLSLCommonFunctions.hlsl | 27 -- Resources/G3DHLSLShaderBasics.hlsl | 26 ++ Resources/G3DHLSLShaderBasics.hlsl.meta | 7 + Resources/G3DViewGenHoleFilling.compute | 262 +++++++++++++++++++ Resources/G3DViewGenHoleFilling.compute.meta | 7 + Resources/G3DViewGeneration.shader | 4 +- Resources/NewComputeShader.compute | 14 + Resources/NewComputeShader.compute.meta | 7 + Scripts/G3DHDRPViewGenerationPass.cs | 85 ++++-- 11 files changed, 403 insertions(+), 52 deletions(-) create mode 100644 Resources/G3DHLSLShaderBasics.hlsl create mode 100644 Resources/G3DHLSLShaderBasics.hlsl.meta create mode 100644 Resources/G3DViewGenHoleFilling.compute create mode 100644 Resources/G3DViewGenHoleFilling.compute.meta create mode 100644 Resources/NewComputeShader.compute create mode 100644 Resources/NewComputeShader.compute.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index f30da60..9044917 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -396,11 +396,12 @@ private void initCustomPass() viewGenerationPass.materialPassName = "G3DViewGeneration"; viewGenerationPass.cameras = cameras; viewGenerationPass.internalCameraCount = internalCameraCount; + viewGenerationPass.depthMosaicHandle = depthMosaicHandle; // add autostereo mosaic generation pass - G3DHDRPViewGenerationMosaicPass customPass = - customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) - as G3DHDRPViewGenerationMosaicPass; + // G3DHDRPViewGenerationMosaicPass customPass = + // customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) + // as G3DHDRPViewGenerationMosaicPass; RenderTexture mosaicTexture = new RenderTexture( mainCamera.pixelWidth, mainCamera.pixelHeight, @@ -410,10 +411,12 @@ private void initCustomPass() format = RenderTextureFormat.ARGB32, depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, }; + mosaicTexture.enableRandomWrite = true; RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); - material.SetTexture("mosaictexture", rtHandleMosaic); - customPass.fullscreenPassMaterial = material; - customPass.materialPassName = "G3DFullScreen3D"; + + // material.SetTexture("mosaictexture", rtHandleMosaic); + // customPass.fullscreenPassMaterial = material; + // customPass.materialPassName = "G3DFullScreen3D"; viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } diff --git a/Resources/G3DDepthMosaic.shader b/Resources/G3DDepthMosaic.shader index 7260e4d..934b640 100644 --- a/Resources/G3DDepthMosaic.shader +++ b/Resources/G3DDepthMosaic.shader @@ -20,6 +20,7 @@ Shader "G3D/DepthMosaic" HLSLPROGRAM #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + #include "G3DHLSLShaderBasics.hlsl" #include "G3DHLSLCommonFunctions.hlsl" #pragma vertex vert diff --git a/Resources/G3DHLSLCommonFunctions.hlsl b/Resources/G3DHLSLCommonFunctions.hlsl index 8d24a53..a398d9b 100644 --- a/Resources/G3DHLSLCommonFunctions.hlsl +++ b/Resources/G3DHLSLCommonFunctions.hlsl @@ -1,30 +1,3 @@ -#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" -#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" - -struct VertAttributes -{ - uint vertexID : SV_VertexID; - UNITY_VERTEX_INPUT_INSTANCE_ID -}; - -struct v2f -{ - float2 uv : TEXCOORD0; - float4 screenPos : SV_POSITION; -}; - -v2f vert(VertAttributes input) -{ - v2f output; - UNITY_SETUP_INSTANCE_ID(input); - UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); - output.uv = GetFullScreenTriangleTexCoord(input.vertexID); - output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); - - return output; - -} - float2 calculateUVForMosaic(int viewIndex, float2 fullScreenUV, int mosaic_rows = 4, int mosaic_columns = 4) { if(viewIndex < 0 ) { diff --git a/Resources/G3DHLSLShaderBasics.hlsl b/Resources/G3DHLSLShaderBasics.hlsl new file mode 100644 index 0000000..73d57a5 --- /dev/null +++ b/Resources/G3DHLSLShaderBasics.hlsl @@ -0,0 +1,26 @@ +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + +struct VertAttributes +{ + uint vertexID : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; + +struct v2f +{ + float2 uv : TEXCOORD0; + float4 screenPos : SV_POSITION; +}; + +v2f vert(VertAttributes input) +{ + v2f output; + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + output.uv = GetFullScreenTriangleTexCoord(input.vertexID); + output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + + return output; + +} \ No newline at end of file diff --git a/Resources/G3DHLSLShaderBasics.hlsl.meta b/Resources/G3DHLSLShaderBasics.hlsl.meta new file mode 100644 index 0000000..b9dd37e --- /dev/null +++ b/Resources/G3DHLSLShaderBasics.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7e489c663c157834ab53e28c0f32e201 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute new file mode 100644 index 0000000..d8c58fd --- /dev/null +++ b/Resources/G3DViewGenHoleFilling.compute @@ -0,0 +1,262 @@ +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" +#include "G3DHLSLCommonFunctions.hlsl" + +#pragma kernel kernelFunction + +Texture2D _depthMosaic; +SamplerState sampler_depthMosaic; +Texture2D _colorMosaic; +SamplerState sampler_colorMosaic; + +// Create a RenderTexture with enableRandomWrite flag and set it +// with cs.SetTexture +RWTexture2D Result; + + +float4x4 inverseProjMatrix0; +float4x4 inverseProjMatrix1; +float4x4 inverseProjMatrix2; +float4x4 inverseProjMatrix3; +float4x4 inverseProjMatrix4; +float4x4 inverseProjMatrix5; +float4x4 inverseProjMatrix6; +float4x4 inverseProjMatrix7; +float4x4 inverseProjMatrix8; +float4x4 inverseProjMatrix9; +float4x4 inverseProjMatrix10; +float4x4 inverseProjMatrix11; +float4x4 inverseProjMatrix12; +float4x4 inverseProjMatrix13; +float4x4 inverseProjMatrix14; +float4x4 inverseProjMatrix15; + +float4x4 viewMatrix0; +float4x4 viewMatrix1; +float4x4 viewMatrix2; +float4x4 viewMatrix3; +float4x4 viewMatrix4; +float4x4 viewMatrix5; +float4x4 viewMatrix6; +float4x4 viewMatrix7; +float4x4 viewMatrix8; +float4x4 viewMatrix9; +float4x4 viewMatrix10; +float4x4 viewMatrix11; +float4x4 viewMatrix12; +float4x4 viewMatrix13; +float4x4 viewMatrix14; +float4x4 viewMatrix15; + + + +int grid_size_x; +int grid_size_y; +int radius; +float sigma; + +int imageWidth; +int imageHeight; +float xStepSize; +float yStepSize; + +float4x4 getInverseViewProjectionMatrix(int viewIndex) { + switch (viewIndex) { + case 0: return inverseProjMatrix0; + case 1: return inverseProjMatrix1; + case 2: return inverseProjMatrix2; + case 3: return inverseProjMatrix3; + case 4: return inverseProjMatrix4; + case 5: return inverseProjMatrix5; + case 6: return inverseProjMatrix6; + case 7: return inverseProjMatrix7; + case 8: return inverseProjMatrix8; + case 9: return inverseProjMatrix9; + case 10: return inverseProjMatrix10; + case 11: return inverseProjMatrix11; + case 12: return inverseProjMatrix12; + case 13: return inverseProjMatrix13; + case 14: return inverseProjMatrix14; + case 15: return inverseProjMatrix15; + default: return float4x4(1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f); + } +} + +float4x4 getViewMatrix(int viewIndex) { + switch (viewIndex) { + case 0: return viewMatrix0; + case 1: return viewMatrix1; + case 2: return viewMatrix2; + case 3: return viewMatrix3; + case 4: return viewMatrix4; + case 5: return viewMatrix5; + case 6: return viewMatrix6; + case 7: return viewMatrix7; + case 8: return viewMatrix8; + case 9: return viewMatrix9; + case 10: return viewMatrix10; + case 11: return viewMatrix11; + case 12: return viewMatrix12; + case 13: return viewMatrix13; + case 14: return viewMatrix14; + case 15: return viewMatrix15; + default: return float4x4(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + } +} + +float get_view_bias(uint viewIndex) { + int maxView = grid_size_x * grid_size_y; + + if (viewIndex == 0) { // leftmost view + return 0.0; + } + float bias = float(viewIndex) / (maxView - 1); + return bias; +} + +// Set the texture array as a shader input +float getCameraLogDepth(float2 fullScreenUV, int viewIndex) { + float2 fragmentUV = calculateUVForMosaic(viewIndex, fullScreenUV, grid_size_y, grid_size_x); + return _depthMosaic.SampleLevel(sampler_depthMosaic, fragmentUV, 0).r; +} + +// here UV is treated as a full screen UV coordinate +float2 calculateProjectedFragmentPosition(float2 uv, int viewIndex, float4x4 viewProjectionMatrix) { + float logDepth = getCameraLogDepth(uv, viewIndex); + // Sample the depth from the Camera depth texture. + float deviceDepth = logDepth; + + // Reconstruct the world space positions. + float3 worldPos = ComputeWorldSpacePosition(uv, deviceDepth, getInverseViewProjectionMatrix(viewIndex)); + + float3 NDC = ComputeNormalizedDeviceCoordinatesWithZ(worldPos, viewProjectionMatrix); // convert from clip space to NDC coordinates + + return float2(NDC.xy); // return the difference between the shifted and original x coordinate +} + +float LinearEyeDepthViewBased(float2 uv, int viewIndex) +{ + float logDepth = getCameraLogDepth(uv, viewIndex); + // Sample the depth from the Camera depth texture. + float deviceDepth = logDepth; + float3 worldPos = ComputeWorldSpacePosition(uv, deviceDepth, getInverseViewProjectionMatrix(viewIndex)); + float eyeDepth = LinearEyeDepth(worldPos, getViewMatrix(viewIndex)); + return eyeDepth; +} + +float4 getColorMosaic(float2 uv, int viewIndex) { + float2 fragmentUV = calculateUVForMosaic(viewIndex, uv, grid_size_y, grid_size_x); + return _colorMosaic.SampleLevel(sampler_colorMosaic, fragmentUV, 0); +} + +float4 fragHDRP (float2 uv) +{ + float2 cellCoordinates = getCellCoordinates(uv, grid_size_x, grid_size_y); + uint viewIndex = getViewIndex(cellCoordinates, grid_size_x, grid_size_y); + float2 cellTexCoords = getCellTexCoords(cellCoordinates); + + // first and last image in the grid are the left and right camera + uint gridCount = grid_size_x * grid_size_y; + if (viewIndex == 0 || viewIndex == gridCount - 1) { + return _colorMosaic.SampleLevel(sampler_colorMosaic, uv, 0); + } + + + + float radius_y = 2; + + float radius_left = 0; + float radius_right = 0; + + + float view_bias = get_view_bias(viewIndex); + if (view_bias > 0.0f) { + radius_right = radius * xStepSize; + radius_left = radius_right / 4.0; + } else { + radius_left = radius * xStepSize; + radius_right = radius_left / 4.0; + } + radius_y = radius * yStepSize; + + float depthAtCenter = LinearEyeDepthViewBased(cellTexCoords, viewIndex); + float4 cellColor = getColorMosaic(cellTexCoords, viewIndex); // sample the color of the left camera texture + + float kernelSum = 0.0f; + float4 colorSum = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (float dx = -radius_left; dx <= radius_right; dx = dx + xStepSize) { + for (float dy = -radius_y; dy <= radius_y; dy = dy + yStepSize) { + float x = clamp(cellTexCoords.x + dx, 0, 1.0f); + float y = clamp(cellTexCoords.y + dy, 0, 1.0f); + + float distance = sqrt(dx * dx + dy * dy); + float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); + // weight = 1.0; + + float2 sampleUV = float2(x, y); + float4 colorSample = getColorMosaic(sampleUV, viewIndex); + + float depth_sample = LinearEyeDepthViewBased(sampleUV, viewIndex); + float4 color_sample = getColorMosaic(sampleUV, viewIndex); + + if (color_sample.w < 1.0f) { + // only collect colors from known values -> skip holes + continue; + } + + if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back + // if center is further at the back then skip sample or reduce weight + + weight = 0.0f; + } else { + // reduce weight with distance to center: + float diff = abs(depthAtCenter - depth_sample); + weight = weight * (diff * diff); + } + + kernelSum += weight; + colorSum += weight * color_sample; + } + } + + if (kernelSum == 0.0f) { + return cellColor; + } + + return float4(colorSum.xyz / kernelSum, 1.0f); // return the average color of the samples +} + +[numthreads(32,32,1)] +void kernelFunction (uint3 id : SV_DispatchThreadID) +{ + int numThreads = 32; + + + float xCohortSize = floor(imageWidth / numThreads); + float yCohortSize = floor(imageHeight / numThreads); + + float xStepSize = 1.0f / (float)imageWidth; + float yStepSize = 1.0f / (float)imageHeight; + + for(int i = 0; i < numThreads; i++) { + for(int j = 0; j < numThreads; j++) { + int x = id.x + i * numThreads; + int y = id.y + j * numThreads; + + if (x >= imageWidth || y >= imageHeight) { + continue; // skip if out of bounds + } + + float2 uv = float2(x * xStepSize, y * yStepSize); + float4 color = fragHDRP(uv); + + Result[float2(x, y)] = color; + } + } +} \ No newline at end of file diff --git a/Resources/G3DViewGenHoleFilling.compute.meta b/Resources/G3DViewGenHoleFilling.compute.meta new file mode 100644 index 0000000..d631719 --- /dev/null +++ b/Resources/G3DViewGenHoleFilling.compute.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8557e7d1d904eab4bbbe5880d0023988 +ComputeShaderImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index 0693662..b4778ec 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -20,6 +20,7 @@ Shader "G3D/ViewGeneration" HLSLPROGRAM #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + #include "G3DHLSLShaderBasics.hlsl" #include "G3DHLSLCommonFunctions.hlsl" #pragma vertex vert @@ -127,7 +128,6 @@ Shader "G3D/ViewGeneration" } } - // Set the texture array as a shader input float getCameraLogDepth(float2 fullScreenUV, int viewIndex) { float2 fragmentUV = calculateUVForMosaic(viewIndex, fullScreenUV, grid_size_y, grid_size_x); return _depthMosaic.Sample(sampler_depthMosaic, fragmentUV).r; @@ -234,7 +234,7 @@ Shader "G3D/ViewGeneration" } if (discardFragmentLeft == 1 && discardFragmentRight == 1) { - discard; + return float4(0.0f, 0.0f, 0.0f, 0.0f); // discard if both fragments are discarded } if (discardFragmentLeft == 1 && discardFragmentRight == 0) { return texture1.Sample(samplertexture1, shiftedRightTexcoords); // sample the right camera texture diff --git a/Resources/NewComputeShader.compute b/Resources/NewComputeShader.compute new file mode 100644 index 0000000..ad8fcb5 --- /dev/null +++ b/Resources/NewComputeShader.compute @@ -0,0 +1,14 @@ +// Each #kernel tells which function to compile; you can have many kernels +#pragma kernel CSMain + +// Create a RenderTexture with enableRandomWrite flag and set it +// with cs.SetTexture +RWTexture2D Result; + +[numthreads(8,8,1)] +void CSMain (uint3 id : SV_DispatchThreadID) +{ + // TODO: insert actual code here! + + Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0.0); +} diff --git a/Resources/NewComputeShader.compute.meta b/Resources/NewComputeShader.compute.meta new file mode 100644 index 0000000..10e7102 --- /dev/null +++ b/Resources/NewComputeShader.compute.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e0d062faab9d74442a479cbd391dc81f +ComputeShaderImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 8f74193..ebd29ba 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -13,8 +13,20 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public int internalCameraCount = 16; public RTHandle mosaicImageHandle; + public RTHandle depthMosaicHandle; - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } + private ComputeShader holeFillingCompShader; + + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) + { + holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); + } + + private void setMatrix(CustomPassContext ctx, Matrix4x4 matrix, string name) + { + ctx.propertyBlock.SetMatrix(Shader.PropertyToID(name), matrix); + ctx.cmd.SetComputeMatrixParam(holeFillingCompShader, name, matrix); + } protected override void Execute(CustomPassContext ctx) { @@ -34,15 +46,9 @@ protected override void Execute(CustomPassContext ctx) Matrix4x4 viewProjectionMatrixInner = projectionMatrixInner * viewMatrixInner; Matrix4x4 invGPUProjMatrix = viewProjectionMatrixInner.inverse; - ctx.propertyBlock.SetMatrix( - Shader.PropertyToID("inverseProjMatrix" + i), - invGPUProjMatrix - ); - ctx.propertyBlock.SetMatrix( - Shader.PropertyToID("viewMatrix" + i), - cameras[i].worldToCameraMatrix - ); + setMatrix(ctx, invGPUProjMatrix, "inverseProjMatrix" + i); + setMatrix(ctx, cameras[i].worldToCameraMatrix, "viewMatrix" + i); } // upload left view projection matrix @@ -52,10 +58,7 @@ protected override void Execute(CustomPassContext ctx) ); Matrix4x4 viewMatrix = cameras[0].worldToCameraMatrix; Matrix4x4 viewProjectionMatrix = projectionMatrix * viewMatrix; - ctx.propertyBlock.SetMatrix( - Shader.PropertyToID("leftViewProjMatrix"), - viewProjectionMatrix - ); + setMatrix(ctx, viewProjectionMatrix, "leftViewProjMatrix"); // upload right view projection matrix Matrix4x4 rightProjectionMatrix = GL.GetGPUProjectionMatrix( @@ -64,11 +67,10 @@ protected override void Execute(CustomPassContext ctx) ); Matrix4x4 rightViewMatrix = cameras[internalCameraCount - 1].worldToCameraMatrix; Matrix4x4 rightViewProjectionMatrix = rightProjectionMatrix * rightViewMatrix; - ctx.propertyBlock.SetMatrix( - Shader.PropertyToID("rightViewProjMatrix"), - rightViewProjectionMatrix - ); + setMatrix(ctx, rightViewProjectionMatrix, "rightViewProjMatrix"); + // TODO remove this line (used to render the mosaic image to screen) + // CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); CoreUtils.DrawFullScreen( ctx.cmd, @@ -76,6 +78,55 @@ protected override void Execute(CustomPassContext ctx) ctx.propertyBlock, shaderPassId: 0 ); + + // fill holes in the mosaic image + int kernel = holeFillingCompShader.FindKernel("kernelFunction"); + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + kernel, + "Result", + mosaicImageHandle + ); + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + kernel, + "_depthMosaic", + depthMosaicHandle + ); + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + kernel, + "_colorMosaic", + mosaicImageHandle + ); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_x", 4); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_y", 4); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", 5); + ctx.cmd.SetComputeFloatParam( + holeFillingCompShader, + "xStepSize", + 1.0f / (float)mosaicImageHandle.rt.width + ); + ctx.cmd.SetComputeFloatParam( + holeFillingCompShader, + "yStepSize", + 1.0f / (float)mosaicImageHandle.rt.height + ); + ctx.cmd.SetComputeIntParam( + holeFillingCompShader, + "imageWidth", + mosaicImageHandle.rt.width + ); + ctx.cmd.SetComputeIntParam( + holeFillingCompShader, + "imageHeight", + mosaicImageHandle.rt.height + ); + + holeFillingCompShader.Dispatch(kernel, 32, 32, 1); + + HDUtils.BlitCameraTexture(ctx.cmd, mosaicImageHandle, ctx.cameraColorBuffer); + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); } } From 30c48d7cac29ef412479b551d85a5e28fe77d913 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 31 Jul 2025 11:36:58 +0200 Subject: [PATCH 015/149] work on compute shader --- G3DCamera.cs | 10 +++++ Resources/G3DViewGenHoleFilling.compute | 53 +++++++++++++------------ Scripts/G3DHDRPViewGenerationPass.cs | 23 +++++------ 3 files changed, 48 insertions(+), 38 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 9044917..925cfd8 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -397,6 +397,16 @@ private void initCustomPass() viewGenerationPass.cameras = cameras; viewGenerationPass.internalCameraCount = internalCameraCount; viewGenerationPass.depthMosaicHandle = depthMosaicHandle; + viewGenerationPass.computeShaderResultTexture = new RenderTexture( + mainCamera.pixelWidth, + mainCamera.pixelHeight, + 0 + ); + viewGenerationPass.computeShaderResultTexture.enableRandomWrite = true; + viewGenerationPass.computeShaderResultTexture.Create(); + viewGenerationPass.computeShaderResultTextureHandle = RTHandles.Alloc( + viewGenerationPass.computeShaderResultTexture + ); // add autostereo mosaic generation pass // G3DHDRPViewGenerationMosaicPass customPass = diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index d8c58fd..b7c4fb5 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -57,8 +57,6 @@ float sigma; int imageWidth; int imageHeight; -float xStepSize; -float yStepSize; float4x4 getInverseViewProjectionMatrix(int viewIndex) { switch (viewIndex) { @@ -155,7 +153,7 @@ float4 getColorMosaic(float2 uv, int viewIndex) { return _colorMosaic.SampleLevel(sampler_colorMosaic, fragmentUV, 0); } -float4 fragHDRP (float2 uv) +float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) { float2 cellCoordinates = getCellCoordinates(uv, grid_size_x, grid_size_y); uint viewIndex = getViewIndex(cellCoordinates, grid_size_x, grid_size_y); @@ -169,29 +167,33 @@ float4 fragHDRP (float2 uv) - float radius_y = 2; - float radius_left = 0; - float radius_right = 0; + float px_radius_left = 0; + float px_radius_right = 0; float view_bias = get_view_bias(viewIndex); if (view_bias > 0.0f) { - radius_right = radius * xStepSize; - radius_left = radius_right / 4.0; + px_radius_right = radius * view_bias; + px_radius_left = px_radius_right / 4.0; } else { - radius_left = radius * xStepSize; - radius_right = radius_left / 4.0; + px_radius_left = radius * view_bias; + px_radius_right = px_radius_left / 4.0; } - radius_y = radius * yStepSize; float depthAtCenter = LinearEyeDepthViewBased(cellTexCoords, viewIndex); float4 cellColor = getColorMosaic(cellTexCoords, viewIndex); // sample the color of the left camera texture + if( cellColor.w >= 1.0f) { + // if the cell color is valid, return the original color + return cellColor; + } float kernelSum = 0.0f; float4 colorSum = float4(0.0f, 0.0f, 0.0f, 0.0f); - for (float dx = -radius_left; dx <= radius_right; dx = dx + xStepSize) { - for (float dy = -radius_y; dy <= radius_y; dy = dy + yStepSize) { + for (int dpxX = -px_radius_left; dpxX <= px_radius_right; ++dpxX) { + for (int dpxY = -radius; dpxY <= radius; ++dpxY) { + float dx = dpxX * xStepSize; + float dy = dpxY * yStepSize; float x = clamp(cellTexCoords.x + dx, 0, 1.0f); float y = clamp(cellTexCoords.y + dy, 0, 1.0f); @@ -225,9 +227,9 @@ float4 fragHDRP (float2 uv) } } - if (kernelSum == 0.0f) { - return cellColor; - } + // if (kernelSum == 0.0f) { + // return float4(1.0f, 0.5f, 0.0f, 1.0f); // return black if no samples were found + // } return float4(colorSum.xyz / kernelSum, 1.0f); // return the average color of the samples } @@ -235,28 +237,29 @@ float4 fragHDRP (float2 uv) [numthreads(32,32,1)] void kernelFunction (uint3 id : SV_DispatchThreadID) { - int numThreads = 32; - + int numThreadsX = 32; + int numThreadsY = 32; - float xCohortSize = floor(imageWidth / numThreads); - float yCohortSize = floor(imageHeight / numThreads); + float xCohortSize = floor((imageWidth + numThreadsX) / numThreadsX); + float yCohortSize = floor((imageHeight + numThreadsY) / numThreadsY); float xStepSize = 1.0f / (float)imageWidth; float yStepSize = 1.0f / (float)imageHeight; - for(int i = 0; i < numThreads; i++) { - for(int j = 0; j < numThreads; j++) { - int x = id.x + i * numThreads; - int y = id.y + j * numThreads; + for(int i = 0; i < xCohortSize; i++) { + for(int j = 0; j < yCohortSize; j++) { + int x = id.x * xCohortSize + i; + int y = id.y * yCohortSize + j; if (x >= imageWidth || y >= imageHeight) { continue; // skip if out of bounds } float2 uv = float2(x * xStepSize, y * yStepSize); - float4 color = fragHDRP(uv); + float4 color = fragHDRP(uv, xStepSize, yStepSize); Result[float2(x, y)] = color; + // Result[float2(x, y)] = float4(uv,0.1f, 1.0f); // write the color to the result texture } } } \ No newline at end of file diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index ebd29ba..aacc516 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -17,6 +17,9 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass private ComputeShader holeFillingCompShader; + public RenderTexture computeShaderResultTexture; + public RTHandle computeShaderResultTextureHandle; + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); @@ -85,7 +88,7 @@ protected override void Execute(CustomPassContext ctx) holeFillingCompShader, kernel, "Result", - mosaicImageHandle + computeShaderResultTexture ); ctx.cmd.SetComputeTextureParam( holeFillingCompShader, @@ -102,16 +105,6 @@ protected override void Execute(CustomPassContext ctx) ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_x", 4); ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_y", 4); ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", 5); - ctx.cmd.SetComputeFloatParam( - holeFillingCompShader, - "xStepSize", - 1.0f / (float)mosaicImageHandle.rt.width - ); - ctx.cmd.SetComputeFloatParam( - holeFillingCompShader, - "yStepSize", - 1.0f / (float)mosaicImageHandle.rt.height - ); ctx.cmd.SetComputeIntParam( holeFillingCompShader, "imageWidth", @@ -123,9 +116,13 @@ protected override void Execute(CustomPassContext ctx) mosaicImageHandle.rt.height ); - holeFillingCompShader.Dispatch(kernel, 32, 32, 1); + ctx.cmd.DispatchCompute(holeFillingCompShader, kernel, 32, 32, 1); - HDUtils.BlitCameraTexture(ctx.cmd, mosaicImageHandle, ctx.cameraColorBuffer); + HDUtils.BlitCameraTexture( + ctx.cmd, + computeShaderResultTextureHandle, + ctx.cameraColorBuffer + ); CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); } } From 37e634bc31a08492792866958fb893f49ed0239f Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 31 Jul 2025 16:15:32 +0200 Subject: [PATCH 016/149] working hole filling --- G3DCamera.cs | 12 ++++----- Resources/G3DViewGenHoleFilling.compute | 36 +++++-------------------- Scripts/G3DHDRPViewGenerationPass.cs | 6 +++-- 3 files changed, 16 insertions(+), 38 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 925cfd8..daec50f 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -409,9 +409,9 @@ private void initCustomPass() ); // add autostereo mosaic generation pass - // G3DHDRPViewGenerationMosaicPass customPass = - // customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) - // as G3DHDRPViewGenerationMosaicPass; + G3DHDRPViewGenerationMosaicPass finalQutostereoGeneration = + customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) + as G3DHDRPViewGenerationMosaicPass; RenderTexture mosaicTexture = new RenderTexture( mainCamera.pixelWidth, mainCamera.pixelHeight, @@ -424,9 +424,9 @@ private void initCustomPass() mosaicTexture.enableRandomWrite = true; RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); - // material.SetTexture("mosaictexture", rtHandleMosaic); - // customPass.fullscreenPassMaterial = material; - // customPass.materialPassName = "G3DFullScreen3D"; + material.SetTexture("mosaictexture", rtHandleMosaic); + finalQutostereoGeneration.fullscreenPassMaterial = material; + finalQutostereoGeneration.materialPassName = "G3DFullScreen3D"; viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index b7c4fb5..555b053 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -108,16 +108,6 @@ float4x4 getViewMatrix(int viewIndex) { } } -float get_view_bias(uint viewIndex) { - int maxView = grid_size_x * grid_size_y; - - if (viewIndex == 0) { // leftmost view - return 0.0; - } - float bias = float(viewIndex) / (maxView - 1); - return bias; -} - // Set the texture array as a shader input float getCameraLogDepth(float2 fullScreenUV, int viewIndex) { float2 fragmentUV = calculateUVForMosaic(viewIndex, fullScreenUV, grid_size_y, grid_size_x); @@ -138,14 +128,14 @@ float2 calculateProjectedFragmentPosition(float2 uv, int viewIndex, float4x4 vie return float2(NDC.xy); // return the difference between the shifted and original x coordinate } -float LinearEyeDepthViewBased(float2 uv, int viewIndex) +float Linear01DepthViewBased(float2 uv, int viewIndex) { float logDepth = getCameraLogDepth(uv, viewIndex); // Sample the depth from the Camera depth texture. float deviceDepth = logDepth; float3 worldPos = ComputeWorldSpacePosition(uv, deviceDepth, getInverseViewProjectionMatrix(viewIndex)); float eyeDepth = LinearEyeDepth(worldPos, getViewMatrix(viewIndex)); - return eyeDepth; + return eyeDepth / _ProjectionParams.z; // convert to linear depth in range [0, 1] } float4 getColorMosaic(float2 uv, int viewIndex) { @@ -172,16 +162,7 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) float px_radius_right = 0; - float view_bias = get_view_bias(viewIndex); - if (view_bias > 0.0f) { - px_radius_right = radius * view_bias; - px_radius_left = px_radius_right / 4.0; - } else { - px_radius_left = radius * view_bias; - px_radius_right = px_radius_left / 4.0; - } - - float depthAtCenter = LinearEyeDepthViewBased(cellTexCoords, viewIndex); + float depthAtCenter = Linear01DepthViewBased(cellTexCoords, viewIndex); float4 cellColor = getColorMosaic(cellTexCoords, viewIndex); // sample the color of the left camera texture if( cellColor.w >= 1.0f) { // if the cell color is valid, return the original color @@ -190,21 +171,20 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) float kernelSum = 0.0f; float4 colorSum = float4(0.0f, 0.0f, 0.0f, 0.0f); - for (int dpxX = -px_radius_left; dpxX <= px_radius_right; ++dpxX) { + for (int dpxX = -radius; dpxX <= radius; ++dpxX) { for (int dpxY = -radius; dpxY <= radius; ++dpxY) { float dx = dpxX * xStepSize; float dy = dpxY * yStepSize; float x = clamp(cellTexCoords.x + dx, 0, 1.0f); float y = clamp(cellTexCoords.y + dy, 0, 1.0f); - float distance = sqrt(dx * dx + dy * dy); + float distance = sqrt(dpxX * dpxX + dpxY * dpxY); float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); // weight = 1.0; float2 sampleUV = float2(x, y); - float4 colorSample = getColorMosaic(sampleUV, viewIndex); - float depth_sample = LinearEyeDepthViewBased(sampleUV, viewIndex); + float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); float4 color_sample = getColorMosaic(sampleUV, viewIndex); if (color_sample.w < 1.0f) { @@ -227,10 +207,6 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) } } - // if (kernelSum == 0.0f) { - // return float4(1.0f, 0.5f, 0.0f, 1.0f); // return black if no samples were found - // } - return float4(colorSum.xyz / kernelSum, 1.0f); // return the average color of the samples } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index aacc516..0917ecb 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -20,6 +20,8 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public RenderTexture computeShaderResultTexture; public RTHandle computeShaderResultTextureHandle; + public int holeFillingRadius = 8; + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); @@ -104,7 +106,8 @@ protected override void Execute(CustomPassContext ctx) ); ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_x", 4); ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_y", 4); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", 5); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); + ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); ctx.cmd.SetComputeIntParam( holeFillingCompShader, "imageWidth", @@ -123,7 +126,6 @@ protected override void Execute(CustomPassContext ctx) computeShaderResultTextureHandle, ctx.cameraColorBuffer ); - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); } } From e89b56dc1f3e41fd342909c293b504404adef998 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 1 Aug 2025 14:49:52 +0200 Subject: [PATCH 017/149] added middle view to reduce holes; made hole filling toggleable; added option to exclude layer from depth maps --- G3DCamera.cs | 30 ++++--- Resources/G3DBlit.shader | 38 ++++++++ Resources/G3DBlit.shader.meta | 9 ++ Resources/G3DViewGeneration.shader | 74 ++++++++------- Scripts/G3DHDRPDepthMapPrePass.cs | 9 ++ Scripts/G3DHDRPViewGenerationPass.cs | 130 ++++++++++++++------------- 6 files changed, 187 insertions(+), 103 deletions(-) create mode 100644 Resources/G3DBlit.shader create mode 100644 Resources/G3DBlit.shader.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index daec50f..df2b98e 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -376,6 +376,7 @@ private void initCustomPass() mainCamera.pixelHeight, 0 ); + depthMosaicTexture.format = RenderTextureFormat.ARGBFloat; depthMosaicTexture.Create(); RTHandle depthMosaicHandle = RTHandles.Alloc(depthMosaicTexture); depthMosaicPass.depthMosaicHandle = depthMosaicHandle; @@ -409,9 +410,9 @@ private void initCustomPass() ); // add autostereo mosaic generation pass - G3DHDRPViewGenerationMosaicPass finalQutostereoGeneration = - customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) - as G3DHDRPViewGenerationMosaicPass; + // G3DHDRPViewGenerationMosaicPass finalAutostereoGeneration = + // customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) + // as G3DHDRPViewGenerationMosaicPass; RenderTexture mosaicTexture = new RenderTexture( mainCamera.pixelWidth, mainCamera.pixelHeight, @@ -425,8 +426,8 @@ private void initCustomPass() RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); material.SetTexture("mosaictexture", rtHandleMosaic); - finalQutostereoGeneration.fullscreenPassMaterial = material; - finalQutostereoGeneration.materialPassName = "G3DFullScreen3D"; + // finalAutostereoGeneration.fullscreenPassMaterial = material; + // finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } @@ -1093,7 +1094,7 @@ void updateCameras() // if generate views only enable the leftmost and rightmost camera if (generateViews) { - if (i == 0 || i == internalCameraCount - 1) + if (i == 0 || i == internalCameraCount / 2 || i == internalCameraCount - 1) { camera.gameObject.SetActive(true); } @@ -1146,10 +1147,18 @@ private bool updateCameraCountBasedOnMode() return false; } + /// + /// adds rendertextres for the cameras to the shader and sets them as target textures for the cameras. + /// + /// + /// + /// + /// only used if view generation is turned on. used to specify the texture name (left, right, middle) private void addRenderTextureToCamera( RenderTexture[] renderTextures, int renderTextureIndex, - int cameraIndex + int cameraIndex, + string texNameInShader = "texture" ) { int width = Screen.width; @@ -1181,7 +1190,7 @@ int cameraIndex if (generateViews) { viewGenerationMaterial.SetTexture( - "texture" + renderTextureIndex, + texNameInShader, renderTextures[renderTextureIndex], RenderTextureSubElement.Color ); @@ -1203,8 +1212,9 @@ public void updateShaderRenderTextures() if (generateViews) { - addRenderTextureToCamera(renderTextures, 0, 0); // right camera - addRenderTextureToCamera(renderTextures, 1, internalCameraCount - 1); // left camera + addRenderTextureToCamera(renderTextures, 0, 0, "_leftCamTex"); // left camera + addRenderTextureToCamera(renderTextures, 1, internalCameraCount / 2, "_middleCamTex"); // middle camera + addRenderTextureToCamera(renderTextures, 2, internalCameraCount - 1, "_rightCamTex"); // right camera } else { diff --git a/Resources/G3DBlit.shader b/Resources/G3DBlit.shader new file mode 100644 index 0000000..6237f75 --- /dev/null +++ b/Resources/G3DBlit.shader @@ -0,0 +1,38 @@ +Shader "G3D/G3DBlit" +{ + SubShader + { + PackageRequirements + { + "com.unity.render-pipelines.high-definition": "unity=2021.3" + } + Tags { "RenderType"="Opaque" "RenderPipeline" = "HDRenderPipeline"} + + Pass + { + Name "G3DBlit" + + ZWrite Off + ZTest Always + Blend Off + Cull Off + + HLSLPROGRAM + #include "G3DHLSLShaderBasics.hlsl" + #include "G3DHLSLCommonFunctions.hlsl" + + #pragma vertex vert + #pragma fragment fragHDRP + + // 0 = left camera, 1 = right camera + Texture2D _mainTex; + SamplerState sampler_mainTex; + + float4 fragHDRP (v2f i) : SV_Target + { + return _mainTex.Sample(sampler_mainTex, i.uv); // sample the main texture + } + ENDHLSL + } + } +} \ No newline at end of file diff --git a/Resources/G3DBlit.shader.meta b/Resources/G3DBlit.shader.meta new file mode 100644 index 0000000..18825d4 --- /dev/null +++ b/Resources/G3DBlit.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b0c81b24f2002d9468f2f132574c55ae +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index b4778ec..3eff6aa 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -27,16 +27,19 @@ Shader "G3D/ViewGeneration" #pragma fragment fragHDRP // 0 = left camera, 1 = right camera - Texture2D texture0; - SamplerState samplertexture0; - Texture2D texture1; - SamplerState samplertexture1; + Texture2D _leftCamTex; + SamplerState sampler_leftCamTex; + Texture2D _middleCamTex; + SamplerState sampler_middleCamTex; + Texture2D _rightCamTex; + SamplerState sampler_rightCamTex; Texture2D _depthMosaic; SamplerState sampler_depthMosaic; float4x4 rightViewProjMatrix; + float4x4 middleViewProjMatrix; float4x4 leftViewProjMatrix; float4x4 inverseProjMatrix0; @@ -182,6 +185,19 @@ Shader "G3D/ViewGeneration" return eyeDepth; } + uint shouldDiscardFragment(float2 shiftedTexCoords, float originalDepth, float actualDepth) + { + uint discardFragment = 0; + if(abs(originalDepth - actualDepth) > 0.1) { + discardFragment = 1; // discard if the depth of the shifted left camera is too far away from the original left camera depth + } + + if(shiftedTexCoords.x < 0 || shiftedTexCoords.x > 1.0f) { + discardFragment = 1; // discard if the tex coord is out of bounds + } + return discardFragment; + } + /// /// creates a grid of views with a given grid size. /// each view is offset by a given disparity value. @@ -197,10 +213,13 @@ Shader "G3D/ViewGeneration" // first and last image in the grid are the left and right camera uint gridCount = grid_size_x * grid_size_y; if (viewIndex == 0) { - return texture0.Sample(samplertexture0, cellTexCoords); // sample the left camera texture + return _leftCamTex.Sample(sampler_leftCamTex, cellTexCoords); // sample the left camera texture + } + if (viewIndex == gridCount / 2) { + return _middleCamTex.Sample(sampler_middleCamTex, cellTexCoords); // sample the middle camera texture } if (viewIndex == gridCount - 1) { - return texture1.Sample(samplertexture1, cellTexCoords); // sample the right camera texture + return _rightCamTex.Sample(sampler_rightCamTex, cellTexCoords); // sample the right camera texture } // ----------------------------------------------------------------------------------------------------------- @@ -208,42 +227,31 @@ Shader "G3D/ViewGeneration" float2 shiftedLeftTexcoords = calculateProjectedFragmentPosition(cellTexCoords, viewIndex, leftViewProjMatrix); // convert to pixel space float originalLeftDepth = LinearEyeDepthViewBased(shiftedLeftTexcoords, 0); // sample the depth of the original left camera - float actualDepth = LinearEyeDepthViewBased(cellTexCoords, viewIndex); // sample the depth of the shifted left camera + + float2 shiftedMiddleTexcoords = calculateProjectedFragmentPosition(cellTexCoords, viewIndex, middleViewProjMatrix); // convert to pixel space + float originalMiddleDepth = LinearEyeDepthViewBased(shiftedMiddleTexcoords, gridCount / 2); // sample the depth of the original middle camera float2 shiftedRightTexcoords = calculateProjectedFragmentPosition(cellTexCoords, viewIndex, rightViewProjMatrix); // convert to pixel space float originalRightDepth = LinearEyeDepthViewBased(shiftedRightTexcoords, gridCount - 1); // sample the depth of the original right camera + + float actualDepth = LinearEyeDepthViewBased(cellTexCoords, viewIndex); // sample the depth of the shifted left camera + uint discardFragmentLeft = shouldDiscardFragment(shiftedLeftTexcoords, originalLeftDepth, actualDepth); + uint discardFragmentMiddle = shouldDiscardFragment(shiftedMiddleTexcoords, originalMiddleDepth, actualDepth); + uint discardFragmentRight = shouldDiscardFragment(shiftedRightTexcoords, originalRightDepth, actualDepth); - uint discardFragmentLeft = 0; - if(abs(originalLeftDepth - actualDepth) > 0.1) { - discardFragmentLeft = 1; // discard if the depth of the shifted left camera is too far away from the original left camera depth - } - - if(shiftedLeftTexcoords.x < 0 || shiftedLeftTexcoords.x > 1.0f) { - discardFragmentLeft = 1; // discard if the tex coord is out of bounds - } - - - uint discardFragmentRight = 0; - if(abs(originalRightDepth - actualDepth) > 0.1) { - discardFragmentRight = 1; // discard if the depth of the shifted right camera is too far away from the original right camera depth - } - - if(shiftedRightTexcoords.x < 0 || shiftedRightTexcoords.x > 1.0f) { - discardFragmentRight = 1; // discard if the tex coord is out of bounds - } - - if (discardFragmentLeft == 1 && discardFragmentRight == 1) { - return float4(0.0f, 0.0f, 0.0f, 0.0f); // discard if both fragments are discarded + float4 finalColor = float4(0.0f, 0.0f, 0.0f, 0.0f); + if (!discardFragmentLeft) { + finalColor = _leftCamTex.Sample(sampler_leftCamTex, shiftedLeftTexcoords); // sample the left camera texture } - if (discardFragmentLeft == 1 && discardFragmentRight == 0) { - return texture1.Sample(samplertexture1, shiftedRightTexcoords); // sample the right camera texture + if (!discardFragmentRight) { + finalColor = _rightCamTex.Sample(sampler_rightCamTex, shiftedRightTexcoords); // sample the right camera texture } - if (discardFragmentLeft == 0 && discardFragmentRight == 1) { - return texture0.Sample(samplertexture0, shiftedLeftTexcoords); // sample the left camera texture + if( !discardFragmentMiddle && finalColor.a == 0.0f) { // if the final color is not set yet, sample the middle camera texture + finalColor = _middleCamTex.Sample(sampler_middleCamTex, shiftedMiddleTexcoords); // sample the middle camera texture } - return texture1.Sample(samplertexture1, shiftedRightTexcoords); + return finalColor; } ENDHLSL } diff --git a/Scripts/G3DHDRPDepthMapPrePass.cs b/Scripts/G3DHDRPDepthMapPrePass.cs index ee626a2..253292f 100644 --- a/Scripts/G3DHDRPDepthMapPrePass.cs +++ b/Scripts/G3DHDRPDepthMapPrePass.cs @@ -12,6 +12,8 @@ internal class G3DHDRPDepthMapPrePass : FullScreenCustomPass RenderTexture[] indivDepthTextures; public RTHandle depthMosaicHandle; + public bool excludeLayer = false; + public int layerToExclude = 3; protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { @@ -54,6 +56,13 @@ protected override void Execute(CustomPassContext ctx) ); bakingCamera.TryGetCullingParameters(out var cullingParams); cullingParams.cullingOptions = CullingOptions.None; + camera.cullingMask &= ~(1 << bakingCamera.gameObject.layer); + + if (excludeLayer) + { + int layerMask = 1 << layerToExclude; + cullingParams.cullingMask = ~(uint)layerMask; + } // Assign the custom culling result to the context // so it'll be used for the following operations diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 0917ecb..d0d77fe 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -20,11 +20,17 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public RenderTexture computeShaderResultTexture; public RTHandle computeShaderResultTextureHandle; - public int holeFillingRadius = 8; + public int holeFillingRadius = 1; + + public bool fillHoles = false; + + private Material blitMaterial; protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); + blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); + blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); } private void setMatrix(CustomPassContext ctx, Matrix4x4 matrix, string name) @@ -33,6 +39,14 @@ private void setMatrix(CustomPassContext ctx, Matrix4x4 matrix, string name) ctx.cmd.SetComputeMatrixParam(holeFillingCompShader, name, matrix); } + private void addViewProjectionMatrix(CustomPassContext ctx, Camera camera, string name) + { + Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false); + Matrix4x4 viewMatrix = camera.worldToCameraMatrix; + Matrix4x4 viewProjectionMatrix = projectionMatrix * viewMatrix; + setMatrix(ctx, viewProjectionMatrix, name); + } + protected override void Execute(CustomPassContext ctx) { var camera = ctx.hdCamera.camera; @@ -56,27 +70,19 @@ protected override void Execute(CustomPassContext ctx) setMatrix(ctx, cameras[i].worldToCameraMatrix, "viewMatrix" + i); } - // upload left view projection matrix - Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix( - cameras[0].projectionMatrix, - false - ); - Matrix4x4 viewMatrix = cameras[0].worldToCameraMatrix; - Matrix4x4 viewProjectionMatrix = projectionMatrix * viewMatrix; - setMatrix(ctx, viewProjectionMatrix, "leftViewProjMatrix"); - - // upload right view projection matrix - Matrix4x4 rightProjectionMatrix = GL.GetGPUProjectionMatrix( - cameras[internalCameraCount - 1].projectionMatrix, - false - ); - Matrix4x4 rightViewMatrix = cameras[internalCameraCount - 1].worldToCameraMatrix; - Matrix4x4 rightViewProjectionMatrix = rightProjectionMatrix * rightViewMatrix; - setMatrix(ctx, rightViewProjectionMatrix, "rightViewProjMatrix"); + addViewProjectionMatrix(ctx, cameras[0], "leftViewProjMatrix"); + addViewProjectionMatrix(ctx, cameras[internalCameraCount / 2], "middleViewProjMatrix"); + addViewProjectionMatrix(ctx, cameras[internalCameraCount - 1], "rightViewProjMatrix"); // TODO remove this line (used to render the mosaic image to screen) - // CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + if (fillHoles) + { + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + } + else + { + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + } CoreUtils.DrawFullScreen( ctx.cmd, fullscreenPassMaterial, @@ -84,48 +90,52 @@ protected override void Execute(CustomPassContext ctx) shaderPassId: 0 ); - // fill holes in the mosaic image - int kernel = holeFillingCompShader.FindKernel("kernelFunction"); - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - kernel, - "Result", - computeShaderResultTexture - ); - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - kernel, - "_depthMosaic", - depthMosaicHandle - ); - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - kernel, - "_colorMosaic", - mosaicImageHandle - ); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_x", 4); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_y", 4); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); - ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); - ctx.cmd.SetComputeIntParam( - holeFillingCompShader, - "imageWidth", - mosaicImageHandle.rt.width - ); - ctx.cmd.SetComputeIntParam( - holeFillingCompShader, - "imageHeight", - mosaicImageHandle.rt.height - ); + if (fillHoles) + { + // fill holes in the mosaic image + int kernel = holeFillingCompShader.FindKernel("kernelFunction"); + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + kernel, + "Result", + computeShaderResultTexture + ); + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + kernel, + "_depthMosaic", + depthMosaicHandle + ); + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + kernel, + "_colorMosaic", + mosaicImageHandle + ); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_x", 4); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_y", 4); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); + ctx.cmd.SetComputeFloatParam( + holeFillingCompShader, + "sigma", + holeFillingRadius / 2.0f + ); + ctx.cmd.SetComputeIntParam( + holeFillingCompShader, + "imageWidth", + mosaicImageHandle.rt.width + ); + ctx.cmd.SetComputeIntParam( + holeFillingCompShader, + "imageHeight", + mosaicImageHandle.rt.height + ); - ctx.cmd.DispatchCompute(holeFillingCompShader, kernel, 32, 32, 1); + ctx.cmd.DispatchCompute(holeFillingCompShader, kernel, 32, 32, 1); - HDUtils.BlitCameraTexture( - ctx.cmd, - computeShaderResultTextureHandle, - ctx.cameraColorBuffer - ); + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); + } } } From 1f77bae792ba7fc7b36c563b9b3462c61c141ca9 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 4 Aug 2025 15:03:04 +0200 Subject: [PATCH 018/149] cleanup --- G3DCamera.cs | 10 +++++----- Resources/G3DViewGeneration.shader | 25 ------------------------- Scripts/G3DHDRPDepthMapPrePass.cs | 6 +++--- Scripts/G3DHDRPViewGenerationPass.cs | 11 ++++++----- 4 files changed, 14 insertions(+), 38 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index df2b98e..e416d2b 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -410,9 +410,9 @@ private void initCustomPass() ); // add autostereo mosaic generation pass - // G3DHDRPViewGenerationMosaicPass finalAutostereoGeneration = - // customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) - // as G3DHDRPViewGenerationMosaicPass; + G3DHDRPViewGenerationMosaicPass finalAutostereoGeneration = + customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) + as G3DHDRPViewGenerationMosaicPass; RenderTexture mosaicTexture = new RenderTexture( mainCamera.pixelWidth, mainCamera.pixelHeight, @@ -426,8 +426,8 @@ private void initCustomPass() RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); material.SetTexture("mosaictexture", rtHandleMosaic); - // finalAutostereoGeneration.fullscreenPassMaterial = material; - // finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; + finalAutostereoGeneration.fullscreenPassMaterial = material; + finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index 3eff6aa..a4f2049 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -134,31 +134,6 @@ Shader "G3D/ViewGeneration" float getCameraLogDepth(float2 fullScreenUV, int viewIndex) { float2 fragmentUV = calculateUVForMosaic(viewIndex, fullScreenUV, grid_size_y, grid_size_x); return _depthMosaic.Sample(sampler_depthMosaic, fragmentUV).r; - - { - - // Test if bluring the depth map removes the banding artifacts - // const float offset[] = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}; - // const float weight[] = {0.191, 0.15, 0.092, 0.044, 0.017, 0.005, 0.001}; - // float4 FragmentColor = float4(0.0f, 0.0f, 0.0f, 0.0f); - // // 2619 x 1387 - // //(1.0, 0.0) -> horizontal blur - // //(0.0, 1.0) -> vertical blur - - // float hstep = 1.0 / 2619; - // float vstep = 1.0 / 1387; - - // for (int i = 1; i < 5; i++) { - // FragmentColor += - // _depthMosaic.Sample(sampler_depthMosaic, fragmentUV + float2(hstep*offset[i], vstep*offset[i]))*weight[i] + - // _depthMosaic.Sample(sampler_depthMosaic, fragmentUV - float2(hstep*offset[i], vstep*offset[i]))*weight[i]; - // } - // float4 ppColour = _depthMosaic.Sample(sampler_depthMosaic, fragmentUV) * weight[0]; - // ppColour += FragmentColor; - - - // return ppColour.r;//_depthMosaic.Sample(sampler_depthMosaic, fragmentUV).r; - } } // here UV is treated as a full screen UV coordinate diff --git a/Scripts/G3DHDRPDepthMapPrePass.cs b/Scripts/G3DHDRPDepthMapPrePass.cs index 253292f..b03d826 100644 --- a/Scripts/G3DHDRPDepthMapPrePass.cs +++ b/Scripts/G3DHDRPDepthMapPrePass.cs @@ -21,9 +21,9 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff for (int i = 0; i < internalCameraCount; i++) { RenderTexture depthTexture = new RenderTexture( - cameras[0].pixelWidth, - cameras[0].pixelHeight, - 32, + cameras[0].pixelWidth / 4, + cameras[0].pixelHeight / 4, + 0, RenderTextureFormat.Depth ); depthTexture.Create(); diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index d0d77fe..211deb6 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -20,10 +20,12 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public RenderTexture computeShaderResultTexture; public RTHandle computeShaderResultTextureHandle; - public int holeFillingRadius = 1; + public int holeFillingRadius = 3; public bool fillHoles = false; + public bool debugRendering = false; + private Material blitMaterial; protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) @@ -74,14 +76,13 @@ protected override void Execute(CustomPassContext ctx) addViewProjectionMatrix(ctx, cameras[internalCameraCount / 2], "middleViewProjMatrix"); addViewProjectionMatrix(ctx, cameras[internalCameraCount - 1], "rightViewProjMatrix"); - // TODO remove this line (used to render the mosaic image to screen) - if (fillHoles) + if (debugRendering) { - CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); } else { - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); } CoreUtils.DrawFullScreen( ctx.cmd, From 799b93628b7c772dc227010658211df8ef473d2a Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 5 Aug 2025 15:54:30 +0200 Subject: [PATCH 019/149] improved depth map handling; code cleanup --- G3DCamera.cs | 75 ++++++++------- Resources/G3DDepthMosaic.shader | 122 ------------------------ Resources/G3DDepthMosaic.shader.meta | 9 -- Resources/G3DViewGenHoleFilling.compute | 65 +++++++++++-- Resources/G3DViewGeneration.shader | 85 ++++++++++++++--- Resources/NewComputeShader.compute | 14 --- Resources/NewComputeShader.compute.meta | 7 -- Scripts/G3DHDRPDepthMapPrePass.cs | 33 +------ Scripts/G3DHDRPViewGenerationPass.cs | 37 ++++--- 9 files changed, 199 insertions(+), 248 deletions(-) delete mode 100644 Resources/G3DDepthMosaic.shader delete mode 100644 Resources/G3DDepthMosaic.shader.meta delete mode 100644 Resources/NewComputeShader.compute delete mode 100644 Resources/NewComputeShader.compute.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index e416d2b..35f4579 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -349,6 +349,10 @@ void Start() #if G3D_HDRP private void initCustomPass() { + bool debugRendering = false; + bool isHoleFilling = false; + int holeFillingRadius = 3; + // init fullscreen postprocessing for hd render pipeline CustomPassVolume customPassVolume = gameObject.AddComponent(); customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; @@ -362,24 +366,21 @@ private void initCustomPass() G3DHDRPDepthMapPrePass depthMosaicPass = customPassVolume.AddPassOfType(typeof(G3DHDRPDepthMapPrePass)) as G3DHDRPDepthMapPrePass; - Material depthMosaicMaterial = new Material(Shader.Find("G3D/DepthMosaic")); - depthMosaicMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); - depthMosaicMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); - - depthMosaicPass.fullscreenPassMaterial = depthMosaicMaterial; - depthMosaicPass.materialPassName = "G3DDepthMosaic"; depthMosaicPass.cameras = cameras; depthMosaicPass.internalCameraCount = internalCameraCount; - RenderTexture depthMosaicTexture = new RenderTexture( - mainCamera.pixelWidth, - mainCamera.pixelHeight, - 0 - ); - depthMosaicTexture.format = RenderTextureFormat.ARGBFloat; - depthMosaicTexture.Create(); - RTHandle depthMosaicHandle = RTHandles.Alloc(depthMosaicTexture); - depthMosaicPass.depthMosaicHandle = depthMosaicHandle; + depthMosaicPass.indivDepthTextures = new RenderTexture[internalCameraCount]; + for (int i = 0; i < internalCameraCount; i++) + { + RenderTexture depthTexture = new RenderTexture( + cameras[0].pixelWidth, + cameras[0].pixelHeight, + 0, + RenderTextureFormat.Depth + ); + depthTexture.Create(); + depthMosaicPass.indivDepthTextures[i] = depthTexture; + } // add multiview generation pass G3DHDRPViewGenerationPass viewGenerationPass = @@ -388,16 +389,11 @@ private void initCustomPass() viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); - viewGenerationMaterial.SetTexture( - Shader.PropertyToID("_depthMosaic"), - depthMosaicHandle - ); viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; viewGenerationPass.materialPassName = "G3DViewGeneration"; viewGenerationPass.cameras = cameras; viewGenerationPass.internalCameraCount = internalCameraCount; - viewGenerationPass.depthMosaicHandle = depthMosaicHandle; viewGenerationPass.computeShaderResultTexture = new RenderTexture( mainCamera.pixelWidth, mainCamera.pixelHeight, @@ -408,27 +404,40 @@ private void initCustomPass() viewGenerationPass.computeShaderResultTextureHandle = RTHandles.Alloc( viewGenerationPass.computeShaderResultTexture ); + for (int i = 0; i < internalCameraCount; i++) + { + viewGenerationPass.fullscreenPassMaterial.SetTexture( + "_depthMap" + i, + depthMosaicPass.indivDepthTextures[i], + RenderTextureSubElement.Depth + ); + } + viewGenerationPass.indivDepthMaps = depthMosaicPass.indivDepthTextures; + viewGenerationPass.debugRendering = debugRendering; + viewGenerationPass.fillHoles = isHoleFilling; + viewGenerationPass.holeFillingRadius = holeFillingRadius; // add autostereo mosaic generation pass - G3DHDRPViewGenerationMosaicPass finalAutostereoGeneration = - customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) - as G3DHDRPViewGenerationMosaicPass; RenderTexture mosaicTexture = new RenderTexture( mainCamera.pixelWidth, mainCamera.pixelHeight, - 0 - ) - { - format = RenderTextureFormat.ARGB32, - depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, - }; + 0, + RenderTextureFormat.ARGB32, + RenderTextureReadWrite.sRGB + ); mosaicTexture.enableRandomWrite = true; - RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); - material.SetTexture("mosaictexture", rtHandleMosaic); - finalAutostereoGeneration.fullscreenPassMaterial = material; - finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; + if (debugRendering == false) + { + G3DHDRPViewGenerationMosaicPass finalAutostereoGeneration = + customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) + as G3DHDRPViewGenerationMosaicPass; + finalAutostereoGeneration.fullscreenPassMaterial = material; + finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; + } + RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); + material.SetTexture("mosaictexture", rtHandleMosaic); viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } else diff --git a/Resources/G3DDepthMosaic.shader b/Resources/G3DDepthMosaic.shader deleted file mode 100644 index 934b640..0000000 --- a/Resources/G3DDepthMosaic.shader +++ /dev/null @@ -1,122 +0,0 @@ -Shader "G3D/DepthMosaic" -{ - SubShader - { - PackageRequirements - { - "com.unity.render-pipelines.high-definition": "unity=2021.3" - } - Tags { "RenderType"="Opaque" "RenderPipeline" = "HDRenderPipeline"} - - Pass - { - Name "G3DDepthMosaic" - - ZWrite Off - ZTest Always - Blend Off - Cull Off - - HLSLPROGRAM - #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" - #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" - #include "G3DHLSLShaderBasics.hlsl" - #include "G3DHLSLCommonFunctions.hlsl" - - #pragma vertex vert - #pragma fragment fragHDRP - - - Texture2D _depthMap0; - SamplerState sampler_depthMap0; - Texture2D _depthMap1; - SamplerState sampler_depthMap1; - Texture2D _depthMap2; - SamplerState sampler_depthMap2; - Texture2D _depthMap3; - SamplerState sampler_depthMap3; - Texture2D _depthMap4; - SamplerState sampler_depthMap4; - Texture2D _depthMap5; - SamplerState sampler_depthMap5; - Texture2D _depthMap6; - SamplerState sampler_depthMap6; - Texture2D _depthMap7; - SamplerState sampler_depthMap7; - Texture2D _depthMap8; - SamplerState sampler_depthMap8; - Texture2D _depthMap9; - SamplerState sampler_depthMap9; - Texture2D _depthMap10; - SamplerState sampler_depthMap10; - Texture2D _depthMap11; - SamplerState sampler_depthMap11; - Texture2D _depthMap12; - SamplerState sampler_depthMap12; - Texture2D _depthMap13; - SamplerState sampler_depthMap13; - Texture2D _depthMap14; - SamplerState sampler_depthMap14; - Texture2D _depthMap15; - SamplerState sampler_depthMap15; - - int grid_size_x; - int grid_size_y; - - // Set the texture array as a shader input - float getCameraLogDepth(float2 uv, int cameraIndex) { - switch(cameraIndex) { - case 0: - return _depthMap0.Sample( sampler_depthMap0, uv).r; - case 1: - return _depthMap1.Sample( sampler_depthMap1, uv).r; - case 2: - return _depthMap2.Sample( sampler_depthMap2, uv).r; - case 3: - return _depthMap3.Sample( sampler_depthMap3, uv).r; - case 4: - return _depthMap4.Sample( sampler_depthMap4, uv).r; - case 5: - return _depthMap5.Sample( sampler_depthMap5, uv).r; - case 6: - return _depthMap6.Sample( sampler_depthMap6, uv).r; - case 7: - return _depthMap7.Sample( sampler_depthMap7, uv).r; - case 8: - return _depthMap8.Sample( sampler_depthMap8, uv).r; - case 9: - return _depthMap9.Sample( sampler_depthMap9, uv).r; - case 10: - return _depthMap10.Sample( sampler_depthMap10, uv).r; - case 11: - return _depthMap11.Sample( sampler_depthMap11, uv).r; - case 12: - return _depthMap12.Sample( sampler_depthMap12, uv).r; - case 13: - return _depthMap13.Sample( sampler_depthMap13, uv).r; - case 14: - return _depthMap14.Sample( sampler_depthMap14, uv).r; - case 15: - return _depthMap15.Sample( sampler_depthMap15, uv).r; - default: - return _depthMap0.Sample( sampler_depthMap0, uv).r; // use left camera depth map as default - } - - } - - /// - /// combines the original depthMaps into one depth map that contains all views - /// - float4 fragHDRP (v2f i) : SV_Target - { - float2 cellCoordinates = getCellCoordinates(i.uv, grid_size_x, grid_size_y); - uint viewIndex = getViewIndex(cellCoordinates, grid_size_x, grid_size_y); - float2 cellTexCoords = getCellTexCoords(cellCoordinates); - - float depth = getCameraLogDepth(cellTexCoords, viewIndex); - return float4(depth, depth, depth, 1.0f); // return the depth value as color - } - ENDHLSL - } - } -} \ No newline at end of file diff --git a/Resources/G3DDepthMosaic.shader.meta b/Resources/G3DDepthMosaic.shader.meta deleted file mode 100644 index e585f93..0000000 --- a/Resources/G3DDepthMosaic.shader.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: dd136f9af67cd0149a6bfbee9674b42e -ShaderImporter: - externalObjects: {} - defaultTextures: [] - nonModifiableTextures: [] - userData: - assetBundleName: - assetBundleVariant: diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index 555b053..7d8c635 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -58,6 +58,65 @@ float sigma; int imageWidth; int imageHeight; +Texture2D _depthMap0; +Texture2D _depthMap1; +Texture2D _depthMap2; +Texture2D _depthMap3; +Texture2D _depthMap4; +Texture2D _depthMap5; +Texture2D _depthMap6; +Texture2D _depthMap7; +Texture2D _depthMap8; +Texture2D _depthMap9; +Texture2D _depthMap10; +Texture2D _depthMap11; +Texture2D _depthMap12; +Texture2D _depthMap13; +Texture2D _depthMap14; +Texture2D _depthMap15; + +SamplerState sampler_linear_repeat; + +// Set the texture array as a shader input +float getCameraLogDepth(float2 uv, int cameraIndex) { + switch(cameraIndex) { + case 0: + return _depthMap0.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 1: + return _depthMap1.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 2: + return _depthMap2.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 3: + return _depthMap3.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 4: + return _depthMap4.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 5: + return _depthMap5.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 6: + return _depthMap6.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 7: + return _depthMap7.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 8: + return _depthMap8.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 9: + return _depthMap9.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 10: + return _depthMap10.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 11: + return _depthMap11.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 12: + return _depthMap12.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 13: + return _depthMap13.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 14: + return _depthMap14.SampleLevel(sampler_linear_repeat, uv, 0).r; + case 15: + return _depthMap15.SampleLevel(sampler_linear_repeat, uv, 0).r; + default: + return _depthMap0.SampleLevel(sampler_linear_repeat, uv, 0).r; // use left camera depth map as default + } +} + float4x4 getInverseViewProjectionMatrix(int viewIndex) { switch (viewIndex) { case 0: return inverseProjMatrix0; @@ -108,12 +167,6 @@ float4x4 getViewMatrix(int viewIndex) { } } -// Set the texture array as a shader input -float getCameraLogDepth(float2 fullScreenUV, int viewIndex) { - float2 fragmentUV = calculateUVForMosaic(viewIndex, fullScreenUV, grid_size_y, grid_size_x); - return _depthMosaic.SampleLevel(sampler_depthMosaic, fragmentUV, 0).r; -} - // here UV is treated as a full screen UV coordinate float2 calculateProjectedFragmentPosition(float2 uv, int viewIndex, float4x4 viewProjectionMatrix) { float logDepth = getCameraLogDepth(uv, viewIndex); diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index a4f2049..1277702 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -34,10 +34,6 @@ Shader "G3D/ViewGeneration" Texture2D _rightCamTex; SamplerState sampler_rightCamTex; - Texture2D _depthMosaic; - SamplerState sampler_depthMosaic; - - float4x4 rightViewProjMatrix; float4x4 middleViewProjMatrix; float4x4 leftViewProjMatrix; @@ -76,6 +72,65 @@ Shader "G3D/ViewGeneration" float4x4 viewMatrix14; float4x4 viewMatrix15; + Texture2D _depthMap0; + Texture2D _depthMap1; + Texture2D _depthMap2; + Texture2D _depthMap3; + Texture2D _depthMap4; + Texture2D _depthMap5; + Texture2D _depthMap6; + Texture2D _depthMap7; + Texture2D _depthMap8; + Texture2D _depthMap9; + Texture2D _depthMap10; + Texture2D _depthMap11; + Texture2D _depthMap12; + Texture2D _depthMap13; + Texture2D _depthMap14; + Texture2D _depthMap15; + + SamplerState sampler_linear_repeat; + + // Set the texture array as a shader input + float getCameraLogDepth(float2 uv, int cameraIndex) { + switch(cameraIndex) { + case 0: + return _depthMap0.Sample( sampler_linear_repeat, uv).r; + case 1: + return _depthMap1.Sample( sampler_linear_repeat, uv).r; + case 2: + return _depthMap2.Sample( sampler_linear_repeat, uv).r; + case 3: + return _depthMap3.Sample( sampler_linear_repeat, uv).r; + case 4: + return _depthMap4.Sample( sampler_linear_repeat, uv).r; + case 5: + return _depthMap5.Sample( sampler_linear_repeat, uv).r; + case 6: + return _depthMap6.Sample( sampler_linear_repeat, uv).r; + case 7: + return _depthMap7.Sample( sampler_linear_repeat, uv).r; + case 8: + return _depthMap8.Sample( sampler_linear_repeat, uv).r; + case 9: + return _depthMap9.Sample( sampler_linear_repeat, uv).r; + case 10: + return _depthMap10.Sample( sampler_linear_repeat, uv).r; + case 11: + return _depthMap11.Sample( sampler_linear_repeat, uv).r; + case 12: + return _depthMap12.Sample( sampler_linear_repeat, uv).r; + case 13: + return _depthMap13.Sample( sampler_linear_repeat, uv).r; + case 14: + return _depthMap14.Sample( sampler_linear_repeat, uv).r; + case 15: + return _depthMap15.Sample( sampler_linear_repeat, uv).r; + default: + return _depthMap0.Sample( sampler_linear_repeat, uv).r; // use left camera depth map as default + } + } + int grid_size_x; @@ -131,11 +186,6 @@ Shader "G3D/ViewGeneration" } } - float getCameraLogDepth(float2 fullScreenUV, int viewIndex) { - float2 fragmentUV = calculateUVForMosaic(viewIndex, fullScreenUV, grid_size_y, grid_size_x); - return _depthMosaic.Sample(sampler_depthMosaic, fragmentUV).r; - } - // here UV is treated as a full screen UV coordinate float2 calculateProjectedFragmentPosition(float2 uv, int viewIndex, float4x4 viewProjectionMatrix) { float logDepth = getCameraLogDepth(uv, viewIndex); @@ -185,6 +235,9 @@ Shader "G3D/ViewGeneration" uint viewIndex = getViewIndex(cellCoordinates, grid_size_x, grid_size_y); float2 cellTexCoords = getCellTexCoords(cellCoordinates); + // float logDepth = getCameraLogDepth(cellTexCoords, viewIndex); + // return float4(logDepth, logDepth, logDepth, 1.0f); // return the log depth for debugging purposes + // first and last image in the grid are the left and right camera uint gridCount = grid_size_x * grid_size_y; if (viewIndex == 0) { @@ -216,17 +269,21 @@ Shader "G3D/ViewGeneration" uint discardFragmentRight = shouldDiscardFragment(shiftedRightTexcoords, originalRightDepth, actualDepth); float4 finalColor = float4(0.0f, 0.0f, 0.0f, 0.0f); + float factor = 0.0f; if (!discardFragmentLeft) { - finalColor = _leftCamTex.Sample(sampler_leftCamTex, shiftedLeftTexcoords); // sample the left camera texture + finalColor += _leftCamTex.Sample(sampler_leftCamTex, shiftedLeftTexcoords); // sample the left camera texture + factor += 1.0f; // increase the factor for the left camera } if (!discardFragmentRight) { - finalColor = _rightCamTex.Sample(sampler_rightCamTex, shiftedRightTexcoords); // sample the right camera texture + finalColor += _rightCamTex.Sample(sampler_rightCamTex, shiftedRightTexcoords); // sample the right camera texture + factor += 1.0f; // increase the factor for the right camera } - if( !discardFragmentMiddle && finalColor.a == 0.0f) { // if the final color is not set yet, sample the middle camera texture - finalColor = _middleCamTex.Sample(sampler_middleCamTex, shiftedMiddleTexcoords); // sample the middle camera texture + if( !discardFragmentMiddle) { // if the final color is not set yet, sample the middle camera texture + finalColor += _middleCamTex.Sample(sampler_middleCamTex, shiftedMiddleTexcoords); // sample the middle camera texture + factor += 1.0f; // increase the factor for the middle camera } - return finalColor; + return finalColor / factor; } ENDHLSL } diff --git a/Resources/NewComputeShader.compute b/Resources/NewComputeShader.compute deleted file mode 100644 index ad8fcb5..0000000 --- a/Resources/NewComputeShader.compute +++ /dev/null @@ -1,14 +0,0 @@ -// Each #kernel tells which function to compile; you can have many kernels -#pragma kernel CSMain - -// Create a RenderTexture with enableRandomWrite flag and set it -// with cs.SetTexture -RWTexture2D Result; - -[numthreads(8,8,1)] -void CSMain (uint3 id : SV_DispatchThreadID) -{ - // TODO: insert actual code here! - - Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0.0); -} diff --git a/Resources/NewComputeShader.compute.meta b/Resources/NewComputeShader.compute.meta deleted file mode 100644 index 10e7102..0000000 --- a/Resources/NewComputeShader.compute.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: e0d062faab9d74442a479cbd391dc81f -ComputeShaderImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Scripts/G3DHDRPDepthMapPrePass.cs b/Scripts/G3DHDRPDepthMapPrePass.cs index b03d826..b7c23ee 100644 --- a/Scripts/G3DHDRPDepthMapPrePass.cs +++ b/Scripts/G3DHDRPDepthMapPrePass.cs @@ -9,32 +9,12 @@ internal class G3DHDRPDepthMapPrePass : FullScreenCustomPass public List cameras; public int internalCameraCount = 16; - RenderTexture[] indivDepthTextures; + public RenderTexture[] indivDepthTextures; - public RTHandle depthMosaicHandle; public bool excludeLayer = false; public int layerToExclude = 3; - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) - { - indivDepthTextures = new RenderTexture[internalCameraCount]; - for (int i = 0; i < internalCameraCount; i++) - { - RenderTexture depthTexture = new RenderTexture( - cameras[0].pixelWidth / 4, - cameras[0].pixelHeight / 4, - 0, - RenderTextureFormat.Depth - ); - depthTexture.Create(); - indivDepthTextures[i] = depthTexture; - fullscreenPassMaterial.SetTexture( - "_depthMap" + i, - depthTexture, - RenderTextureSubElement.Depth - ); - } - } + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } protected override void Execute(CustomPassContext ctx) { @@ -80,15 +60,6 @@ protected override void Execute(CustomPassContext ctx) overrideRenderState: overrideDepthTest ); } - - // combine depthmaps into mosaic depth map - CoreUtils.SetRenderTarget(ctx.cmd, depthMosaicHandle, ClearFlag.None); - CoreUtils.DrawFullScreen( - ctx.cmd, - fullscreenPassMaterial, - ctx.propertyBlock, - shaderPassId: 0 - ); } /// diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 211deb6..fe4af3b 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -13,21 +13,22 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public int internalCameraCount = 16; public RTHandle mosaicImageHandle; - public RTHandle depthMosaicHandle; private ComputeShader holeFillingCompShader; public RenderTexture computeShaderResultTexture; public RTHandle computeShaderResultTextureHandle; - public int holeFillingRadius = 3; + public int holeFillingRadius; - public bool fillHoles = false; + public bool fillHoles; - public bool debugRendering = false; + public bool debugRendering; private Material blitMaterial; + public RenderTexture[] indivDepthMaps; + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); @@ -76,7 +77,7 @@ protected override void Execute(CustomPassContext ctx) addViewProjectionMatrix(ctx, cameras[internalCameraCount / 2], "middleViewProjMatrix"); addViewProjectionMatrix(ctx, cameras[internalCameraCount - 1], "rightViewProjMatrix"); - if (debugRendering) + if (debugRendering && !fillHoles) { CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); } @@ -84,6 +85,7 @@ protected override void Execute(CustomPassContext ctx) { CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); } + CoreUtils.DrawFullScreen( ctx.cmd, fullscreenPassMaterial, @@ -101,12 +103,15 @@ protected override void Execute(CustomPassContext ctx) "Result", computeShaderResultTexture ); - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - kernel, - "_depthMosaic", - depthMosaicHandle - ); + for (int i = 0; i < internalCameraCount; i++) + { + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + kernel, + "_depthMap" + i, + indivDepthMaps[i] + ); + } ctx.cmd.SetComputeTextureParam( holeFillingCompShader, kernel, @@ -134,7 +139,15 @@ protected override void Execute(CustomPassContext ctx) ctx.cmd.DispatchCompute(holeFillingCompShader, kernel, 32, 32, 1); - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + if (debugRendering) + { + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + } + else + { + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + } + CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); } } From 364136e613e1e88b1a3e5d7e0cf4c9a247b808f6 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 7 Aug 2025 15:09:42 +0200 Subject: [PATCH 020/149] first version of vector based view map generation --- G3DCamera.cs | 45 +++- G3DCameraMosaicMultiview.cs | 11 +- Resources/G3DHDRPMultiviewMosaicVector.shader | 231 ++++++++++++++++++ .../G3DHDRPMultiviewMosaicVector.shader.meta | 9 + Resources/G3DShader.shader | 14 +- Resources/G3DShaderMultiview.shader | 12 +- Resources/G3DShaderMultiviewMosaic.shader | 16 +- Runtime/ViewMapGenerator.meta | 8 + Runtime/ViewMapGenerator/bin.meta | 8 + .../bin/viewmap_generator.dll | 3 + .../bin/viewmap_generator.dll.meta | 27 ++ Runtime/ViewMapGenerator/include.meta | 8 + .../ViewMapGenerator/include/G3DMonitorLib.h | 94 +++++++ .../include/G3DMonitorLib.h.meta | 27 ++ .../include/G3DMonitorTypes.h | 54 ++++ .../include/G3DMonitorTypes.h.meta | 27 ++ Runtime/ViewMapGenerator/lib.meta | 8 + .../lib/viewmap_generator.lib | 3 + .../lib/viewmap_generator.lib.meta | 27 ++ Scripts/VectorBasedViewMap.meta | 8 + .../ViewmapGeneratorInterface.cs | 223 +++++++++++++++++ .../ViewmapGeneratorInterface.cs.meta | 11 + 22 files changed, 842 insertions(+), 32 deletions(-) create mode 100644 Resources/G3DHDRPMultiviewMosaicVector.shader create mode 100644 Resources/G3DHDRPMultiviewMosaicVector.shader.meta create mode 100644 Runtime/ViewMapGenerator.meta create mode 100644 Runtime/ViewMapGenerator/bin.meta create mode 100644 Runtime/ViewMapGenerator/bin/viewmap_generator.dll create mode 100644 Runtime/ViewMapGenerator/bin/viewmap_generator.dll.meta create mode 100644 Runtime/ViewMapGenerator/include.meta create mode 100644 Runtime/ViewMapGenerator/include/G3DMonitorLib.h create mode 100644 Runtime/ViewMapGenerator/include/G3DMonitorLib.h.meta create mode 100644 Runtime/ViewMapGenerator/include/G3DMonitorTypes.h create mode 100644 Runtime/ViewMapGenerator/include/G3DMonitorTypes.h.meta create mode 100644 Runtime/ViewMapGenerator/lib.meta create mode 100644 Runtime/ViewMapGenerator/lib/viewmap_generator.lib create mode 100644 Runtime/ViewMapGenerator/lib/viewmap_generator.lib.meta create mode 100644 Scripts/VectorBasedViewMap.meta create mode 100644 Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs create mode 100644 Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index 35f4579..c0a3f4a 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using PlasticGui.WorkspaceWindow; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; @@ -249,6 +250,7 @@ private float scaledHalfCameraWidthAtStart #endregion private bool generateViews = true; + private bool useVectorMapViewGeneration = false; private Material viewGenerationMaterial; // TODO Handle viewport resizing/ moving @@ -284,10 +286,10 @@ void Start() shaderHandles = new ShaderHandles() { - leftViewportPosition = Shader.PropertyToID("v_pos_x"), - bottomViewportPosition = Shader.PropertyToID("v_pos_y"), - screenWidth = Shader.PropertyToID("s_width"), - screenHeight = Shader.PropertyToID("s_height"), + leftViewportPosition = Shader.PropertyToID("viewport_pos_x"), + bottomViewportPosition = Shader.PropertyToID("viewport_pos_y"), + screenWidth = Shader.PropertyToID("screen_width"), + screenHeight = Shader.PropertyToID("screen_height"), nativeViewCount = Shader.PropertyToID("nativeViewCount"), angleRatioNumerator = Shader.PropertyToID("zwinkel"), angleRatioDenominator = Shader.PropertyToID("nwinkel"), @@ -344,6 +346,30 @@ void Start() cachedWindowSize = new Vector2Int(Screen.width, Screen.height); headPositionLog = new Queue(10000); + + if (useVectorMapViewGeneration) + { + Texture2D viewMap = ViewmapGeneratorInterface.getViewMap( + (uint)shaderParameters.screenWidth, // PixelCountX + (uint)shaderParameters.screenHeight, // PixelCountY + (uint)shaderParameters.nativeViewCount, // ViewCount + (uint)shaderParameters.angleRatioDenominator, // LensWidth + (uint)shaderParameters.angleRatioNumerator, // LensAngleCounter + false, // ViewOrderInverted + shaderParameters.leftLensOrientation != 0, // Rotated + false, // FullPixel + shaderParameters.BGRPixelLayout != 0 // BGRMode + ); + viewMap.Apply(); + material?.SetTexture("_viewMap", viewMap); + + float[] indexMap = new float[shaderParameters.nativeViewCount]; + for (int i = 0; i < shaderParameters.nativeViewCount; i++) + { + indexMap[i] = i; + } + material?.SetFloatArray("indexMap", indexMap); + } } #if G3D_HDRP @@ -437,7 +463,7 @@ private void initCustomPass() } RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); - material.SetTexture("mosaictexture", rtHandleMosaic); + material.SetTexture("_colorMosaic", rtHandleMosaic); viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } else @@ -834,7 +860,11 @@ private void reinitializeShader() { if (mode == G3DCameraMode.MULTIVIEW) { - if (generateViews) + if (useVectorMapViewGeneration && generateViews) + { + material = new Material(Shader.Find("G3D/MultiviewMosaicVector")); + } + else if (!useVectorMapViewGeneration && generateViews) { material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); } @@ -948,6 +978,7 @@ private void updateScreenViewportProperties() // this parameter is used in the shader to invert the y axis material?.SetInt(Shader.PropertyToID("viewportHeight"), Screen.height); + material?.SetInt(Shader.PropertyToID("viewportWidth"), Screen.width); } private void updateShaderParameters() @@ -1479,7 +1510,7 @@ private string formatErrorMessage(string caption, string cause, string remedy) if (string.IsNullOrEmpty(remedy) == false) { - messageText = messageText + "\n" + remedy; + messageText = messageText + "\\n" + remedy; } return messageText; diff --git a/G3DCameraMosaicMultiview.cs b/G3DCameraMosaicMultiview.cs index 3170bec..696d494 100644 --- a/G3DCameraMosaicMultiview.cs +++ b/G3DCameraMosaicMultiview.cs @@ -75,9 +75,10 @@ void Start() shaderHandles = new ShaderHandles() { - leftViewportPosition = Shader.PropertyToID("v_pos_x"), - bottomViewportPosition = Shader.PropertyToID("v_pos_y"), - screenHeight = Shader.PropertyToID("s_height"), + leftViewportPosition = Shader.PropertyToID("viewport_pos_x"), + bottomViewportPosition = Shader.PropertyToID("viewport_pos_y"), + screenHeight = Shader.PropertyToID("screen_height"), + screenWidth = Shader.PropertyToID("screen_width"), nativeViewCount = Shader.PropertyToID("nativeViewCount"), angleRatioNumerator = Shader.PropertyToID("zwinkel"), angleRatioDenominator = Shader.PropertyToID("nwinkel"), @@ -121,7 +122,7 @@ void Start() public void reinitializeShader() { material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); - material.SetTexture("mosaictexture", mosaicTexture, RenderTextureSubElement.Color); + material.SetTexture("_colorMosaic", mosaicTexture, RenderTextureSubElement.Color); updateScreenViewportProperties(); updateShaderParameters(); @@ -226,6 +227,7 @@ private void updateScreenViewportProperties() // this parameter is used in the shader to invert the y axis material?.SetInt(Shader.PropertyToID("viewportHeight"), Screen.height); + material?.SetInt(Shader.PropertyToID("viewportWidth"), Screen.width); } private void updateShaderParameters() @@ -236,6 +238,7 @@ private void updateShaderParameters() shaderParameters.bottomViewportPosition ); material?.SetInt(shaderHandles.screenHeight, shaderParameters.screenHeight); + material?.SetInt(shaderHandles.screenWidth, shaderParameters.screenWidth); material?.SetInt(shaderHandles.nativeViewCount, shaderParameters.nativeViewCount); material?.SetInt(shaderHandles.angleRatioNumerator, shaderParameters.angleRatioNumerator); material?.SetInt( diff --git a/Resources/G3DHDRPMultiviewMosaicVector.shader b/Resources/G3DHDRPMultiviewMosaicVector.shader new file mode 100644 index 0000000..22fe154 --- /dev/null +++ b/Resources/G3DHDRPMultiviewMosaicVector.shader @@ -0,0 +1,231 @@ +Shader "G3D/MultiviewMosaicVector" +{ + HLSLINCLUDE + #include "G3DHLSLCommonFunctions.hlsl" + + struct v2f + { + float2 uv : TEXCOORD0; + float4 screenPos : SV_POSITION; + }; + + // 0 = left camera, 1 = right camera + Texture2D _colorMosaic; + SamplerState sampler_colorMosaic; + + Texture2D _viewMap; + SamplerState sampler_viewMap; + + uint screen_height; // screen height + uint screen_width; // screen width + uint viewport_pos_x; // horizontal viewport position + uint viewport_pos_y; // vertical viewport position + int viewportHeight; + int viewportWidth; + + // contains a list mapping view index to actual view (view 0 actually uses texture 0, view 1 uses texture 1, etc. (or shifted)) + // i.e. [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] + uint indexMap[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + uint nativeViewCount = 16; // length of indexMap + int mirror; // 1: mirror from left to right, 0: no mirror + + + // viewIndices are the view indices for the three color channels + // i.e. viewIndex.x maps to view 3, viewIndex.y maps to view 5, and viewIndex.z maps to view 8 + float3 GetColors(int3 viewIndices, v2f i) + { + float3 OutColors = float3(0.0, 0.0, 0.0); + + float2 uvCoords = i.uv; + // mirror the image if necessary + if (mirror != 0) { + uvCoords.x = 1.0 - uvCoords.x; + } + + + for (int ColorIndex = 0; ColorIndex < 3; ColorIndex ++) + { + int viewIndex = viewIndices[ColorIndex]; + uint TextureIndex = 255; + if (viewIndex < nativeViewCount) + { + TextureIndex = indexMap[viewIndex]; + } + + float2 mappedUVCoords = calculateUVForMosaic(TextureIndex, uvCoords); + float4 tmpColor = _colorMosaic.Sample(sampler_colorMosaic, mappedUVCoords); + + float Texture1 = 0.0; + switch (TextureIndex) + { + case 254: + if (ColorIndex == 0) + Texture1 = 1.0; + break; + case 255: + break; + default: + Texture1 = tmpColor[ColorIndex]; + break; + } + OutColors[ColorIndex] = Texture1; + } + + return OutColors; + } + + + float4 frag (v2f i) : SV_Target + { + + float xScreenCoords = i.uv.x * viewportWidth + viewport_pos_x; + viewport_pos_y = viewport_pos_y - viewportHeight; + float yScreenCoords = (viewportHeight - i.uv.y * viewportHeight) + viewport_pos_y; + // TODO check if this is necessary + + // position of window in screen coordinates + uint2 computedScreenPos = uint2(xScreenCoords, yScreenCoords); + float2 screenPosUV = float2(computedScreenPos) / float2(screen_width, screen_height); + + // get view indices from view map + // every color value contain index of view in view array to use for this color + // this works only, if texture is flipped in y since texelFetch use 0,0 for bottom/left + float4 ViewIndexFloat = _viewMap.Sample(sampler_viewMap, screenPosUV); + + // since value is normalized (0..1), we bring it back to byte value + int3 ViewIndex = int3(round(ViewIndexFloat.x * 255), + round(ViewIndexFloat.y * 255), + round(ViewIndexFloat.z * 255)); + + return float4(GetColors(ViewIndex, i), 1.0f); + } + ENDHLSL + + // URP Shader + SubShader + { + PackageRequirements + { + "com.unity.render-pipelines.universal": "unity=2021.3" + } + + Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"} + LOD 100 + ZWrite Off Cull Off + + Pass + { + ZWrite Off + ZTest Always + Blend Off + Cull Off + + HLSLPROGRAM + #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" + + #pragma vertex vert + #pragma fragment frag + + struct VertAttributes + { + uint vertexID : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + v2f vert(VertAttributes input) + { + v2f output; + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + output.uv = GetFullScreenTriangleTexCoord(input.vertexID); + output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + + return output; + + } + ENDHLSL + } + } + + // HDRP Shader + SubShader + { + PackageRequirements + { + "com.unity.render-pipelines.high-definition": "unity=2021.3" + } + + Tags { "RenderType"="Opaque" "RenderPipeline" = "HDRenderPipeline"} + + Pass + { + Name "G3DFullScreen3D" + + ZWrite Off + ZTest Always + Blend Off + Cull Off + + HLSLPROGRAM + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + + #pragma vertex vert + #pragma fragment frag + + struct VertAttributes + { + uint vertexID : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + v2f vert(VertAttributes input) + { + v2f output; + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + output.uv = GetFullScreenTriangleTexCoord(input.vertexID); + output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + + return output; + + } + ENDHLSL + } + } + + // Built-in Render Pipeline + SubShader + { + // No culling or depth + Cull Off ZWrite Off ZTest Always + + Pass + { + HLSLPROGRAM + #pragma vertex vert + #pragma fragment fragSRP + + #include "UnityCG.cginc" + + v2f vert(float4 vertex : POSITION, float2 uv : TEXCOORD0) + { + v2f o; + o.uv = uv; + o.screenPos = UnityObjectToClipPos(vertex); + return o; + } + + + float4 fragSRP(v2f i) : SV_Target + { + // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) + // The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity. + i.screenPos.y = viewportHeight - i.screenPos.y; + return frag(i); + } + + ENDHLSL + } + } +} \ No newline at end of file diff --git a/Resources/G3DHDRPMultiviewMosaicVector.shader.meta b/Resources/G3DHDRPMultiviewMosaicVector.shader.meta new file mode 100644 index 0000000..0dbb81c --- /dev/null +++ b/Resources/G3DHDRPMultiviewMosaicVector.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a7bb4408da385ba4e81a4a5d2ad0de70 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/G3DShader.shader b/Resources/G3DShader.shader index 34f4d75..068f35d 100644 --- a/Resources/G3DShader.shader +++ b/Resources/G3DShader.shader @@ -22,9 +22,9 @@ Shader "G3D/Autostereo" int brs; // black right start int bre; // black right end - int s_height; // screen height - int v_pos_x; // horizontal viewport position - int v_pos_y; // vertical viewport position + int screen_height; // screen height + int viewport_pos_x; // horizontal viewport position + int viewport_pos_y; // vertical viewport position int tvx; // zCorrectionValue int zkom; // zCompensationValue, kompensiert den Shift der durch die Z-Korrektur entsteht @@ -43,7 +43,7 @@ Shader "G3D/Autostereo" // unused parameters int bborder; // blackBorder schwarz verblendung zwischen den views? int bspace; // blackSpace - int s_width; // screen width + int screen_width; // screen width int blur; // je größer der Wert umso mehr wird verwischt 0-1000 sinnvoll struct v2f @@ -72,12 +72,12 @@ Shader "G3D/Autostereo" float4 frag (v2f i) : SV_Target { // Start der Berechnung von dynamische Daten - int xScreenCoords = int(i.screenPos.x) + v_pos_x; // transform x position from viewport to screen coordinates + int xScreenCoords = int(i.screenPos.x) + viewport_pos_x; // transform x position from viewport to screen coordinates // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) // The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity. - int yScreenCoords = int(i.screenPos.y) + v_pos_y; // transform y position from viewport to screen coordinates + int yScreenCoords = int(i.screenPos.y) + viewport_pos_y; // transform y position from viewport to screen coordinates if (isleft == 0) { - yScreenCoords = s_height - yScreenCoords ; // invertieren für rechts geneigte Linse + yScreenCoords = screen_height - yScreenCoords ; // invertieren für rechts geneigte Linse } int yw = int(yScreenCoords * zwinkel) / nwinkel; // Winkelberechnung für die Renderberechnung diff --git a/Resources/G3DShaderMultiview.shader b/Resources/G3DShaderMultiview.shader index 33e2a40..40e5136 100644 --- a/Resources/G3DShaderMultiview.shader +++ b/Resources/G3DShaderMultiview.shader @@ -13,9 +13,9 @@ Shader "G3D/AutostereoMultiview" int mstart; // Viewshift permanent Offset int hqview; // hqhqViewCount - int s_height; // screen height - int v_pos_x; // horizontal viewport position - int v_pos_y; // vertical viewport position + int screen_height; // screen height + int viewport_pos_x; // horizontal viewport position + int viewport_pos_y; // vertical viewport position // This shader was originally implemented for OpenGL, so we need to invert the y axis to make it work in Unity. // to do this we need the actual viewport height @@ -30,7 +30,7 @@ Shader "G3D/AutostereoMultiview" int isBGR; // 0 = RGB, 1 = BGR // unused parameters -> only here for so that this shader overlaps with the multiview shader - int s_width; // screen width + int screen_width; // screen width int bborder; // blackBorder schwarz verblendung zwischen den views? int bspace; // blackSpace int tvx; // zCorrectionValue @@ -153,9 +153,9 @@ Shader "G3D/AutostereoMultiview" { return sampleFromView(nativeViewCount - 0 - 1, i.uv); - float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) + float yPos = screen_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) - float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); + float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(viewport_pos_x, viewport_pos_y); int3 viewIndices = getSubPixelViewIndices(computedScreenPos); float2 uvCoords = i.uv; diff --git a/Resources/G3DShaderMultiviewMosaic.shader b/Resources/G3DShaderMultiviewMosaic.shader index d47217d..7e8ea6c 100644 --- a/Resources/G3DShaderMultiviewMosaic.shader +++ b/Resources/G3DShaderMultiviewMosaic.shader @@ -13,9 +13,9 @@ Shader "G3D/AutostereoMultiviewMosaic" int mstart; // Viewshift permanent Offset - int s_height; // screen height - int v_pos_x; // horizontal viewport position - int v_pos_y; // vertical viewport position + int screen_height; // screen height + int viewport_pos_x; // horizontal viewport position + int viewport_pos_y; // vertical viewport position // This shader was originally implemented for OpenGL, so we need to invert the y axis to make it work in Unity. // to do this we need the actual viewport height @@ -40,8 +40,8 @@ Shader "G3D/AutostereoMultiviewMosaic" }; - Texture2D mosaictexture; - SamplerState samplermosaictexture; + Texture2D _colorMosaic; + SamplerState sampler_colorMosaic; int map(int x, int in_min, int in_max, int out_min, int out_max) { @@ -86,9 +86,9 @@ Shader "G3D/AutostereoMultiviewMosaic" float4 frag (v2f i) : SV_Target { - float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) + float yPos = screen_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) - float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); + float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(viewport_pos_x, viewport_pos_y); int3 viewIndices = getSubPixelViewIndices(computedScreenPos); float2 uvCoords = i.uv; @@ -115,7 +115,7 @@ Shader "G3D/AutostereoMultiviewMosaic" } float2 mappedUVCoords = calculateUVForMosaic(viewIndex, uvCoords); - float4 tmpColor = mosaictexture.Sample(samplermosaictexture, mappedUVCoords); + float4 tmpColor = _colorMosaic.Sample(sampler_colorMosaic, mappedUVCoords); if(channel == 0) { color.x = tmpColor.x; diff --git a/Runtime/ViewMapGenerator.meta b/Runtime/ViewMapGenerator.meta new file mode 100644 index 0000000..4864c46 --- /dev/null +++ b/Runtime/ViewMapGenerator.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4f7808ea8eee504499536b10dd674af4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ViewMapGenerator/bin.meta b/Runtime/ViewMapGenerator/bin.meta new file mode 100644 index 0000000..922e4b3 --- /dev/null +++ b/Runtime/ViewMapGenerator/bin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0089e43dfb50a5f418ff7a2802499f90 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ViewMapGenerator/bin/viewmap_generator.dll b/Runtime/ViewMapGenerator/bin/viewmap_generator.dll new file mode 100644 index 0000000..89f78f9 --- /dev/null +++ b/Runtime/ViewMapGenerator/bin/viewmap_generator.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6dbdf8a31407f844402c9c0e1a071a9635c06087ba2c69f245371e516b107aa +size 32768 diff --git a/Runtime/ViewMapGenerator/bin/viewmap_generator.dll.meta b/Runtime/ViewMapGenerator/bin/viewmap_generator.dll.meta new file mode 100644 index 0000000..adfed63 --- /dev/null +++ b/Runtime/ViewMapGenerator/bin/viewmap_generator.dll.meta @@ -0,0 +1,27 @@ +fileFormatVersion: 2 +guid: 07bf56d292d711943a7c9ad03b155e9d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ViewMapGenerator/include.meta b/Runtime/ViewMapGenerator/include.meta new file mode 100644 index 0000000..3967c96 --- /dev/null +++ b/Runtime/ViewMapGenerator/include.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 778c9bbae97e79c43935385a2a409311 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ViewMapGenerator/include/G3DMonitorLib.h b/Runtime/ViewMapGenerator/include/G3DMonitorLib.h new file mode 100644 index 0000000..d39fa39 --- /dev/null +++ b/Runtime/ViewMapGenerator/include/G3DMonitorLib.h @@ -0,0 +1,94 @@ +#ifndef VIEWMAP_GENERATOR_H +#define VIEWMAP_GENERATOR_H + +#if defined(_WIN32) && defined(VIEWMAP_GENERATOR_DLL) +#ifdef viewmap_generator_EXPORTS +#define VIEWMAP_GENERATOR_API __declspec(dllexport) +#else +#define VIEWMAP_GENERATOR_API __declspec(dllimport) +#endif +#else +#define VIEWMAP_GENERATOR_API +#endif + +#include "G3DMonitorTypes.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + typedef enum G3DMonitor_Error { + G3DMONITOR_SUCCESS, // no error + G3DMONITOR_ERROR, // general error, call Error_GetLastError for details + G3DMONITOR_INVALID_PARAMETER, // invalid parameter (e.g. invalid instance) + G3DMONITOR_BUFFER_TOO_SMALL, // buffer is too small + G3DMONITOR_NOT_IMPLEMENTED, // function is not implemented + } G3DMonitor_Error; + +#define ERRORINTRO \ + try \ + { + +#define ERROROUTRO \ + return G3DMONITOR_SUCCESS; \ + } \ + catch (const std::exception& e) \ + { \ + Error_SetLastError(e.what()); \ + } \ + catch (...) \ + { \ + Error_SetLastError("Unexpected exception"); \ + } \ + return G3DMONITOR_ERROR; + +#define G3DMONITOR_CALLTYPE /* default */ + + // error handling + VIEWMAP_GENERATOR_API void G3DMONITOR_CALLTYPE Error_SetLastError(const char* ErrorText); + VIEWMAP_GENERATOR_API bool G3DMONITOR_CALLTYPE Error_GetLastError(char* ErrorText, size_t* Size); + + // monitor instance handling + VIEWMAP_GENERATOR_API G3DMonitor_Error G3DMONITOR_CALLTYPE G3DMonitor_Create( + uint32_t PixelCountX, // number of pixels in x (used for full view map dimension) + uint32_t PixelCountY, // number of pixels in y (used for full view map dimension) + uint16_t ViewCount, // view count (if 0, 3d is not supported) + uint32_t LensWidth, // lens width (in number of pixel) + int32_t LensAngleCounter, // angle counter (including sign) + bool ViewOrderInverted, // if view order is inverted (left and right mirrored) + bool Rotated, // if monitor is rotated by 90° (not supported by deprecated algorithm) + bool FullPixel, // if only full pixel available (lens is always over full pixel; no sub pixel addressable) + bool BGRMode, // if red and blue channel are exchanged (display rotated by 180°) + void** Monitor); + VIEWMAP_GENERATOR_API G3DMonitor_Error G3DMONITOR_CALLTYPE G3DMonitor_Destroy(void* Monitor); + + // calculate view map from given parameters + // is result = G3DMONITOR_SUCCESS, view map is valid + // do not forget to free memory of view map, if result is G3DMONITOR_SUCCESS (calling G3DMonitor_FreeViewMap) + VIEWMAP_GENERATOR_API G3DMonitor_Error G3DMONITOR_CALLTYPE G3DMonitor_BuildViewMap( + void* Monitor, // monitor instance (created with G3DMonitor_Create) + bool HQMode, // hq mode (use all views up to 250; in non-hq mode, number of views is limited to number of base views) + G3DViewMapAlignment ViewAlignment, // view alignment + G3DViewMapZeroPixel ViewZeroPixel, // zero sub pixel, if view alignment is vma_DefinedZero; z is color to use (0=red, 1=green, 2=blue) + bool EnlargeViewMapX, // if to repeat shrinked viewmap to full monitor resolution + bool EnlargeViewMapY, // if to repeat shrinked viewmap to full monitor resolution + bool AViewMapIsBGR, // if colors aligned blue, green, red (instead red, green, blue; e.g. used for windows bitmap) + bool AViewMapInvertY, // if y order is inverted (bottom line is last in memory (like windows bitmap)) + int8_t AViewMapLinePadding, // line padding (<0=automatic for windows bitmap, 0=off, 1..n = define number of fill bytes) + uint8_t* ResultViewCount, // number of views used in view map (will be <= 250 in any case since 251..255 are reserved) + uint32_t* ResultViewMapWidth, // dimension of viewmap in pixel in x + uint32_t* ResultViewMapHeight, // dimension of viewmap in pixel in y + uint32_t* ResultViewMapSize, // size of view map (in bytes) + uint32_t* ResultViewMapScanLineSize, // size of single line of view map (in bytes) + uint8_t** ResultViewMap // result view map (memory is allocated and filled automatically; memory size of view map is ResultViewMapWidth*ResultViewMapHeight*3 (format is RGB)) + ); + + // destroy view map memory and set pointer null + VIEWMAP_GENERATOR_API G3DMonitor_Error G3DMONITOR_CALLTYPE G3DMonitor_FreeViewMap(uint8_t** ViewMap); + +#ifdef __cplusplus +} +#endif + +#endif // VIEWMAP_GENERATOR_H diff --git a/Runtime/ViewMapGenerator/include/G3DMonitorLib.h.meta b/Runtime/ViewMapGenerator/include/G3DMonitorLib.h.meta new file mode 100644 index 0000000..fa68d37 --- /dev/null +++ b/Runtime/ViewMapGenerator/include/G3DMonitorLib.h.meta @@ -0,0 +1,27 @@ +fileFormatVersion: 2 +guid: 81088ae9a0a484b49bbbb16b3b3c4925 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h b/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h new file mode 100644 index 0000000..c2fcabb --- /dev/null +++ b/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h @@ -0,0 +1,54 @@ +#ifndef G3DMONITORTYPES_H +#define G3DMONITORTYPES_H + +#include +#include + +// alignment of view map +typedef enum G3DViewMapAlignment { + vma_Default, // use calculated view indizes (first left sub pixel use last view index (or 0, if view order is inverted) + vma_DefinedZero, // defined sub pixel will get view index 0 + vma_CompatibleDeprecated, // compatible to deprecated algorithm (if screen is left, left pixel of last row has highest view number; if screen is right, left pixel of first row has highest view number) + vma_CompatibleMPV // compatible to mpv (left red subpixel in second row from top will use view 0; since there are multiple implementations, this may not work as used version of mpv; mpv use different calculation, if map is calculated shader!) +} G3DViewMapAlignment; + +// zero sub pixel +typedef struct G3DViewMapZeroPixel { + uint32_t x; + uint32_t y; + uint32_t z; +} G3DViewMapZeroPixel; + +// pixel colors +typedef enum G3DPixelColorChannel { + pc_Red, + pc_Green, + pc_Blue, + pc_NumColors +} G3DPixelColorChannel; + +// 2d point +typedef struct G3DPoint2DF { + float x; + float y; + + G3DPoint2DF() + { + x = 0; + y = 0; + } + + G3DPoint2DF(float ax, float ay) + { + x = ax; + y = ay; + } +} G3DPoint2DF; + +// 2d point for pixel +typedef struct G3DPixelPointF { + G3DPoint2DF& operator[](G3DPixelColorChannel i) { return data[i]; } + G3DPoint2DF data[pc_NumColors]; +} G3DPixelPointF; + +#endif // G3DMONITORTYPES_H diff --git a/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h.meta b/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h.meta new file mode 100644 index 0000000..69cb002 --- /dev/null +++ b/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h.meta @@ -0,0 +1,27 @@ +fileFormatVersion: 2 +guid: f8d0e289de452c045a13f833d3669c4b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ViewMapGenerator/lib.meta b/Runtime/ViewMapGenerator/lib.meta new file mode 100644 index 0000000..f704004 --- /dev/null +++ b/Runtime/ViewMapGenerator/lib.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ab4811b56f0b3bc42a34f0516deefc16 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ViewMapGenerator/lib/viewmap_generator.lib b/Runtime/ViewMapGenerator/lib/viewmap_generator.lib new file mode 100644 index 0000000..703f7bd --- /dev/null +++ b/Runtime/ViewMapGenerator/lib/viewmap_generator.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f58b866825a1c6402a696d8bd717edeaf7a640674749ddb25e6e1abe9cf67a0f +size 3122 diff --git a/Runtime/ViewMapGenerator/lib/viewmap_generator.lib.meta b/Runtime/ViewMapGenerator/lib/viewmap_generator.lib.meta new file mode 100644 index 0000000..d5bd308 --- /dev/null +++ b/Runtime/ViewMapGenerator/lib/viewmap_generator.lib.meta @@ -0,0 +1,27 @@ +fileFormatVersion: 2 +guid: 41c889b4543a73649aec930c891adf19 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/VectorBasedViewMap.meta b/Scripts/VectorBasedViewMap.meta new file mode 100644 index 0000000..102183b --- /dev/null +++ b/Scripts/VectorBasedViewMap.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 93f00ca423bf80442bfc4725ee756598 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs b/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs new file mode 100644 index 0000000..61006d9 --- /dev/null +++ b/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs @@ -0,0 +1,223 @@ +using System; +using System.Runtime.InteropServices; +using UnityEngine; + +internal enum G3DViewMapAlignment +{ + vma_Default = 0, // use calculated view indizes (first left sub pixel use last view index (or 0, if view order is inverted) + vma_DefinedZero = 1, // defined sub pixel will get view index 0 + vma_CompatibleDeprecated = 2, // compatible to deprecated algorithm (if screen is left, left pixel of last row has highest view number; if screen is right, left pixel of first row has highest view number) + vma_CompatibleMPV = 3 // compatible to mpv (left red subpixel in second row from top will use view 0; since there are multiple implementations, this may not work as used version of mpv; mpv use different calculation, if map is calculated shader!) +} + +internal struct G3DViewMapZeroPixel +{ + public uint x; + public uint y; + public uint z; +} + +internal enum G3DPixelColorChannel +{ + pc_Red = 0, + pc_Green = 1, + pc_Blue = 2, + pc_NumColors = 3 +} + +internal struct G3DPoint2DF +{ + public float x; + public float y; + + public G3DPoint2DF(float ax, float ay) + { + x = ax; + y = ay; + } +} + +internal struct G3DPixelPointF +{ + public G3DPoint2DF[] data; +} + +internal enum G3DMonitor_Error +{ + G3DMONITOR_SUCCESS, // no error + G3DMONITOR_ERROR, // general error, call Error_GetLastError for details + G3DMONITOR_INVALID_PARAMETER, // invalid parameter (e.g. invalid instance) + G3DMONITOR_BUFFER_TOO_SMALL, // buffer is too small + G3DMONITOR_NOT_IMPLEMENTED, // function is not implemented +} + +/// +/// does not upload the texture to the GPU. +/// +public sealed class ViewmapGeneratorInterface +{ + public static Texture2D getViewMap( + uint PixelCountX, // number of pixels in x (used for full view map dimension) + uint PixelCountY, // number of pixels in y (used for full view map dimension) + uint ViewCount, // view count (if 0, 3d is not supported) + uint LensWidth, // lens width (in number of pixel) + uint LensAngleCounter, // angle counter (including sign) + bool ViewOrderInverted, // if view order is inverted (left and right mirrored) + bool Rotated, // if monitor is rotated by 90° (not supported by deprecated algorithm) + bool FullPixel, // if only full pixel available (lens is always over full pixel; no sub pixel addressable) + bool BGRMode + ) + { + // first create the monitor instance + IntPtr monitor; + G3DMonitor_Error result = ViewmapGeneratorCpp.G3DMonitor_Create( + PixelCountX, + PixelCountY, + ViewCount, + LensWidth, + LensAngleCounter, + ViewOrderInverted, + Rotated, + FullPixel, + BGRMode, + out monitor + ); + if (result != G3DMonitor_Error.G3DMONITOR_SUCCESS) + { + Debug.LogError("Failed to create monitor instance: " + result); + return null; + } + + // now build the view map + IntPtr resultViewMapPtr; + byte ResultViewCount; // number of views used in view map (will be <= 250 in any case since 251..255 are reserved) + uint ResultViewMapWidth; // dimension of viewmap in pixel in x + uint ResultViewMapHeight; // dimension of viewmap in pixel in y + uint ResultViewMapSize; // size of view map (in bytes) + uint ResultViewMapScanLineSize; + G3DMonitor_Error buildViewmapResult = ViewmapGeneratorCpp.G3DMonitor_BuildViewMap( + monitor, + false, + G3DViewMapAlignment.vma_Default, + new G3DViewMapZeroPixel + { + x = 0, + y = 0, + z = 0 + }, + true, + true, + false, + false, + 0, + out ResultViewCount, + out ResultViewMapWidth, + out ResultViewMapHeight, + out ResultViewMapSize, + out ResultViewMapScanLineSize, + out resultViewMapPtr + ); + byte[] managedArray = new byte[ResultViewMapSize]; + Marshal.Copy(resultViewMapPtr, managedArray, 0, (int)ResultViewMapSize); + + G3DMonitor_Error freeMapResult = ViewmapGeneratorCpp.G3DMonitor_FreeViewMap( + resultViewMapPtr + ); + + // copy the result view map to a byte array + + // free view map memory + + // free monitor instance + G3DMonitor_Error destroyResult = ViewmapGeneratorCpp.G3DMonitor_Destroy(monitor); + if (destroyResult != G3DMonitor_Error.G3DMONITOR_SUCCESS) + { + Debug.LogError("Failed to destroy monitor instance: " + destroyResult); + return null; + } + + Texture2D texture = new Texture2D( + (int)ResultViewMapWidth, + (int)ResultViewMapHeight, + TextureFormat.RGB24, + false + ); + // for (int x = 0; x < ResultViewMapWidth; x++) + // { + // for (int y = 0; y < ResultViewMapHeight; y++) + // { + // int index = (y * (int)ResultViewMapWidth + x) * 3; + // Color color = new Color( + // managedArray[index] / 255f, + // managedArray[index + 1] / 255f, + // managedArray[index + 2] / 255f + // ); + // texture.SetPixel(x, y, color); + // } + // } + texture.LoadRawTextureData(managedArray); + return texture; + } +} + +/// +/// This class provides the raw C interface to the G3D Universal Head Tracking Library. +/// +internal static class ViewmapGeneratorCpp +{ + //function definitions + [DllImport( + "viewmap_generator.dll", + EntryPoint = "G3DMonitor_BuildViewMap", + CallingConvention = CallingConvention.Cdecl + )] + public static extern G3DMonitor_Error G3DMonitor_BuildViewMap( + IntPtr Monitor, // monitor instance (created with G3DMonitor_Create) + bool HQMode, // hq mode (use all views up to 250; in non-hq mode, number of views is limited to number of base views) + G3DViewMapAlignment ViewAlignment, // view alignment + G3DViewMapZeroPixel ViewZeroPixel, // zero sub pixel, if view alignment is vma_DefinedZero; z is color to use (0=red, 1=green, 2=blue) + bool EnlargeViewMapX, // if to repeat shrinked viewmap to full monitor resolution + bool EnlargeViewMapY, // if to repeat shrinked viewmap to full monitor resolution + bool AViewMapIsBGR, // if colors aligned blue, green, red (instead red, green, blue; e.g. used for windows bitmap) + bool AViewMapInvertY, // if y order is inverted (bottom line is last in memory (like windows bitmap)) + byte AViewMapLinePadding, // line padding (<0=automatic for windows bitmap, 0=off, 1..n = define number of fill bytes) + out byte ResultViewCount, // number of views used in view map (will be <= 250 in any case since 251..255 are reserved) + out uint ResultViewMapWidth, // dimension of viewmap in pixel in x + out uint ResultViewMapHeight, // dimension of viewmap in pixel in y + out uint ResultViewMapSize, // size of view map (in bytes) + out uint ResultViewMapScanLineSize, // size of single line of view map (in bytes) + out IntPtr ResultViewMap // result view map (memory is allocated and filled automatically; memory size of view map is ResultViewMapWidth*ResultViewMapHeight*3 (format is RGB)) + ); + + [DllImport( + "viewmap_generator.dll", + EntryPoint = "G3DMonitor_FreeViewMap", + CallingConvention = CallingConvention.Cdecl + )] + public static extern G3DMonitor_Error G3DMonitor_FreeViewMap(in IntPtr ViewMap); + + [DllImport( + "viewmap_generator.dll", + EntryPoint = "G3DMonitor_Create", + CallingConvention = CallingConvention.Cdecl + )] + public static extern G3DMonitor_Error G3DMonitor_Create( + uint PixelCountX, // number of pixels in x (used for full view map dimension) + uint PixelCountY, // number of pixels in y (used for full view map dimension) + uint ViewCount, // view count (if 0, 3d is not supported) + uint LensWidth, // lens width (in number of pixel) + uint LensAngleCounter, // angle counter (including sign) + [MarshalAs(UnmanagedType.U1)] bool ViewOrderInverted, // if view order is inverted (left and right mirrored) + [MarshalAs(UnmanagedType.U1)] bool Rotated, // if monitor is rotated by 90° (not supported by deprecated algorithm) + [MarshalAs(UnmanagedType.U1)] bool FullPixel, // if only full pixel available (lens is always over full pixel; no sub pixel addressable) + [MarshalAs(UnmanagedType.U1)] bool BGRMode, // if red and blue channel are exchanged (display rotated by 180°) + out IntPtr Monitor + ); + + [DllImport( + "viewmap_generator.dll", + EntryPoint = "G3DMonitor_Destroy", + CallingConvention = CallingConvention.Cdecl + )] + public static extern G3DMonitor_Error G3DMonitor_Destroy(IntPtr Monitor); +} diff --git a/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs.meta b/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs.meta new file mode 100644 index 0000000..1415218 --- /dev/null +++ b/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f705fdbed805c5b41b9595a8182d49cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From f8c4945dcfbd8015a64a29dd8a4c6334fa8aa0de Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 7 Aug 2025 16:41:13 +0200 Subject: [PATCH 021/149] fix for angle --- G3DCamera.cs | 15 +++++++++------ Resources/G3DHDRPMultiviewMosaicVector.shader | 1 - .../ViewmapGeneratorInterface.cs | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index c0a3f4a..4672601 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using PlasticGui.WorkspaceWindow; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; @@ -250,7 +249,7 @@ private float scaledHalfCameraWidthAtStart #endregion private bool generateViews = true; - private bool useVectorMapViewGeneration = false; + private bool useVectorMapViewGeneration = true; private Material viewGenerationMaterial; // TODO Handle viewport resizing/ moving @@ -349,14 +348,15 @@ void Start() if (useVectorMapViewGeneration) { + int invert = shaderParameters.leftLensOrientation == 1 ? 1 : -1; Texture2D viewMap = ViewmapGeneratorInterface.getViewMap( (uint)shaderParameters.screenWidth, // PixelCountX (uint)shaderParameters.screenHeight, // PixelCountY (uint)shaderParameters.nativeViewCount, // ViewCount (uint)shaderParameters.angleRatioDenominator, // LensWidth - (uint)shaderParameters.angleRatioNumerator, // LensAngleCounter + invert * shaderParameters.angleRatioNumerator, // LensAngleCounter false, // ViewOrderInverted - shaderParameters.leftLensOrientation != 0, // Rotated + false, // Rotated false, // FullPixel shaderParameters.BGRPixelLayout != 0 // BGRMode ); @@ -376,7 +376,7 @@ void Start() private void initCustomPass() { bool debugRendering = false; - bool isHoleFilling = false; + bool isFillingHoles = false; int holeFillingRadius = 3; // init fullscreen postprocessing for hd render pipeline @@ -440,7 +440,7 @@ private void initCustomPass() } viewGenerationPass.indivDepthMaps = depthMosaicPass.indivDepthTextures; viewGenerationPass.debugRendering = debugRendering; - viewGenerationPass.fillHoles = isHoleFilling; + viewGenerationPass.fillHoles = isFillingHoles; viewGenerationPass.holeFillingRadius = holeFillingRadius; // add autostereo mosaic generation pass @@ -1033,6 +1033,9 @@ private void updateShaderParameters() material?.SetInt(Shader.PropertyToID("cameraCount"), internalCameraCount); material?.SetInt(Shader.PropertyToID("mirror"), mirrorViews ? 1 : 0); + + material?.SetInt(Shader.PropertyToID("mosaic_rows"), 4); + material?.SetInt(Shader.PropertyToID("mosaic_columns"), 4); } } diff --git a/Resources/G3DHDRPMultiviewMosaicVector.shader b/Resources/G3DHDRPMultiviewMosaicVector.shader index 22fe154..e16c6f1 100644 --- a/Resources/G3DHDRPMultiviewMosaicVector.shader +++ b/Resources/G3DHDRPMultiviewMosaicVector.shader @@ -81,7 +81,6 @@ Shader "G3D/MultiviewMosaicVector" float xScreenCoords = i.uv.x * viewportWidth + viewport_pos_x; viewport_pos_y = viewport_pos_y - viewportHeight; float yScreenCoords = (viewportHeight - i.uv.y * viewportHeight) + viewport_pos_y; - // TODO check if this is necessary // position of window in screen coordinates uint2 computedScreenPos = uint2(xScreenCoords, yScreenCoords); diff --git a/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs b/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs index 61006d9..67a8a05 100644 --- a/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs +++ b/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs @@ -61,7 +61,7 @@ public static Texture2D getViewMap( uint PixelCountY, // number of pixels in y (used for full view map dimension) uint ViewCount, // view count (if 0, 3d is not supported) uint LensWidth, // lens width (in number of pixel) - uint LensAngleCounter, // angle counter (including sign) + int LensAngleCounter, // angle counter (including sign) bool ViewOrderInverted, // if view order is inverted (left and right mirrored) bool Rotated, // if monitor is rotated by 90° (not supported by deprecated algorithm) bool FullPixel, // if only full pixel available (lens is always over full pixel; no sub pixel addressable) @@ -206,7 +206,7 @@ public static extern G3DMonitor_Error G3DMonitor_Create( uint PixelCountY, // number of pixels in y (used for full view map dimension) uint ViewCount, // view count (if 0, 3d is not supported) uint LensWidth, // lens width (in number of pixel) - uint LensAngleCounter, // angle counter (including sign) + int LensAngleCounter, // angle counter (including sign) [MarshalAs(UnmanagedType.U1)] bool ViewOrderInverted, // if view order is inverted (left and right mirrored) [MarshalAs(UnmanagedType.U1)] bool Rotated, // if monitor is rotated by 90° (not supported by deprecated algorithm) [MarshalAs(UnmanagedType.U1)] bool FullPixel, // if only full pixel available (lens is always over full pixel; no sub pixel addressable) From 7899211ea61dea9b45dbbf43689366b8a1c6ac7a Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 11 Aug 2025 13:38:14 +0200 Subject: [PATCH 022/149] working view mapping --- Resources/G3DHDRPMultiviewMosaicVector.shader | 42 ++++++++++--------- .../ViewmapGeneratorInterface.cs | 33 ++++++++------- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/Resources/G3DHDRPMultiviewMosaicVector.shader b/Resources/G3DHDRPMultiviewMosaicVector.shader index e16c6f1..79569b6 100644 --- a/Resources/G3DHDRPMultiviewMosaicVector.shader +++ b/Resources/G3DHDRPMultiviewMosaicVector.shader @@ -29,46 +29,48 @@ Shader "G3D/MultiviewMosaicVector" uint nativeViewCount = 16; // length of indexMap int mirror; // 1: mirror from left to right, 0: no mirror + int mosaic_columns = 4; // number of columns in the mosaic + int mosaic_rows = 4; // number of rows in the mosaic // viewIndices are the view indices for the three color channels // i.e. viewIndex.x maps to view 3, viewIndex.y maps to view 5, and viewIndex.z maps to view 8 float3 GetColors(int3 viewIndices, v2f i) { - float3 OutColors = float3(0.0, 0.0, 0.0); - float2 uvCoords = i.uv; // mirror the image if necessary if (mirror != 0) { uvCoords.x = 1.0 - uvCoords.x; } - + float3 OutColors = float3(0.0, 0.0, 0.0); for (int ColorIndex = 0; ColorIndex < 3; ColorIndex ++) { int viewIndex = viewIndices[ColorIndex]; + + uint TextureIndex = 255; if (viewIndex < nativeViewCount) { TextureIndex = indexMap[viewIndex]; } - + float2 mappedUVCoords = calculateUVForMosaic(TextureIndex, uvCoords); float4 tmpColor = _colorMosaic.Sample(sampler_colorMosaic, mappedUVCoords); - - float Texture1 = 0.0; + + float colorValue = 0.0; switch (TextureIndex) { - case 254: + case 254: if (ColorIndex == 0) - Texture1 = 1.0; + colorValue = 1.0; break; - case 255: + case 255: break; - default: - Texture1 = tmpColor[ColorIndex]; + default: + colorValue = tmpColor[ColorIndex]; break; } - OutColors[ColorIndex] = Texture1; + OutColors[ColorIndex] = colorValue; } return OutColors; @@ -81,6 +83,7 @@ Shader "G3D/MultiviewMosaicVector" float xScreenCoords = i.uv.x * viewportWidth + viewport_pos_x; viewport_pos_y = viewport_pos_y - viewportHeight; float yScreenCoords = (viewportHeight - i.uv.y * viewportHeight) + viewport_pos_y; + // position of window in screen coordinates uint2 computedScreenPos = uint2(xScreenCoords, yScreenCoords); @@ -89,14 +92,15 @@ Shader "G3D/MultiviewMosaicVector" // get view indices from view map // every color value contain index of view in view array to use for this color // this works only, if texture is flipped in y since texelFetch use 0,0 for bottom/left - float4 ViewIndexFloat = _viewMap.Sample(sampler_viewMap, screenPosUV); - - // since value is normalized (0..1), we bring it back to byte value - int3 ViewIndex = int3(round(ViewIndexFloat.x * 255), - round(ViewIndexFloat.y * 255), - round(ViewIndexFloat.z * 255)); + float4 viewIndexFloat = _viewMap.Load(float4(computedScreenPos, 0, 0)) * 255; + + int3 ViewIndex = int3(round(viewIndexFloat.x), + round(viewIndexFloat.y), + round(viewIndexFloat.z)); - return float4(GetColors(ViewIndex, i), 1.0f); + // get colors for the view indices + float3 colors = GetColors(ViewIndex, i); + return float4(colors, 1.0f); } ENDHLSL diff --git a/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs b/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs index 67a8a05..ded2060 100644 --- a/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs +++ b/Scripts/VectorBasedViewMap/ViewmapGeneratorInterface.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.InteropServices; using UnityEngine; +using UnityEngine.Experimental.Rendering; internal enum G3DViewMapAlignment { @@ -120,6 +121,18 @@ out resultViewMapPtr byte[] managedArray = new byte[ResultViewMapSize]; Marshal.Copy(resultViewMapPtr, managedArray, 0, (int)ResultViewMapSize); + Color32[] data = new Color32[ResultViewMapWidth * ResultViewMapHeight]; + for (int y = 0; y < ResultViewMapHeight; y++) + for (int x = 0; x < ResultViewMapWidth; x++) + { + data[y * ResultViewMapWidth + x] = new Color32( + managedArray[y * ResultViewMapWidth * 3 + x * 3 + 2], + managedArray[y * ResultViewMapWidth * 3 + x * 3 + 1], + managedArray[y * ResultViewMapWidth * 3 + x * 3 + 0], + 255 + ); + } + G3DMonitor_Error freeMapResult = ViewmapGeneratorCpp.G3DMonitor_FreeViewMap( resultViewMapPtr ); @@ -139,23 +152,11 @@ out resultViewMapPtr Texture2D texture = new Texture2D( (int)ResultViewMapWidth, (int)ResultViewMapHeight, - TextureFormat.RGB24, - false + GraphicsFormat.R8G8B8A8_UNorm, + 0 ); - // for (int x = 0; x < ResultViewMapWidth; x++) - // { - // for (int y = 0; y < ResultViewMapHeight; y++) - // { - // int index = (y * (int)ResultViewMapWidth + x) * 3; - // Color color = new Color( - // managedArray[index] / 255f, - // managedArray[index + 1] / 255f, - // managedArray[index + 2] / 255f - // ); - // texture.SetPixel(x, y, color); - // } - // } - texture.LoadRawTextureData(managedArray); + texture.filterMode = FilterMode.Point; + texture.SetPixels32(data); return texture; } } From 7b3915cde25764b89b733a32730a19fd98838134 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 12 Aug 2025 12:34:56 +0200 Subject: [PATCH 023/149] flip cameras --- G3DCamera.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 4672601..1cbee16 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -1662,7 +1662,7 @@ int tmpCameraCount offset = offset + correctionTerm; } - int flip = mirrorViews ? -1 : 1; + int flip = mirrorViews ? 1 : -1; return offset * flip; } From 5e7551e0609ce16417dd67bed1a1ac503b23c0f5 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 12 Aug 2025 14:37:38 +0200 Subject: [PATCH 024/149] render depth maps at same resolution as color images --- G3DCamera.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 1cbee16..223269d 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -398,9 +398,17 @@ private void initCustomPass() depthMosaicPass.indivDepthTextures = new RenderTexture[internalCameraCount]; for (int i = 0; i < internalCameraCount; i++) { + // TODO update render texture sizes when screen size changes + + float width = Screen.width; + float height = Screen.height; + + width = width * (renderResolutionScale / 100f); + height = height * (renderResolutionScale / 100f); + RenderTexture depthTexture = new RenderTexture( - cameras[0].pixelWidth, - cameras[0].pixelHeight, + (int)width, + (int)height, 0, RenderTextureFormat.Depth ); From 4912808f496c90c03d390f4b206ef5427009c414 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 12 Aug 2025 17:16:04 +0200 Subject: [PATCH 025/149] use point_repeat sampler for texture sampling --- Resources/G3DViewGeneration.shader | 48 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index 1277702..92e101b 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -89,45 +89,45 @@ Shader "G3D/ViewGeneration" Texture2D _depthMap14; Texture2D _depthMap15; - SamplerState sampler_linear_repeat; + SamplerState sampler_point_repeat; // Set the texture array as a shader input float getCameraLogDepth(float2 uv, int cameraIndex) { switch(cameraIndex) { case 0: - return _depthMap0.Sample( sampler_linear_repeat, uv).r; + return _depthMap0.Sample( sampler_point_repeat, uv).r; case 1: - return _depthMap1.Sample( sampler_linear_repeat, uv).r; + return _depthMap1.Sample( sampler_point_repeat, uv).r; case 2: - return _depthMap2.Sample( sampler_linear_repeat, uv).r; + return _depthMap2.Sample( sampler_point_repeat, uv).r; case 3: - return _depthMap3.Sample( sampler_linear_repeat, uv).r; + return _depthMap3.Sample( sampler_point_repeat, uv).r; case 4: - return _depthMap4.Sample( sampler_linear_repeat, uv).r; + return _depthMap4.Sample( sampler_point_repeat, uv).r; case 5: - return _depthMap5.Sample( sampler_linear_repeat, uv).r; + return _depthMap5.Sample( sampler_point_repeat, uv).r; case 6: - return _depthMap6.Sample( sampler_linear_repeat, uv).r; + return _depthMap6.Sample( sampler_point_repeat, uv).r; case 7: - return _depthMap7.Sample( sampler_linear_repeat, uv).r; + return _depthMap7.Sample( sampler_point_repeat, uv).r; case 8: - return _depthMap8.Sample( sampler_linear_repeat, uv).r; + return _depthMap8.Sample( sampler_point_repeat, uv).r; case 9: - return _depthMap9.Sample( sampler_linear_repeat, uv).r; + return _depthMap9.Sample( sampler_point_repeat, uv).r; case 10: - return _depthMap10.Sample( sampler_linear_repeat, uv).r; + return _depthMap10.Sample( sampler_point_repeat, uv).r; case 11: - return _depthMap11.Sample( sampler_linear_repeat, uv).r; + return _depthMap11.Sample( sampler_point_repeat, uv).r; case 12: - return _depthMap12.Sample( sampler_linear_repeat, uv).r; + return _depthMap12.Sample( sampler_point_repeat, uv).r; case 13: - return _depthMap13.Sample( sampler_linear_repeat, uv).r; + return _depthMap13.Sample( sampler_point_repeat, uv).r; case 14: - return _depthMap14.Sample( sampler_linear_repeat, uv).r; + return _depthMap14.Sample( sampler_point_repeat, uv).r; case 15: - return _depthMap15.Sample( sampler_linear_repeat, uv).r; + return _depthMap15.Sample( sampler_point_repeat, uv).r; default: - return _depthMap0.Sample( sampler_linear_repeat, uv).r; // use left camera depth map as default + return _depthMap0.Sample( sampler_point_repeat, uv).r; // use left camera depth map as default } } @@ -271,17 +271,17 @@ Shader "G3D/ViewGeneration" float4 finalColor = float4(0.0f, 0.0f, 0.0f, 0.0f); float factor = 0.0f; if (!discardFragmentLeft) { - finalColor += _leftCamTex.Sample(sampler_leftCamTex, shiftedLeftTexcoords); // sample the left camera texture + finalColor += _leftCamTex.Sample(sampler_point_repeat, shiftedLeftTexcoords); // sample the left camera texture factor += 1.0f; // increase the factor for the left camera } - if (!discardFragmentRight) { - finalColor += _rightCamTex.Sample(sampler_rightCamTex, shiftedRightTexcoords); // sample the right camera texture - factor += 1.0f; // increase the factor for the right camera - } if( !discardFragmentMiddle) { // if the final color is not set yet, sample the middle camera texture - finalColor += _middleCamTex.Sample(sampler_middleCamTex, shiftedMiddleTexcoords); // sample the middle camera texture + finalColor += _middleCamTex.Sample(sampler_point_repeat, shiftedMiddleTexcoords); // sample the middle camera texture factor += 1.0f; // increase the factor for the middle camera } + if (!discardFragmentRight) { + finalColor += _rightCamTex.Sample(sampler_point_repeat, shiftedRightTexcoords); // sample the right camera texture + factor += 1.0f; // increase the factor for the right camera + } return finalColor / factor; } From 902e2fcd04b2f0b64d46ac96fb57ba980133d989 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 12 Aug 2025 17:16:30 +0200 Subject: [PATCH 026/149] disabled view mapping --- G3DCamera.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 223269d..f42f621 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -249,7 +249,7 @@ private float scaledHalfCameraWidthAtStart #endregion private bool generateViews = true; - private bool useVectorMapViewGeneration = true; + private bool useVectorMapViewGeneration = false; private Material viewGenerationMaterial; // TODO Handle viewport resizing/ moving From f0f6fdec0b2fc958095bbdb524c22860586dd837 Mon Sep 17 00:00:00 2001 From: Jim Date: Tue, 12 Aug 2025 16:51:32 +0200 Subject: [PATCH 027/149] plugin importer --- .../G3D_HeadTrackingLibrary_c.dll.meta | 27 ++++++++++++++++++- .../G3D_HeadTrackingLibrary_c.lib.meta | 27 ++++++++++++++++++- Runtime/HeadTrackingLib/ftd2xx.dll.meta | 27 ++++++++++++++++++- Runtime/HeadTrackingLib/opengl32sw.dll.meta | 27 ++++++++++++++++++- 4 files changed, 104 insertions(+), 4 deletions(-) diff --git a/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.dll.meta b/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.dll.meta index b09c2a1..5d36653 100644 --- a/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.dll.meta +++ b/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.dll.meta @@ -1,2 +1,27 @@ fileFormatVersion: 2 -guid: 00e77c5faaa540a419efd61fd32a5318 \ No newline at end of file +guid: 00e77c5faaa540a419efd61fd32a5318 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.lib.meta b/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.lib.meta index f999223..f272ad0 100644 --- a/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.lib.meta +++ b/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.lib.meta @@ -1,2 +1,27 @@ fileFormatVersion: 2 -guid: 103214c26ba3e8d44b0d9c8b3d746260 \ No newline at end of file +guid: 103214c26ba3e8d44b0d9c8b3d746260 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/HeadTrackingLib/ftd2xx.dll.meta b/Runtime/HeadTrackingLib/ftd2xx.dll.meta index aa783c5..ffd3168 100644 --- a/Runtime/HeadTrackingLib/ftd2xx.dll.meta +++ b/Runtime/HeadTrackingLib/ftd2xx.dll.meta @@ -1,2 +1,27 @@ fileFormatVersion: 2 -guid: 248b6bdb18cf378429710d798a04db7a \ No newline at end of file +guid: 248b6bdb18cf378429710d798a04db7a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/HeadTrackingLib/opengl32sw.dll.meta b/Runtime/HeadTrackingLib/opengl32sw.dll.meta index b74acf4..90ac4b7 100644 --- a/Runtime/HeadTrackingLib/opengl32sw.dll.meta +++ b/Runtime/HeadTrackingLib/opengl32sw.dll.meta @@ -1,2 +1,27 @@ fileFormatVersion: 2 -guid: dc294affd701dc44d803c218a19c820e \ No newline at end of file +guid: dc294affd701dc44d803c218a19c820e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: From 265447d14a8d8ebb3607a618b76a6dde4dcacc3f Mon Sep 17 00:00:00 2001 From: Jim Date: Tue, 12 Aug 2025 17:16:59 +0200 Subject: [PATCH 028/149] fix linear vs srgb problem --- G3DCamera.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index f42f621..2256b20 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -431,7 +431,9 @@ private void initCustomPass() viewGenerationPass.computeShaderResultTexture = new RenderTexture( mainCamera.pixelWidth, mainCamera.pixelHeight, - 0 + 0, + RenderTextureFormat.Default, + RenderTextureReadWrite.Linear ); viewGenerationPass.computeShaderResultTexture.enableRandomWrite = true; viewGenerationPass.computeShaderResultTexture.Create(); From 9a8ed7be9664728d01e3f7fd7fdb05aa0f437ec8 Mon Sep 17 00:00:00 2001 From: Jim Date: Tue, 12 Aug 2025 17:17:15 +0200 Subject: [PATCH 029/149] use uint instead of int --- Resources/G3DViewGenHoleFilling.compute | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index 7d8c635..8f526e9 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -55,8 +55,8 @@ int grid_size_y; int radius; float sigma; -int imageWidth; -int imageHeight; +uint imageWidth; +uint imageHeight; Texture2D _depthMap0; Texture2D _depthMap1; @@ -208,13 +208,9 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) return _colorMosaic.SampleLevel(sampler_colorMosaic, uv, 0); } - - - float px_radius_left = 0; float px_radius_right = 0; - float depthAtCenter = Linear01DepthViewBased(cellTexCoords, viewIndex); float4 cellColor = getColorMosaic(cellTexCoords, viewIndex); // sample the color of the left camera texture if( cellColor.w >= 1.0f) { @@ -266,19 +262,19 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) [numthreads(32,32,1)] void kernelFunction (uint3 id : SV_DispatchThreadID) { - int numThreadsX = 32; - int numThreadsY = 32; + uint numThreadsX = 32; + uint numThreadsY = 32; - float xCohortSize = floor((imageWidth + numThreadsX) / numThreadsX); - float yCohortSize = floor((imageHeight + numThreadsY) / numThreadsY); + uint xCohortSize = floor((imageWidth + numThreadsX) / numThreadsX); + uint yCohortSize = floor((imageHeight + numThreadsY) / numThreadsY); float xStepSize = 1.0f / (float)imageWidth; float yStepSize = 1.0f / (float)imageHeight; - for(int i = 0; i < xCohortSize; i++) { - for(int j = 0; j < yCohortSize; j++) { - int x = id.x * xCohortSize + i; - int y = id.y * yCohortSize + j; + for(uint i = 0; i < xCohortSize; i++) { + for(uint j = 0; j < yCohortSize; j++) { + uint x = id.x * xCohortSize + i; + uint y = id.y * yCohortSize + j; if (x >= imageWidth || y >= imageHeight) { continue; // skip if out of bounds From ab4be3731b6476384a7238f9b60c823b3db22469 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 13 Aug 2025 14:12:56 +0200 Subject: [PATCH 030/149] added fxaa postprocess pass --- G3DCamera.cs | 2 +- Resources/G3DFXAA.compute | 185 ++++++++++++++++++++ Resources/G3DFXAA.compute.meta | 7 + Resources/G3DViewGenHoleFilling.compute | 1 - Scripts/G3DHDRPViewGenerationPass.cs | 223 +++++++++++++++--------- 5 files changed, 329 insertions(+), 89 deletions(-) create mode 100644 Resources/G3DFXAA.compute create mode 100644 Resources/G3DFXAA.compute.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index 2256b20..848e823 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -432,7 +432,7 @@ private void initCustomPass() mainCamera.pixelWidth, mainCamera.pixelHeight, 0, - RenderTextureFormat.Default, + RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear ); viewGenerationPass.computeShaderResultTexture.enableRandomWrite = true; diff --git a/Resources/G3DFXAA.compute b/Resources/G3DFXAA.compute new file mode 100644 index 0000000..2a2b97e --- /dev/null +++ b/Resources/G3DFXAA.compute @@ -0,0 +1,185 @@ +// Each #kernel tells which function to compile; you can have many kernels +#pragma kernel FXAA +#pragma multi_compile _ ENABLE_ALPHA +#pragma multi_compile _ HDR_INPUT + +#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch + +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/PostProcessDefines.hlsl" + +RWTexture2D _OutputTexture; +Texture2D _colorMosaic; +SamplerState sampler_colorMosaic; + + +float4 _HDROutputParams; +#define _PaperWhite _HDROutputParams.z +#define _OneOverPaperWhite _HDROutputParams.w + +#define FXAA_HDR_MAPUNMAP defined(HDR_INPUT) +#define FXAA_SPAN_MAX (4.0) +#define FXAA_REDUCE_MUL (1.0 / 4.0) +#define FXAA_REDUCE_MIN (1.0 / 32.0) +// original: +// #define FXAA_SPAN_MAX (8.0) +// #define FXAA_REDUCE_MUL (1.0 / 8.0) +// #define FXAA_REDUCE_MIN (1.0 / 128.0) + +float3 Fetch(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 coords, float2 offset) +{ + float2 uv = saturate(coords + offset) * _RTHandlePostProcessScale.xy; + return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).xyz; +} + +CTYPE Load(TEXTURE2D(_InputTexture), int2 icoords, int idx, int idy) +{ + return LOAD_TEXTURE2D(_InputTexture, min(icoords + int2(idx, idy), _PostProcessScreenSize.xy - 1.0)).CTYPE_SWIZZLE; +} + +float FetchAlpha(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 coords, float2 offset) +{ + float2 uv = saturate(coords + offset) * _RTHandleScale.xy; + return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).w; +} + +void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout CTYPE outColor, uint2 positionSS, float2 positionNDC, float paperWhite, float oneOverPaperWhite) +{ + { + // Edge detection + CTYPE rgbNW = Load(_InputTexture, positionSS, -1, -1); + CTYPE rgbNE = Load(_InputTexture, positionSS, 1, -1); + CTYPE rgbSW = Load(_InputTexture, positionSS, -1, 1); + CTYPE rgbSE = Load(_InputTexture, positionSS, 1, 1); + +#if FXAA_HDR_MAPUNMAP + // The pixel values we have are already tonemapped but in the range [0, 10000] nits. To run FXAA properly, we need to convert them + // to a SDR range [0; 1]. Since the tonemapped values are not evenly distributed and mostly close to the paperWhite nits value, we can + // normalize by paperWhite to get most of the scene in [0; 1] range. For the remaining pixels, we can use the FastTonemap() to remap + // them to [0, 1] range. + float lumaNW = Luminance(FastTonemap(rgbNW.xyz * oneOverPaperWhite)); + float lumaNE = Luminance(FastTonemap(rgbNE.xyz * oneOverPaperWhite)); + float lumaSW = Luminance(FastTonemap(rgbSW.xyz * oneOverPaperWhite)); + float lumaSE = Luminance(FastTonemap(rgbSE.xyz * oneOverPaperWhite)); + float lumaM = Luminance(FastTonemap(outColor.xyz * oneOverPaperWhite)); +#else + rgbNW.xyz = saturate(rgbNW.xyz); + rgbNE.xyz = saturate(rgbNE.xyz); + rgbSW.xyz = saturate(rgbSW.xyz); + rgbSE.xyz = saturate(rgbSE.xyz); + outColor.xyz = saturate(outColor.xyz); + float lumaNW = Luminance(rgbNW.xyz); + float lumaNE = Luminance(rgbNE.xyz); + float lumaSW = Luminance(rgbSW.xyz); + float lumaSE = Luminance(rgbSE.xyz); + float lumaM = Luminance(outColor.xyz); +#endif + + float2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + float lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; + float dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + float rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); + + dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; + + // Blur + float3 rgb03 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); + float3 rgb13 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); + float3 rgb23 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); + float3 rgb33 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); + +#if FXAA_HDR_MAPUNMAP + rgb03 = FastTonemap(rgb03 * oneOverPaperWhite); + rgb13 = FastTonemap(rgb13 * oneOverPaperWhite); + rgb23 = FastTonemap(rgb23 * oneOverPaperWhite); + rgb33 = FastTonemap(rgb33 * oneOverPaperWhite); +#else + rgb03 = saturate(rgb03); + rgb13 = saturate(rgb13); + rgb23 = saturate(rgb23); + rgb33 = saturate(rgb33); +#endif + + float3 rgbA = 0.5 * (rgb13 + rgb23); + float3 rgbB = rgbA * 0.5 + 0.25 * (rgb03 + rgb33); + + float lumaB = Luminance(rgbB); + + float lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); + float lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); + + float3 rgb = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? rgbA : rgbB; + +#if FXAA_HDR_MAPUNMAP + outColor.xyz = FastTonemapInvert(rgb) * paperWhite;; +#else + outColor.xyz = rgb; +#endif + +#ifdef ENABLE_ALPHA + // FXAA for the alpha channel: alpha can be completely decorelated from the RGB channels, so we might fetch different neighbors for the alpha! + // For this reason we have to recompute the fetch direction + lumaNW = rgbNW.w; + lumaNE = rgbNE.w; + lumaSW = rgbSW.w; + lumaSE = rgbSE.w; + lumaM = outColor.w; + + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; + dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); + + dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; + + // Blur + float a03 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); + float a13 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); + float a23 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); + float a33 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); + + a03 = saturate(a03); + a13 = saturate(a13); + a23 = saturate(a23); + a33 = saturate(a33); + + float A = 0.5 * (a13 + a23); + float B = A * 0.5 + 0.25 * (a03 + a33); + + lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); + lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); + + outColor.w = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? A : B; +#endif + + } +} + + +[numthreads(8,8,1)] +void FXAA(uint3 dispatchThreadId : SV_DispatchThreadID) +{ + UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z); + uint2 positionSS = dispatchThreadId.xy; + float2 positionNDC = positionSS * _PostProcessScreenSize.zw + (0.5 * _PostProcessScreenSize.zw); + + float3 outColor = _colorMosaic.Load(float4(positionSS, 0, 0)).xyz; + float3 beforeFXAA = outColor.xyz; + + RunFXAA(_colorMosaic, s_linear_clamp_sampler, outColor, positionSS, positionNDC, _PaperWhite, _OneOverPaperWhite); + +#if defined(ENABLE_ALPHA) + // When alpha processing is enabled, FXAA should not affect pixels with zero alpha + outColor.xyz = outColor.a > 0 ? outColor.xyz : beforeFXAA.xyz; +#endif + + _OutputTexture[positionSS] = float4(outColor, 1.0); +} diff --git a/Resources/G3DFXAA.compute.meta b/Resources/G3DFXAA.compute.meta new file mode 100644 index 0000000..686b3a3 --- /dev/null +++ b/Resources/G3DFXAA.compute.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a9a71f14ec65e804188d9e2d40bb9721 +ComputeShaderImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index 8f526e9..e2feb0d 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -284,7 +284,6 @@ void kernelFunction (uint3 id : SV_DispatchThreadID) float4 color = fragHDRP(uv, xStepSize, yStepSize); Result[float2(x, y)] = color; - // Result[float2(x, y)] = float4(uv,0.1f, 1.0f); // write the color to the result texture } } } \ No newline at end of file diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index fe4af3b..dda1b61 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -15,6 +15,7 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public RTHandle mosaicImageHandle; private ComputeShader holeFillingCompShader; + private int holeFillingKernel; public RenderTexture computeShaderResultTexture; public RTHandle computeShaderResultTextureHandle; @@ -29,9 +30,24 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public RenderTexture[] indivDepthMaps; + private ComputeShader fxaaCompShader; + private int fxaaKernel; + private bool fxaaEnabled = true; + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { - holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); + if (fillHoles) + { + holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); + holeFillingKernel = holeFillingCompShader.FindKernel("kernelFunction"); + } + + if (fxaaEnabled) + { + fxaaCompShader = Resources.Load("G3DFXAA"); + fxaaKernel = fxaaCompShader.FindKernel("FXAA"); + } + blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); } @@ -39,7 +55,10 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff private void setMatrix(CustomPassContext ctx, Matrix4x4 matrix, string name) { ctx.propertyBlock.SetMatrix(Shader.PropertyToID(name), matrix); - ctx.cmd.SetComputeMatrixParam(holeFillingCompShader, name, matrix); + if (fillHoles) + { + ctx.cmd.SetComputeMatrixParam(holeFillingCompShader, name, matrix); + } } private void addViewProjectionMatrix(CustomPassContext ctx, Camera camera, string name) @@ -55,102 +74,132 @@ protected override void Execute(CustomPassContext ctx) var camera = ctx.hdCamera.camera; if (isMainG3DCamera(camera)) { - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + runReprojection(ctx); + // color image now in mosaicImageHandle - // upload all inv view projection matrices - for (int i = 0; i < internalCameraCount; i++) - { - Matrix4x4 projectionMatrixInner = GL.GetGPUProjectionMatrix( - cameras[i].projectionMatrix, - false - ); - Matrix4x4 viewMatrixInner = cameras[i].worldToCameraMatrix; - - Matrix4x4 viewProjectionMatrixInner = projectionMatrixInner * viewMatrixInner; - Matrix4x4 invGPUProjMatrix = viewProjectionMatrixInner.inverse; - - setMatrix(ctx, invGPUProjMatrix, "inverseProjMatrix" + i); - setMatrix(ctx, cameras[i].worldToCameraMatrix, "viewMatrix" + i); - } + runHoleFilling(ctx); - addViewProjectionMatrix(ctx, cameras[0], "leftViewProjMatrix"); - addViewProjectionMatrix(ctx, cameras[internalCameraCount / 2], "middleViewProjMatrix"); - addViewProjectionMatrix(ctx, cameras[internalCameraCount - 1], "rightViewProjMatrix"); + runFXAA(ctx); - if (debugRendering && !fillHoles) + if (debugRendering) { CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); } - else - { - CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); - } + } + } - CoreUtils.DrawFullScreen( - ctx.cmd, - fullscreenPassMaterial, - ctx.propertyBlock, - shaderPassId: 0 + private void runReprojection(CustomPassContext ctx) + { + // upload all inv view projection matrices + for (int i = 0; i < internalCameraCount; i++) + { + Matrix4x4 projectionMatrixInner = GL.GetGPUProjectionMatrix( + cameras[i].projectionMatrix, + false ); + Matrix4x4 viewMatrixInner = cameras[i].worldToCameraMatrix; - if (fillHoles) - { - // fill holes in the mosaic image - int kernel = holeFillingCompShader.FindKernel("kernelFunction"); - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - kernel, - "Result", - computeShaderResultTexture - ); - for (int i = 0; i < internalCameraCount; i++) - { - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - kernel, - "_depthMap" + i, - indivDepthMaps[i] - ); - } - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - kernel, - "_colorMosaic", - mosaicImageHandle - ); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_x", 4); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_y", 4); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); - ctx.cmd.SetComputeFloatParam( - holeFillingCompShader, - "sigma", - holeFillingRadius / 2.0f - ); - ctx.cmd.SetComputeIntParam( - holeFillingCompShader, - "imageWidth", - mosaicImageHandle.rt.width - ); - ctx.cmd.SetComputeIntParam( - holeFillingCompShader, - "imageHeight", - mosaicImageHandle.rt.height - ); - - ctx.cmd.DispatchCompute(holeFillingCompShader, kernel, 32, 32, 1); - - if (debugRendering) - { - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - } - else - { - CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); - } + Matrix4x4 viewProjectionMatrixInner = projectionMatrixInner * viewMatrixInner; + Matrix4x4 invGPUProjMatrix = viewProjectionMatrixInner.inverse; - CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); - } + setMatrix(ctx, invGPUProjMatrix, "inverseProjMatrix" + i); + setMatrix(ctx, cameras[i].worldToCameraMatrix, "viewMatrix" + i); + } + + addViewProjectionMatrix(ctx, cameras[0], "leftViewProjMatrix"); + addViewProjectionMatrix(ctx, cameras[internalCameraCount / 2], "middleViewProjMatrix"); + addViewProjectionMatrix(ctx, cameras[internalCameraCount - 1], "rightViewProjMatrix"); + + // always render to mosaic image handle + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + + // reprojection pass + CoreUtils.DrawFullScreen( + ctx.cmd, + fullscreenPassMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); + } + + private void runHoleFilling(CustomPassContext ctx) + { + if (fillHoles == false) + { + return; } + + // fill holes in the mosaic image + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + holeFillingKernel, + "Result", + computeShaderResultTexture + ); + for (int i = 0; i < internalCameraCount; i++) + { + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + holeFillingKernel, + "_depthMap" + i, + indivDepthMaps[i] + ); + } + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + holeFillingKernel, + "_colorMosaic", + mosaicImageHandle + ); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_x", 4); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_y", 4); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); + ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "imageWidth", mosaicImageHandle.rt.width); + ctx.cmd.SetComputeIntParam( + holeFillingCompShader, + "imageHeight", + mosaicImageHandle.rt.height + ); + + ctx.cmd.DispatchCompute(holeFillingCompShader, holeFillingKernel, 32, 32, 1); + + // Blit the result to the mosaic image + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); + } + + private void runFXAA(CustomPassContext ctx) + { + if (fxaaEnabled == false) + { + return; + } + Tonemapping tonemapping = ctx.hdCamera.volumeStack.GetComponent(); + float paperWhite = tonemapping.paperWhite.value; + Vector4 hdroutParameters = new Vector4(0, 1000, paperWhite, 1f / paperWhite); + + ctx.cmd.SetComputeTextureParam( + fxaaCompShader, + fxaaKernel, + "_colorMosaic", + mosaicImageHandle + ); + ctx.cmd.SetComputeTextureParam( + fxaaCompShader, + fxaaKernel, + "_OutputTexture", + computeShaderResultTexture + ); + ctx.cmd.SetComputeVectorParam(fxaaCompShader, "_HDROutputParams", hdroutParameters); + ctx.cmd.DispatchCompute( + fxaaCompShader, + fxaaKernel, + (mosaicImageHandle.rt.width + 7) / 8, + (mosaicImageHandle.rt.height + 7) / 8, + 1 + ); } /// From d5d741188932e11b9a5efe4bb9e9e70e20eca9b3 Mon Sep 17 00:00:00 2001 From: Jim Date: Wed, 13 Aug 2025 15:02:54 +0200 Subject: [PATCH 031/149] avoid singularity and make O(2n) --- G3DCamera.cs | 2 +- Resources/G3DViewGenHoleFilling.compute | 54 ++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 848e823..bec278e 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -377,7 +377,7 @@ private void initCustomPass() { bool debugRendering = false; bool isFillingHoles = false; - int holeFillingRadius = 3; + int holeFillingRadius = 32; // init fullscreen postprocessing for hd render pipeline CustomPassVolume customPassVolume = gameObject.AddComponent(); diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index e2feb0d..6a13f7f 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -220,14 +220,58 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) float kernelSum = 0.0f; float4 colorSum = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int dpxX = -radius; dpxX <= radius; ++dpxX) { - for (int dpxY = -radius; dpxY <= radius; ++dpxY) { + // for (int dpxY = -radius; dpxY <= radius; ++dpxY) { + float dx = dpxX * xStepSize; - float dy = dpxY * yStepSize; + + // float dy = dpxY * yStepSize; float x = clamp(cellTexCoords.x + dx, 0, 1.0f); + float y = clamp(cellTexCoords.y, 0, 1.0f); + // float y = clamp(cellTexCoords.y + dy, 0, 1.0f); + + float distance = dpxX; + // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); + float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); + // weight = 1.0; + + float2 sampleUV = float2(x, y); + + float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); + float4 color_sample = getColorMosaic(sampleUV, viewIndex); + + if (color_sample.w < 1.0f) { + // only collect colors from known values -> skip holes + continue; + } + + if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back + // if center is further at the back then skip sample or reduce weight + + weight = 0.1f; + } else { + // reduce weight with distance to center: + float diff = abs(depthAtCenter - depth_sample); + weight = weight * (diff * diff); + } + + kernelSum += weight; + colorSum += weight * color_sample; + // } + } + + // for (int dpxX = -radius; dpxX <= radius; ++dpxX) { + for (int dpxY = -radius; dpxY <= radius; ++dpxY) { + + // float dx = dpxX * xStepSize; + + float dy = dpxY * yStepSize; + float x = clamp(cellTexCoords.x, 0, 1.0f); float y = clamp(cellTexCoords.y + dy, 0, 1.0f); - float distance = sqrt(dpxX * dpxX + dpxY * dpxY); + float distance = dpxY; + // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); // weight = 1.0; @@ -244,7 +288,7 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back // if center is further at the back then skip sample or reduce weight - weight = 0.0f; + weight = 0.1f; } else { // reduce weight with distance to center: float diff = abs(depthAtCenter - depth_sample); @@ -254,7 +298,7 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) kernelSum += weight; colorSum += weight * color_sample; } - } + // } return float4(colorSum.xyz / kernelSum, 1.0f); // return the average color of the samples } From 9e2e6dfa030d030da85f7644aa1feccb7b3168cf Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 13 Aug 2025 15:15:50 +0200 Subject: [PATCH 032/149] tiny refactor --- G3DCamera.cs | 9 +++++---- Scripts/G3DHDRPViewGenerationPass.cs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index bec278e..729a40a 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -250,6 +250,10 @@ private float scaledHalfCameraWidthAtStart private bool generateViews = true; private bool useVectorMapViewGeneration = false; + private bool isFillingHoles = true; + private bool applyFSAA = true; + private bool debugRendering = true; + private int holeFillingRadius = 8; private Material viewGenerationMaterial; // TODO Handle viewport resizing/ moving @@ -375,10 +379,6 @@ void Start() #if G3D_HDRP private void initCustomPass() { - bool debugRendering = false; - bool isFillingHoles = false; - int holeFillingRadius = 32; - // init fullscreen postprocessing for hd render pipeline CustomPassVolume customPassVolume = gameObject.AddComponent(); customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; @@ -452,6 +452,7 @@ private void initCustomPass() viewGenerationPass.debugRendering = debugRendering; viewGenerationPass.fillHoles = isFillingHoles; viewGenerationPass.holeFillingRadius = holeFillingRadius; + viewGenerationPass.fxaaEnabled = applyFSAA; // add autostereo mosaic generation pass RenderTexture mosaicTexture = new RenderTexture( diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index dda1b61..1cd79ea 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -32,7 +32,7 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass private ComputeShader fxaaCompShader; private int fxaaKernel; - private bool fxaaEnabled = true; + public bool fxaaEnabled; protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { From 239098e7309d60639ed22682a3be084c2f9c67ef Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 13 Aug 2025 15:45:35 +0200 Subject: [PATCH 033/149] fix typo; fix build issue; fix debug rendering --- G3DCamera.cs | 8 ++++---- Resources/G3DFXAA.compute | 28 ++++++++++++++-------------- Scripts/G3DHDRPViewGenerationPass.cs | 12 ++++++++++++ 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 729a40a..40f08e1 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -251,9 +251,9 @@ private float scaledHalfCameraWidthAtStart private bool generateViews = true; private bool useVectorMapViewGeneration = false; private bool isFillingHoles = true; - private bool applyFSAA = true; - private bool debugRendering = true; - private int holeFillingRadius = 8; + private bool applyFXAA = false; + private bool debugRendering = false; + private int holeFillingRadius = 4; private Material viewGenerationMaterial; // TODO Handle viewport resizing/ moving @@ -452,7 +452,7 @@ private void initCustomPass() viewGenerationPass.debugRendering = debugRendering; viewGenerationPass.fillHoles = isFillingHoles; viewGenerationPass.holeFillingRadius = holeFillingRadius; - viewGenerationPass.fxaaEnabled = applyFSAA; + viewGenerationPass.fxaaEnabled = applyFXAA; // add autostereo mosaic generation pass RenderTexture mosaicTexture = new RenderTexture( diff --git a/Resources/G3DFXAA.compute b/Resources/G3DFXAA.compute index 2a2b97e..d87be9e 100644 --- a/Resources/G3DFXAA.compute +++ b/Resources/G3DFXAA.compute @@ -35,9 +35,9 @@ float3 Fetch(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 coords return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).xyz; } -CTYPE Load(TEXTURE2D(_InputTexture), int2 icoords, int idx, int idy) +float4 Load(TEXTURE2D(_InputTexture), int2 icoords, int idx, int idy) { - return LOAD_TEXTURE2D(_InputTexture, min(icoords + int2(idx, idy), _PostProcessScreenSize.xy - 1.0)).CTYPE_SWIZZLE; + return LOAD_TEXTURE2D(_InputTexture, min(icoords + int2(idx, idy), _PostProcessScreenSize.xy - 1.0)); } float FetchAlpha(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 coords, float2 offset) @@ -46,14 +46,14 @@ float FetchAlpha(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 co return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).w; } -void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout CTYPE outColor, uint2 positionSS, float2 positionNDC, float paperWhite, float oneOverPaperWhite) +void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 outColor, uint2 positionSS, float2 positionNDC, float paperWhite, float oneOverPaperWhite) { { // Edge detection - CTYPE rgbNW = Load(_InputTexture, positionSS, -1, -1); - CTYPE rgbNE = Load(_InputTexture, positionSS, 1, -1); - CTYPE rgbSW = Load(_InputTexture, positionSS, -1, 1); - CTYPE rgbSE = Load(_InputTexture, positionSS, 1, 1); + float4 rgbNW = Load(_InputTexture, positionSS, -1, -1); + float4 rgbNE = Load(_InputTexture, positionSS, 1, -1); + float4 rgbSW = Load(_InputTexture, positionSS, -1, 1); + float4 rgbSE = Load(_InputTexture, positionSS, 1, 1); #if FXAA_HDR_MAPUNMAP // The pixel values we have are already tonemapped but in the range [0, 10000] nits. To run FXAA properly, we need to convert them @@ -171,15 +171,15 @@ void FXAA(uint3 dispatchThreadId : SV_DispatchThreadID) uint2 positionSS = dispatchThreadId.xy; float2 positionNDC = positionSS * _PostProcessScreenSize.zw + (0.5 * _PostProcessScreenSize.zw); - float3 outColor = _colorMosaic.Load(float4(positionSS, 0, 0)).xyz; - float3 beforeFXAA = outColor.xyz; + float4 outColor = _colorMosaic.Load(float4(positionSS, 0, 0)); + float4 beforeFXAA = outColor; RunFXAA(_colorMosaic, s_linear_clamp_sampler, outColor, positionSS, positionNDC, _PaperWhite, _OneOverPaperWhite); -#if defined(ENABLE_ALPHA) - // When alpha processing is enabled, FXAA should not affect pixels with zero alpha - outColor.xyz = outColor.a > 0 ? outColor.xyz : beforeFXAA.xyz; -#endif +// #if defined(ENABLE_ALPHA) +// // When alpha processing is enabled, FXAA should not affect pixels with zero alpha +// outColor.xyz = outColor.a > 0 ? outColor.xyz : beforeFXAA.xyz; +// #endif - _OutputTexture[positionSS] = float4(outColor, 1.0); + _OutputTexture[positionSS] = outColor; } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 1cd79ea..2dff145 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -83,6 +83,18 @@ protected override void Execute(CustomPassContext ctx) if (debugRendering) { + if (fxaaEnabled == false && fillHoles == false) + { + blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), mosaicImageHandle); + } + else + { + blitMaterial.SetTexture( + Shader.PropertyToID("_mainTex"), + computeShaderResultTexture + ); + } + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); } From ed4f77a5cb1871faf092ce3068cdcaf8573c5123 Mon Sep 17 00:00:00 2001 From: Jim Date: Wed, 13 Aug 2025 16:19:03 +0200 Subject: [PATCH 034/149] fibonacci spiral sampling --- G3DCamera.cs | 4 + Resources/G3DViewGenHoleFilling.compute | 132 +++++++++++++++++------- 2 files changed, 97 insertions(+), 39 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 40f08e1..34f8755 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -379,6 +379,10 @@ void Start() #if G3D_HDRP private void initCustomPass() { + bool debugRendering = true; + bool isFillingHoles = true; + int holeFillingRadius = 32; + // init fullscreen postprocessing for hd render pipeline CustomPassVolume customPassVolume = gameObject.AddComponent(); customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index 6a13f7f..5802334 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -221,57 +221,57 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) float kernelSum = 0.0f; float4 colorSum = float4(0.0f, 0.0f, 0.0f, 0.0f); - for (int dpxX = -radius; dpxX <= radius; ++dpxX) { - // for (int dpxY = -radius; dpxY <= radius; ++dpxY) { + // Sample (0, 0) + { + float x = clamp(cellTexCoords.x, 0, 1.0f); + float y = clamp(cellTexCoords.y, 0, 1.0f); + + float weight = 1.0; - float dx = dpxX * xStepSize; + float2 sampleUV = float2(x, y); - // float dy = dpxY * yStepSize; - float x = clamp(cellTexCoords.x + dx, 0, 1.0f); - float y = clamp(cellTexCoords.y, 0, 1.0f); - // float y = clamp(cellTexCoords.y + dy, 0, 1.0f); + float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); + float4 color_sample = getColorMosaic(sampleUV, viewIndex); - float distance = dpxX; - // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); - float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); - // weight = 1.0; - - float2 sampleUV = float2(x, y); + if (color_sample.w < 1.0f) { + // only collect colors from known values -> skip holes + weight = 0.0; + } - float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); - float4 color_sample = getColorMosaic(sampleUV, viewIndex); + if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back + // if center is further at the back then skip sample or reduce weight - if (color_sample.w < 1.0f) { - // only collect colors from known values -> skip holes - continue; - } + weight = 0.1f; + } else { + // reduce weight with distance to center: + float diff = abs(depthAtCenter - depth_sample); + weight = weight * (diff * diff); + } - if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back - // if center is further at the back then skip sample or reduce weight + kernelSum += weight; + colorSum += weight * color_sample; + } - weight = 0.1f; - } else { - // reduce weight with distance to center: - float diff = abs(depthAtCenter - depth_sample); - weight = weight * (diff * diff); - } + float a = 1.0; + float b = 1.0; + float2 samplePoint = float2(-1.0, 0.0); + float side = 1.0; - kernelSum += weight; - colorSum += weight * color_sample; - // } - } - - // for (int dpxX = -radius; dpxX <= radius; ++dpxX) { - for (int dpxY = -radius; dpxY <= radius; ++dpxY) { - - // float dx = dpxX * xStepSize; + float2 stride = float2(1.0, 1.0); + for (uint i = 0; i < (uint) radius; i++) { + + // Sample at current Fibonacci point + { + float dpxX = samplePoint.x; + float dpxY = samplePoint.y; + float dx = dpxX * xStepSize; float dy = dpxY * yStepSize; - float x = clamp(cellTexCoords.x, 0, 1.0f); + float x = clamp(cellTexCoords.x + dx, 0, 1.0f); float y = clamp(cellTexCoords.y + dy, 0, 1.0f); - float distance = dpxY; // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); + float distance = side; float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); // weight = 1.0; @@ -282,7 +282,7 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) if (color_sample.w < 1.0f) { // only collect colors from known values -> skip holes - continue; + weight = 0.1f; } if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back @@ -298,6 +298,60 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) kernelSum += weight; colorSum += weight * color_sample; } + + if (i > 1) { + float next = a + b; + a = b; + b = next; + side = b; + } + + float2 square = float2(side, side); + + if ((i >> 1) & 1 != 0) { + square.x *= -1.0; + } + if (((i + 1) >> 1) & 1 != 0) { + square.y *= -1.0; + } + + samplePoint += square; + } + + // for (int dpxX = 1; dpxX <= radius; ++dpxX) { + // float dx = dpxX * xStepSize; + // float dy = dpxY * yStepSize; + // float x = clamp(cellTexCoords.x + dx, 0, 1.0f); + // float y = clamp(cellTexCoords.y, 0, 1.0f); + // // float y = clamp(cellTexCoords.y + dy, 0, 1.0f); + + // float distance = dpxX; + // // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); + // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); + // // weight = 1.0; + + // float2 sampleUV = float2(x, y); + + // float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); + // float4 color_sample = getColorMosaic(sampleUV, viewIndex); + + // if (color_sample.w < 1.0f) { + // // only collect colors from known values -> skip holes + // continue; + // } + + // if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back + // // if center is further at the back then skip sample or reduce weight + + // weight = 0.1f; + // } else { + // // reduce weight with distance to center: + // float diff = abs(depthAtCenter - depth_sample); + // weight = weight * (diff * diff); + // } + + // kernelSum += weight; + // colorSum += weight * color_sample; // } return float4(colorSum.xyz / kernelSum, 1.0f); // return the average color of the samples From 6e593ff79f4b9ae75bf32e8174e81f1f323a10ce Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 13 Aug 2025 16:23:20 +0200 Subject: [PATCH 035/149] refactor --- G3DCamera.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 34f8755..75f786b 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -254,6 +254,7 @@ private float scaledHalfCameraWidthAtStart private bool applyFXAA = false; private bool debugRendering = false; private int holeFillingRadius = 4; + private int holeFillingRadius = 8; private Material viewGenerationMaterial; // TODO Handle viewport resizing/ moving @@ -379,9 +380,6 @@ void Start() #if G3D_HDRP private void initCustomPass() { - bool debugRendering = true; - bool isFillingHoles = true; - int holeFillingRadius = 32; // init fullscreen postprocessing for hd render pipeline CustomPassVolume customPassVolume = gameObject.AddComponent(); From 23e5bcea902b0fdeadceba4088a79bcc5e2c2c57 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 14 Aug 2025 10:54:22 +0200 Subject: [PATCH 036/149] added parameters to inspector --- G3DCamera.cs | 10 ++++------ Resources/G3DCameraInspector.uxml | 4 ++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 75f786b..506cbdb 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -250,11 +250,10 @@ private float scaledHalfCameraWidthAtStart private bool generateViews = true; private bool useVectorMapViewGeneration = false; - private bool isFillingHoles = true; - private bool applyFXAA = false; - private bool debugRendering = false; - private int holeFillingRadius = 4; - private int holeFillingRadius = 8; + public bool isFillingHoles; + public bool applyFXAA; + public bool debugRendering; + public int holeFillingRadius; private Material viewGenerationMaterial; // TODO Handle viewport resizing/ moving @@ -380,7 +379,6 @@ void Start() #if G3D_HDRP private void initCustomPass() { - // init fullscreen postprocessing for hd render pipeline CustomPassVolume customPassVolume = gameObject.AddComponent(); customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; diff --git a/Resources/G3DCameraInspector.uxml b/Resources/G3DCameraInspector.uxml index 2131da6..2c6b61d 100644 --- a/Resources/G3DCameraInspector.uxml +++ b/Resources/G3DCameraInspector.uxml @@ -8,6 +8,10 @@ + + + + From 6f635cf94e60e5aaf85b84531bb8f91571374406 Mon Sep 17 00:00:00 2001 From: Jim Date: Thu, 14 Aug 2025 12:29:57 +0200 Subject: [PATCH 037/149] fix length computation --- Resources/G3DViewGenHoleFilling.compute | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index 5802334..d0261d2 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -262,17 +262,18 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) // Sample at current Fibonacci point { - float dpxX = samplePoint.x; - float dpxY = samplePoint.y; + // float dpxX = samplePoint.x; + // float dpxY = samplePoint.y; - float dx = dpxX * xStepSize; - float dy = dpxY * yStepSize; + float dx = samplePoint.x * xStepSize; + float dy = samplePoint.y * yStepSize; float x = clamp(cellTexCoords.x + dx, 0, 1.0f); float y = clamp(cellTexCoords.y + dy, 0, 1.0f); // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); - float distance = side; - float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); + // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); + float distanceSq = dot(samplePoint, samplePoint); + float weight = exp(-distanceSq / (2.0f * sigma * sigma)); // weight = 1.0; float2 sampleUV = float2(x, y); From 192c424be7db755ef39759dca27899c36dd887e7 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 14 Aug 2025 12:36:23 +0200 Subject: [PATCH 038/149] use point sampling for rendered images --- Resources/G3DViewGeneration.shader | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index 92e101b..da5abb1 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -241,13 +241,13 @@ Shader "G3D/ViewGeneration" // first and last image in the grid are the left and right camera uint gridCount = grid_size_x * grid_size_y; if (viewIndex == 0) { - return _leftCamTex.Sample(sampler_leftCamTex, cellTexCoords); // sample the left camera texture + return _leftCamTex.Sample(sampler_point_repeat, cellTexCoords); // sample the left camera texture } if (viewIndex == gridCount / 2) { - return _middleCamTex.Sample(sampler_middleCamTex, cellTexCoords); // sample the middle camera texture + return _middleCamTex.Sample(sampler_point_repeat, cellTexCoords); // sample the middle camera texture } if (viewIndex == gridCount - 1) { - return _rightCamTex.Sample(sampler_rightCamTex, cellTexCoords); // sample the right camera texture + return _rightCamTex.Sample(sampler_point_repeat, cellTexCoords); // sample the right camera texture } // ----------------------------------------------------------------------------------------------------------- @@ -270,15 +270,15 @@ Shader "G3D/ViewGeneration" float4 finalColor = float4(0.0f, 0.0f, 0.0f, 0.0f); float factor = 0.0f; - if (!discardFragmentLeft) { + if (!discardFragmentLeft && factor < 1.0f) { // second condition to ensure we only use one fragment here finalColor += _leftCamTex.Sample(sampler_point_repeat, shiftedLeftTexcoords); // sample the left camera texture factor += 1.0f; // increase the factor for the left camera } - if( !discardFragmentMiddle) { // if the final color is not set yet, sample the middle camera texture + if( !discardFragmentMiddle && factor < 1.0f) { // if the final color is not set yet, sample the middle camera texture finalColor += _middleCamTex.Sample(sampler_point_repeat, shiftedMiddleTexcoords); // sample the middle camera texture factor += 1.0f; // increase the factor for the middle camera } - if (!discardFragmentRight) { + if (!discardFragmentRight && factor < 1.0f) { finalColor += _rightCamTex.Sample(sampler_point_repeat, shiftedRightTexcoords); // sample the right camera texture factor += 1.0f; // increase the factor for the right camera } From 4f2ff6987a8b549330f263e4d5aa79d0468d7428 Mon Sep 17 00:00:00 2001 From: Jim Date: Thu, 14 Aug 2025 12:42:26 +0200 Subject: [PATCH 039/149] shift spiral, remove special case for (0, 0) --- Resources/G3DViewGenHoleFilling.compute | 35 ++----------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index d0261d2..ba762a7 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -221,40 +221,9 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) float kernelSum = 0.0f; float4 colorSum = float4(0.0f, 0.0f, 0.0f, 0.0f); - // Sample (0, 0) - { - float x = clamp(cellTexCoords.x, 0, 1.0f); - float y = clamp(cellTexCoords.y, 0, 1.0f); - - float weight = 1.0; - - float2 sampleUV = float2(x, y); - - float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); - float4 color_sample = getColorMosaic(sampleUV, viewIndex); - - if (color_sample.w < 1.0f) { - // only collect colors from known values -> skip holes - weight = 0.0; - } - - if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back - // if center is further at the back then skip sample or reduce weight - - weight = 0.1f; - } else { - // reduce weight with distance to center: - float diff = abs(depthAtCenter - depth_sample); - weight = weight * (diff * diff); - } - - kernelSum += weight; - colorSum += weight * color_sample; - } - float a = 1.0; float b = 1.0; - float2 samplePoint = float2(-1.0, 0.0); + float2 samplePoint = float2(0.0, 0.0); float side = 1.0; float2 stride = float2(1.0, 1.0); @@ -275,7 +244,7 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) float distanceSq = dot(samplePoint, samplePoint); float weight = exp(-distanceSq / (2.0f * sigma * sigma)); // weight = 1.0; - + float2 sampleUV = float2(x, y); float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); From f3c80cc9ebe4d7f221b71ecd2eab8ea38190c630 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 14 Aug 2025 12:44:20 +0200 Subject: [PATCH 040/149] update hole filling from ui every frame --- G3DCamera.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 506cbdb..58a1423 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -253,12 +253,18 @@ private float scaledHalfCameraWidthAtStart public bool isFillingHoles; public bool applyFXAA; public bool debugRendering; + + [Range(1, 64)] public int holeFillingRadius; private Material viewGenerationMaterial; +#if G3D_HDRP + private G3DHDRPViewGenerationPass viewGenerationPass; +#endif // TODO Handle viewport resizing/ moving #region Initialization + void Start() { mainCamera = GetComponent(); @@ -417,7 +423,7 @@ private void initCustomPass() } // add multiview generation pass - G3DHDRPViewGenerationPass viewGenerationPass = + viewGenerationPass = customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationPass)) as G3DHDRPViewGenerationPass; viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); @@ -951,6 +957,10 @@ void Update() oldRenderResolutionScale = renderResolutionScale; updateShaderRenderTextures(); } + +#if G3D_HDRP + viewGenerationPass.holeFillingRadius = holeFillingRadius; +#endif } private void updateScreenViewportProperties() From 97252d79d5fb949a49bfc0a5d7f81919ce9a6ac7 Mon Sep 17 00:00:00 2001 From: Jim Date: Thu, 14 Aug 2025 12:49:23 +0200 Subject: [PATCH 041/149] sample immediate neighbours --- Resources/G3DViewGenHoleFilling.compute | 156 ++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index ba762a7..fb627ff 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -221,6 +221,162 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) float kernelSum = 0.0f; float4 colorSum = float4(0.0f, 0.0f, 0.0f, 0.0f); + // Sample at (1, 0) + { + float2 samplePoint = float2(1.0, 0.0); + + float dx = samplePoint.x * xStepSize; + float dy = samplePoint.y * yStepSize; + float x = clamp(cellTexCoords.x + dx, 0, 1.0f); + float y = clamp(cellTexCoords.y + dy, 0, 1.0f); + + // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); + // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); + float distanceSq = dot(samplePoint, samplePoint); + float weight = exp(-distanceSq / (2.0f * sigma * sigma)); + // weight = 1.0; + + float2 sampleUV = float2(x, y); + + float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); + float4 color_sample = getColorMosaic(sampleUV, viewIndex); + + if (color_sample.w < 1.0f) { + // only collect colors from known values -> skip holes + weight = 0.1f; + } + + if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back + // if center is further at the back then skip sample or reduce weight + + weight = 0.1f; + } else { + // reduce weight with distance to center: + float diff = abs(depthAtCenter - depth_sample); + weight = weight * (diff * diff); + } + + kernelSum += weight; + colorSum += weight * color_sample; + } + + // Sample at (-1, 0) + { + float2 samplePoint = float2(-1.0, 0.0); + + float dx = samplePoint.x * xStepSize; + float dy = samplePoint.y * yStepSize; + float x = clamp(cellTexCoords.x + dx, 0, 1.0f); + float y = clamp(cellTexCoords.y + dy, 0, 1.0f); + + // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); + // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); + float distanceSq = dot(samplePoint, samplePoint); + float weight = exp(-distanceSq / (2.0f * sigma * sigma)); + // weight = 1.0; + + float2 sampleUV = float2(x, y); + + float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); + float4 color_sample = getColorMosaic(sampleUV, viewIndex); + + if (color_sample.w < 1.0f) { + // only collect colors from known values -> skip holes + weight = 0.1f; + } + + if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back + // if center is further at the back then skip sample or reduce weight + + weight = 0.1f; + } else { + // reduce weight with distance to center: + float diff = abs(depthAtCenter - depth_sample); + weight = weight * (diff * diff); + } + + kernelSum += weight; + colorSum += weight * color_sample; + } + + // Sample at (0, 1) + { + float2 samplePoint = float2(0.0, 1.0); + + float dx = samplePoint.x * xStepSize; + float dy = samplePoint.y * yStepSize; + float x = clamp(cellTexCoords.x + dx, 0, 1.0f); + float y = clamp(cellTexCoords.y + dy, 0, 1.0f); + + // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); + // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); + float distanceSq = dot(samplePoint, samplePoint); + float weight = exp(-distanceSq / (2.0f * sigma * sigma)); + // weight = 1.0; + + float2 sampleUV = float2(x, y); + + float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); + float4 color_sample = getColorMosaic(sampleUV, viewIndex); + + if (color_sample.w < 1.0f) { + // only collect colors from known values -> skip holes + weight = 0.1f; + } + + if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back + // if center is further at the back then skip sample or reduce weight + + weight = 0.1f; + } else { + // reduce weight with distance to center: + float diff = abs(depthAtCenter - depth_sample); + weight = weight * (diff * diff); + } + + kernelSum += weight; + colorSum += weight * color_sample; + } + + // Sample at (0, -1) + { + float2 samplePoint = float2(0.0, -1.0); + + float dx = samplePoint.x * xStepSize; + float dy = samplePoint.y * yStepSize; + float x = clamp(cellTexCoords.x + dx, 0, 1.0f); + float y = clamp(cellTexCoords.y + dy, 0, 1.0f); + + // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); + // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); + float distanceSq = dot(samplePoint, samplePoint); + float weight = exp(-distanceSq / (2.0f * sigma * sigma)); + // weight = 1.0; + + float2 sampleUV = float2(x, y); + + float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); + float4 color_sample = getColorMosaic(sampleUV, viewIndex); + + if (color_sample.w < 1.0f) { + // only collect colors from known values -> skip holes + weight = 0.1f; + } + + if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back + // if center is further at the back then skip sample or reduce weight + + weight = 0.1f; + } else { + // reduce weight with distance to center: + float diff = abs(depthAtCenter - depth_sample); + weight = weight * (diff * diff); + } + + kernelSum += weight; + colorSum += weight * color_sample; + } + float a = 1.0; float b = 1.0; float2 samplePoint = float2(0.0, 0.0); From 0e24c1350be0a867f93dae1ee2ecac4bd0a8ba15 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 17 Aug 2025 17:22:10 +0200 Subject: [PATCH 042/149] extract sampleColorAtPoint shader function --- Resources/G3DViewGenHoleFilling.compute | 262 +++++------------------- 1 file changed, 52 insertions(+), 210 deletions(-) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index fb627ff..c540f4a 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -196,6 +196,48 @@ float4 getColorMosaic(float2 uv, int viewIndex) { return _colorMosaic.SampleLevel(sampler_colorMosaic, fragmentUV, 0); } +void sampleColorAtPoint(float2 samplePoint, float2 cellTexCoords, float xStepSize, float yStepSize, uint viewIndex, float depthAtCenter, inout float kernelSum, inout float4 colorSum) { + // Sample at current Fibonacci point + // float dpxX = samplePoint.x; + // float dpxY = samplePoint.y; + + float dx = samplePoint.x * xStepSize; + float dy = samplePoint.y * yStepSize; + float x = clamp(cellTexCoords.x + dx, 0, 1.0f); + float y = clamp(cellTexCoords.y + dy, 0, 1.0f); + + // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); + // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); + float distanceSq = dot(samplePoint, samplePoint); + // float weight = exp(-distanceSq / (2.0f * sigma * sigma)); + float2 sampleUV = float2(x, y); + + float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); + float4 color_sample = getColorMosaic(sampleUV, viewIndex); + + if (color_sample.w < 1.0f) { + // only collect colors from known values -> skip holes + // weight = 0.0f; + return; + } + + float weight = 0.0f; // default weight is 0.0 + if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back + // if center is further at the back then skip sample or reduce weight + + weight = 0.0f; + } else { + // reduce weight with distance to center: + float diff = abs(depthAtCenter - depth_sample); + // weight = weight * (diff * diff); + weight = 1.0f; + weight /= distanceSq + 0.01; + } + + kernelSum += weight; + colorSum += weight * color_sample; +} + float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) { float2 cellCoordinates = getCellCoordinates(uv, grid_size_x, grid_size_y); @@ -224,157 +266,25 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) // Sample at (1, 0) { float2 samplePoint = float2(1.0, 0.0); - - float dx = samplePoint.x * xStepSize; - float dy = samplePoint.y * yStepSize; - float x = clamp(cellTexCoords.x + dx, 0, 1.0f); - float y = clamp(cellTexCoords.y + dy, 0, 1.0f); - - // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); - // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); - float distanceSq = dot(samplePoint, samplePoint); - float weight = exp(-distanceSq / (2.0f * sigma * sigma)); - // weight = 1.0; - - float2 sampleUV = float2(x, y); - - float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); - float4 color_sample = getColorMosaic(sampleUV, viewIndex); - - if (color_sample.w < 1.0f) { - // only collect colors from known values -> skip holes - weight = 0.1f; - } - - if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back - // if center is further at the back then skip sample or reduce weight - - weight = 0.1f; - } else { - // reduce weight with distance to center: - float diff = abs(depthAtCenter - depth_sample); - weight = weight * (diff * diff); - } - - kernelSum += weight; - colorSum += weight * color_sample; + sampleColorAtPoint(samplePoint, cellTexCoords, xStepSize, yStepSize, viewIndex, depthAtCenter, kernelSum, colorSum); } // Sample at (-1, 0) { float2 samplePoint = float2(-1.0, 0.0); - - float dx = samplePoint.x * xStepSize; - float dy = samplePoint.y * yStepSize; - float x = clamp(cellTexCoords.x + dx, 0, 1.0f); - float y = clamp(cellTexCoords.y + dy, 0, 1.0f); - - // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); - // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); - float distanceSq = dot(samplePoint, samplePoint); - float weight = exp(-distanceSq / (2.0f * sigma * sigma)); - // weight = 1.0; - - float2 sampleUV = float2(x, y); - - float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); - float4 color_sample = getColorMosaic(sampleUV, viewIndex); - - if (color_sample.w < 1.0f) { - // only collect colors from known values -> skip holes - weight = 0.1f; - } - - if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back - // if center is further at the back then skip sample or reduce weight - - weight = 0.1f; - } else { - // reduce weight with distance to center: - float diff = abs(depthAtCenter - depth_sample); - weight = weight * (diff * diff); - } - - kernelSum += weight; - colorSum += weight * color_sample; + sampleColorAtPoint(samplePoint, cellTexCoords, xStepSize, yStepSize, viewIndex, depthAtCenter, kernelSum, colorSum); } // Sample at (0, 1) { float2 samplePoint = float2(0.0, 1.0); - - float dx = samplePoint.x * xStepSize; - float dy = samplePoint.y * yStepSize; - float x = clamp(cellTexCoords.x + dx, 0, 1.0f); - float y = clamp(cellTexCoords.y + dy, 0, 1.0f); - - // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); - // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); - float distanceSq = dot(samplePoint, samplePoint); - float weight = exp(-distanceSq / (2.0f * sigma * sigma)); - // weight = 1.0; - - float2 sampleUV = float2(x, y); - - float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); - float4 color_sample = getColorMosaic(sampleUV, viewIndex); - - if (color_sample.w < 1.0f) { - // only collect colors from known values -> skip holes - weight = 0.1f; - } - - if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back - // if center is further at the back then skip sample or reduce weight - - weight = 0.1f; - } else { - // reduce weight with distance to center: - float diff = abs(depthAtCenter - depth_sample); - weight = weight * (diff * diff); - } - - kernelSum += weight; - colorSum += weight * color_sample; + sampleColorAtPoint(samplePoint, cellTexCoords, xStepSize, yStepSize, viewIndex, depthAtCenter, kernelSum, colorSum); } // Sample at (0, -1) { float2 samplePoint = float2(0.0, -1.0); - - float dx = samplePoint.x * xStepSize; - float dy = samplePoint.y * yStepSize; - float x = clamp(cellTexCoords.x + dx, 0, 1.0f); - float y = clamp(cellTexCoords.y + dy, 0, 1.0f); - - // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); - // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); - float distanceSq = dot(samplePoint, samplePoint); - float weight = exp(-distanceSq / (2.0f * sigma * sigma)); - // weight = 1.0; - - float2 sampleUV = float2(x, y); - - float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); - float4 color_sample = getColorMosaic(sampleUV, viewIndex); - - if (color_sample.w < 1.0f) { - // only collect colors from known values -> skip holes - weight = 0.1f; - } - - if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back - // if center is further at the back then skip sample or reduce weight - - weight = 0.1f; - } else { - // reduce weight with distance to center: - float diff = abs(depthAtCenter - depth_sample); - weight = weight * (diff * diff); - } - - kernelSum += weight; - colorSum += weight * color_sample; + sampleColorAtPoint(samplePoint, cellTexCoords, xStepSize, yStepSize, viewIndex, depthAtCenter, kernelSum, colorSum); } float a = 1.0; @@ -386,45 +296,9 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) for (uint i = 0; i < (uint) radius; i++) { // Sample at current Fibonacci point - { - // float dpxX = samplePoint.x; - // float dpxY = samplePoint.y; - - float dx = samplePoint.x * xStepSize; - float dy = samplePoint.y * yStepSize; - float x = clamp(cellTexCoords.x + dx, 0, 1.0f); - float y = clamp(cellTexCoords.y + dy, 0, 1.0f); - - // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); - // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); - float distanceSq = dot(samplePoint, samplePoint); - float weight = exp(-distanceSq / (2.0f * sigma * sigma)); - // weight = 1.0; - - float2 sampleUV = float2(x, y); - - float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); - float4 color_sample = getColorMosaic(sampleUV, viewIndex); - - if (color_sample.w < 1.0f) { - // only collect colors from known values -> skip holes - weight = 0.1f; - } - - if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back - // if center is further at the back then skip sample or reduce weight - - weight = 0.1f; - } else { - // reduce weight with distance to center: - float diff = abs(depthAtCenter - depth_sample); - weight = weight * (diff * diff); - } - - kernelSum += weight; - colorSum += weight * color_sample; - } + sampleColorAtPoint(samplePoint * 0.5f, cellTexCoords, xStepSize, yStepSize, viewIndex, depthAtCenter, kernelSum, colorSum); + // calculate new Fibonacci point if (i > 1) { float next = a + b; a = b; @@ -443,42 +317,10 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) samplePoint += square; } - - // for (int dpxX = 1; dpxX <= radius; ++dpxX) { - // float dx = dpxX * xStepSize; - // float dy = dpxY * yStepSize; - // float x = clamp(cellTexCoords.x + dx, 0, 1.0f); - // float y = clamp(cellTexCoords.y, 0, 1.0f); - // // float y = clamp(cellTexCoords.y + dy, 0, 1.0f); - - // float distance = dpxX; - // // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); - // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); - // // weight = 1.0; - - // float2 sampleUV = float2(x, y); - - // float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); - // float4 color_sample = getColorMosaic(sampleUV, viewIndex); - - // if (color_sample.w < 1.0f) { - // // only collect colors from known values -> skip holes - // continue; - // } - - // if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back - // // if center is further at the back then skip sample or reduce weight - - // weight = 0.1f; - // } else { - // // reduce weight with distance to center: - // float diff = abs(depthAtCenter - depth_sample); - // weight = weight * (diff * diff); - // } - - // kernelSum += weight; - // colorSum += weight * color_sample; - // } + + if (kernelSum <= 0.01f) { + return float4(1.0f, 0.5f, 0.1f, 1.0f); // return orange if no samples were collected + } return float4(colorSum.xyz / kernelSum, 1.0f); // return the average color of the samples } From a6508e5637713f0ffebe43d0b59419ed9e08d29e Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 17 Aug 2025 18:03:37 +0200 Subject: [PATCH 043/149] shorten and vectorize shader code --- Resources/G3DHLSLCommonFunctions.hlsl | 26 +++++++-------- Resources/G3DViewGenHoleFilling.compute | 44 +++++++++++-------------- Resources/G3DViewGeneration.shader | 4 +-- 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/Resources/G3DHLSLCommonFunctions.hlsl b/Resources/G3DHLSLCommonFunctions.hlsl index a398d9b..8c34a39 100644 --- a/Resources/G3DHLSLCommonFunctions.hlsl +++ b/Resources/G3DHLSLCommonFunctions.hlsl @@ -1,8 +1,5 @@ float2 calculateUVForMosaic(int viewIndex, float2 fullScreenUV, int mosaic_rows = 4, int mosaic_columns = 4) { - if(viewIndex < 0 ) - { - viewIndex = 0; - } + viewIndex = max(0, viewIndex); int xAxis = viewIndex % mosaic_columns; int yAxis = viewIndex / mosaic_columns; // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) @@ -28,17 +25,16 @@ float2 calculateUVForMosaic(int viewIndex, float2 fullScreenUV, int mosaic_rows // -> e.g. 2.4 turns to 2 // step 3: subtract the integer part from the transformed tex coords to get the texel coords in the grid cell // -> e.g. 2.4 - 2 = 0.4 -> final texel coords in the grid cell are 0.4, 0.5 -float2 getCellCoordinates(float2 uv, int gridSizeX, int gridSizeY) { - float2 cellCoordinates = float2(uv.x, 1.0 - uv.y); // flip y coordiate to have cell index 0 in upper left corner - cellCoordinates = float2(cellCoordinates.x * gridSizeX, cellCoordinates.y * gridSizeY); - return cellCoordinates; +float2 getCellCoordinates(float2 uv, int2 gridSize) { + // flip y coordiate to have cell index 0 in upper left corner + return float2(uv.x, 1.0 - uv.y) * float2(gridSize); } -uint getViewIndex(float2 cellCoordinates, int gridSizeX, int gridSizeY) { - uint viewIndex = uint(cellCoordinates.x) + gridSizeX * uint(cellCoordinates.y); - return viewIndex; + +uint getViewIndex(float2 cellCoordinates, int2 gridSize) { + return uint(cellCoordinates.x) + gridSize.x * uint(cellCoordinates.y); } + float2 getCellTexCoords(float2 cellCoordinates) { - float2 cellTexCoords = float2(cellCoordinates.x - float(int(cellCoordinates.x)), cellCoordinates.y - float(int(cellCoordinates.y))); - cellTexCoords.y = 1.0 - cellTexCoords.y; // flip y coordinate to match original tex coords - return cellTexCoords; -} \ No newline at end of file + float2 uv = frac(cellCoordinates); + return float2(uv.x, 1.0 - uv.y); +} diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index c540f4a..238e4d1 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -181,8 +181,7 @@ float2 calculateProjectedFragmentPosition(float2 uv, int viewIndex, float4x4 vie return float2(NDC.xy); // return the difference between the shifted and original x coordinate } -float Linear01DepthViewBased(float2 uv, int viewIndex) -{ +float Linear01DepthViewBased(float2 uv, int viewIndex) { float logDepth = getCameraLogDepth(uv, viewIndex); // Sample the depth from the Camera depth texture. float deviceDepth = logDepth; @@ -193,16 +192,15 @@ float Linear01DepthViewBased(float2 uv, int viewIndex) float4 getColorMosaic(float2 uv, int viewIndex) { float2 fragmentUV = calculateUVForMosaic(viewIndex, uv, grid_size_y, grid_size_x); - return _colorMosaic.SampleLevel(sampler_colorMosaic, fragmentUV, 0); -} + // return _colorMosaic.Sample(sampler_colorMosaic, fragmentUV); + // return float4(1.0, 0.0, 0.0, 1.0); -void sampleColorAtPoint(float2 samplePoint, float2 cellTexCoords, float xStepSize, float yStepSize, uint viewIndex, float depthAtCenter, inout float kernelSum, inout float4 colorSum) { - // Sample at current Fibonacci point - // float dpxX = samplePoint.x; - // float dpxY = samplePoint.y; + return _colorMosaic.Load(int3(imageWidth * fragmentUV.x, imageHeight * fragmentUV.y, 0)); +} - float dx = samplePoint.x * xStepSize; - float dy = samplePoint.y * yStepSize; +void sampleColorAtPoint(float2 samplePoint, float2 cellTexCoords, uint viewIndex, float depthAtCenter, inout float kernelSum, inout float4 colorSum) { + float dx = samplePoint.x / imageWidth; + float dy = samplePoint.y / imageHeight; float x = clamp(cellTexCoords.x + dx, 0, 1.0f); float y = clamp(cellTexCoords.y + dy, 0, 1.0f); @@ -238,16 +236,15 @@ void sampleColorAtPoint(float2 samplePoint, float2 cellTexCoords, float xStepSiz colorSum += weight * color_sample; } -float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) -{ - float2 cellCoordinates = getCellCoordinates(uv, grid_size_x, grid_size_y); - uint viewIndex = getViewIndex(cellCoordinates, grid_size_x, grid_size_y); +float4 fragHDRP(float2 uv, int2 coord) { + float2 cellCoordinates = getCellCoordinates(float2(coord) / float2(imageWidth, imageHeight), int2(grid_size_x, grid_size_y)); + uint viewIndex = getViewIndex(cellCoordinates, int2(grid_size_x, grid_size_y)); float2 cellTexCoords = getCellTexCoords(cellCoordinates); // first and last image in the grid are the left and right camera uint gridCount = grid_size_x * grid_size_y; if (viewIndex == 0 || viewIndex == gridCount - 1) { - return _colorMosaic.SampleLevel(sampler_colorMosaic, uv, 0); + return _colorMosaic.Load(int3(coord, 0)); } float px_radius_left = 0; @@ -266,25 +263,25 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) // Sample at (1, 0) { float2 samplePoint = float2(1.0, 0.0); - sampleColorAtPoint(samplePoint, cellTexCoords, xStepSize, yStepSize, viewIndex, depthAtCenter, kernelSum, colorSum); + sampleColorAtPoint(samplePoint, cellTexCoords, viewIndex, depthAtCenter, kernelSum, colorSum); } // Sample at (-1, 0) { float2 samplePoint = float2(-1.0, 0.0); - sampleColorAtPoint(samplePoint, cellTexCoords, xStepSize, yStepSize, viewIndex, depthAtCenter, kernelSum, colorSum); + sampleColorAtPoint(samplePoint, cellTexCoords, viewIndex, depthAtCenter, kernelSum, colorSum); } // Sample at (0, 1) { float2 samplePoint = float2(0.0, 1.0); - sampleColorAtPoint(samplePoint, cellTexCoords, xStepSize, yStepSize, viewIndex, depthAtCenter, kernelSum, colorSum); + sampleColorAtPoint(samplePoint, cellTexCoords, viewIndex, depthAtCenter, kernelSum, colorSum); } // Sample at (0, -1) { float2 samplePoint = float2(0.0, -1.0); - sampleColorAtPoint(samplePoint, cellTexCoords, xStepSize, yStepSize, viewIndex, depthAtCenter, kernelSum, colorSum); + sampleColorAtPoint(samplePoint, cellTexCoords, viewIndex, depthAtCenter, kernelSum, colorSum); } float a = 1.0; @@ -296,7 +293,7 @@ float4 fragHDRP (float2 uv, float xStepSize, float yStepSize) for (uint i = 0; i < (uint) radius; i++) { // Sample at current Fibonacci point - sampleColorAtPoint(samplePoint * 0.5f, cellTexCoords, xStepSize, yStepSize, viewIndex, depthAtCenter, kernelSum, colorSum); + sampleColorAtPoint(samplePoint * 0.5f, cellTexCoords, viewIndex, depthAtCenter, kernelSum, colorSum); // calculate new Fibonacci point if (i > 1) { @@ -334,9 +331,6 @@ void kernelFunction (uint3 id : SV_DispatchThreadID) uint xCohortSize = floor((imageWidth + numThreadsX) / numThreadsX); uint yCohortSize = floor((imageHeight + numThreadsY) / numThreadsY); - float xStepSize = 1.0f / (float)imageWidth; - float yStepSize = 1.0f / (float)imageHeight; - for(uint i = 0; i < xCohortSize; i++) { for(uint j = 0; j < yCohortSize; j++) { uint x = id.x * xCohortSize + i; @@ -346,8 +340,8 @@ void kernelFunction (uint3 id : SV_DispatchThreadID) continue; // skip if out of bounds } - float2 uv = float2(x * xStepSize, y * yStepSize); - float4 color = fragHDRP(uv, xStepSize, yStepSize); + float2 uv = float2(x, y) / float2(imageWidth, imageHeight); + float4 color = fragHDRP(uv, int2(x, y)); Result[float2(x, y)] = color; } diff --git a/Resources/G3DViewGeneration.shader b/Resources/G3DViewGeneration.shader index da5abb1..5f79afa 100644 --- a/Resources/G3DViewGeneration.shader +++ b/Resources/G3DViewGeneration.shader @@ -231,8 +231,8 @@ Shader "G3D/ViewGeneration" /// float4 fragHDRP (v2f i) : SV_Target { - float2 cellCoordinates = getCellCoordinates(i.uv, grid_size_x, grid_size_y); - uint viewIndex = getViewIndex(cellCoordinates, grid_size_x, grid_size_y); + float2 cellCoordinates = getCellCoordinates(i.uv, int2(grid_size_x, grid_size_y)); + uint viewIndex = getViewIndex(cellCoordinates, int2(grid_size_x, grid_size_y)); float2 cellTexCoords = getCellTexCoords(cellCoordinates); // float logDepth = getCameraLogDepth(cellTexCoords, viewIndex); From c6aa78c242999c28f1b87513a16602fa5102ccae Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 17 Aug 2025 18:14:22 +0200 Subject: [PATCH 044/149] vectorize grid size --- Resources/G3DViewGenHoleFilling.compute | 11 +++++------ Scripts/G3DHDRPViewGenerationPass.cs | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index 238e4d1..af86978 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -50,8 +50,7 @@ float4x4 viewMatrix15; -int grid_size_x; -int grid_size_y; +int2 gridSize; int radius; float sigma; @@ -191,7 +190,7 @@ float Linear01DepthViewBased(float2 uv, int viewIndex) { } float4 getColorMosaic(float2 uv, int viewIndex) { - float2 fragmentUV = calculateUVForMosaic(viewIndex, uv, grid_size_y, grid_size_x); + float2 fragmentUV = calculateUVForMosaic(viewIndex, uv, gridSize.y, gridSize.x); // return _colorMosaic.Sample(sampler_colorMosaic, fragmentUV); // return float4(1.0, 0.0, 0.0, 1.0); @@ -237,12 +236,12 @@ void sampleColorAtPoint(float2 samplePoint, float2 cellTexCoords, uint viewIndex } float4 fragHDRP(float2 uv, int2 coord) { - float2 cellCoordinates = getCellCoordinates(float2(coord) / float2(imageWidth, imageHeight), int2(grid_size_x, grid_size_y)); - uint viewIndex = getViewIndex(cellCoordinates, int2(grid_size_x, grid_size_y)); + float2 cellCoordinates = getCellCoordinates(float2(coord) / float2(imageWidth, imageHeight), gridSize); + uint viewIndex = getViewIndex(cellCoordinates, gridSize); float2 cellTexCoords = getCellTexCoords(cellCoordinates); // first and last image in the grid are the left and right camera - uint gridCount = grid_size_x * grid_size_y; + uint gridCount = gridSize.x * gridSize.y; if (viewIndex == 0 || viewIndex == gridCount - 1) { return _colorMosaic.Load(int3(coord, 0)); } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 2dff145..1bd3b41 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -164,8 +164,7 @@ private void runHoleFilling(CustomPassContext ctx) "_colorMosaic", mosaicImageHandle ); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_x", 4); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "grid_size_y", 4); + ctx.cmd.SetComputeIntParams(holeFillingCompShader, "gridSize", new int[] {4, 4}); ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); ctx.cmd.SetComputeIntParam(holeFillingCompShader, "imageWidth", mosaicImageHandle.rt.width); From 9aee00ca74b7ea4d8292ce8fa2937cf6764668b9 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 17 Aug 2025 18:27:59 +0200 Subject: [PATCH 045/149] vectorize image size --- Resources/G3DViewGenHoleFilling.compute | 19 +++++++++---------- Scripts/G3DHDRPViewGenerationPass.cs | 7 +------ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index af86978..e26dad0 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -54,8 +54,7 @@ int2 gridSize; int radius; float sigma; -uint imageWidth; -uint imageHeight; +int2 imageSize; Texture2D _depthMap0; Texture2D _depthMap1; @@ -194,12 +193,12 @@ float4 getColorMosaic(float2 uv, int viewIndex) { // return _colorMosaic.Sample(sampler_colorMosaic, fragmentUV); // return float4(1.0, 0.0, 0.0, 1.0); - return _colorMosaic.Load(int3(imageWidth * fragmentUV.x, imageHeight * fragmentUV.y, 0)); + return _colorMosaic.Load(int3(float2(imageSize) * fragmentUV, 0)); } void sampleColorAtPoint(float2 samplePoint, float2 cellTexCoords, uint viewIndex, float depthAtCenter, inout float kernelSum, inout float4 colorSum) { - float dx = samplePoint.x / imageWidth; - float dy = samplePoint.y / imageHeight; + float dx = samplePoint.x / imageSize.x; + float dy = samplePoint.y / imageSize.y; float x = clamp(cellTexCoords.x + dx, 0, 1.0f); float y = clamp(cellTexCoords.y + dy, 0, 1.0f); @@ -236,7 +235,7 @@ void sampleColorAtPoint(float2 samplePoint, float2 cellTexCoords, uint viewIndex } float4 fragHDRP(float2 uv, int2 coord) { - float2 cellCoordinates = getCellCoordinates(float2(coord) / float2(imageWidth, imageHeight), gridSize); + float2 cellCoordinates = getCellCoordinates(float2(coord) / imageSize, gridSize); uint viewIndex = getViewIndex(cellCoordinates, gridSize); float2 cellTexCoords = getCellTexCoords(cellCoordinates); @@ -327,19 +326,19 @@ void kernelFunction (uint3 id : SV_DispatchThreadID) uint numThreadsX = 32; uint numThreadsY = 32; - uint xCohortSize = floor((imageWidth + numThreadsX) / numThreadsX); - uint yCohortSize = floor((imageHeight + numThreadsY) / numThreadsY); + uint xCohortSize = floor((imageSize.x + numThreadsX) / numThreadsX); + uint yCohortSize = floor((imageSize.y + numThreadsY) / numThreadsY); for(uint i = 0; i < xCohortSize; i++) { for(uint j = 0; j < yCohortSize; j++) { uint x = id.x * xCohortSize + i; uint y = id.y * yCohortSize + j; - if (x >= imageWidth || y >= imageHeight) { + if (x >= imageSize.x || y >= imageSize.y) { continue; // skip if out of bounds } - float2 uv = float2(x, y) / float2(imageWidth, imageHeight); + float2 uv = float2(x, y) / imageSize; float4 color = fragHDRP(uv, int2(x, y)); Result[float2(x, y)] = color; diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 1bd3b41..fb411ce 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -167,12 +167,7 @@ private void runHoleFilling(CustomPassContext ctx) ctx.cmd.SetComputeIntParams(holeFillingCompShader, "gridSize", new int[] {4, 4}); ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "imageWidth", mosaicImageHandle.rt.width); - ctx.cmd.SetComputeIntParam( - holeFillingCompShader, - "imageHeight", - mosaicImageHandle.rt.height - ); + ctx.cmd.SetComputeIntParams(holeFillingCompShader, "imageSize", new int[] {mosaicImageHandle.rt.width, mosaicImageHandle.rt.height}); ctx.cmd.DispatchCompute(holeFillingCompShader, holeFillingKernel, 32, 32, 1); From 196bd508b7b85c7700d4a6f48e242b782f8b166d Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 17 Aug 2025 18:34:19 +0200 Subject: [PATCH 046/149] match thread group count to image size --- Resources/G3DViewGenHoleFilling.compute | 27 +++++-------------------- Scripts/G3DHDRPViewGenerationPass.cs | 2 +- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index e26dad0..aab0216 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -320,28 +320,11 @@ float4 fragHDRP(float2 uv, int2 coord) { return float4(colorSum.xyz / kernelSum, 1.0f); // return the average color of the samples } -[numthreads(32,32,1)] +[numthreads(1, 1, 1)] void kernelFunction (uint3 id : SV_DispatchThreadID) { - uint numThreadsX = 32; - uint numThreadsY = 32; - - uint xCohortSize = floor((imageSize.x + numThreadsX) / numThreadsX); - uint yCohortSize = floor((imageSize.y + numThreadsY) / numThreadsY); - - for(uint i = 0; i < xCohortSize; i++) { - for(uint j = 0; j < yCohortSize; j++) { - uint x = id.x * xCohortSize + i; - uint y = id.y * yCohortSize + j; - - if (x >= imageSize.x || y >= imageSize.y) { - continue; // skip if out of bounds - } - - float2 uv = float2(x, y) / imageSize; - float4 color = fragHDRP(uv, int2(x, y)); - - Result[float2(x, y)] = color; - } - } + float2 coord = float2(id.x, id.y); + float2 uv = coord / imageSize; + float4 color = fragHDRP(uv, coord); + Result[coord] = color; } \ No newline at end of file diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index fb411ce..2d0ea67 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -169,7 +169,7 @@ private void runHoleFilling(CustomPassContext ctx) ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); ctx.cmd.SetComputeIntParams(holeFillingCompShader, "imageSize", new int[] {mosaicImageHandle.rt.width, mosaicImageHandle.rt.height}); - ctx.cmd.DispatchCompute(holeFillingCompShader, holeFillingKernel, 32, 32, 1); + ctx.cmd.DispatchCompute(holeFillingCompShader, holeFillingKernel, mosaicImageHandle.rt.width, mosaicImageHandle.rt.height, 1); // Blit the result to the mosaic image CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); From 4e3297b12fb24ca398e63bb4a67307ac8496642d Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 17 Aug 2025 19:10:44 +0200 Subject: [PATCH 047/149] clean up shader --- Resources/G3DViewGenHoleFilling.compute | 113 ++++++++---------------- Scripts/G3DHDRPViewGenerationPass.cs | 2 +- 2 files changed, 40 insertions(+), 75 deletions(-) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index aab0216..d15f2f1 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -2,7 +2,7 @@ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" #include "G3DHLSLCommonFunctions.hlsl" -#pragma kernel kernelFunction +#pragma kernel main Texture2D _depthMosaic; SamplerState sampler_depthMosaic; @@ -190,97 +190,63 @@ float Linear01DepthViewBased(float2 uv, int viewIndex) { float4 getColorMosaic(float2 uv, int viewIndex) { float2 fragmentUV = calculateUVForMosaic(viewIndex, uv, gridSize.y, gridSize.x); - // return _colorMosaic.Sample(sampler_colorMosaic, fragmentUV); - // return float4(1.0, 0.0, 0.0, 1.0); - - return _colorMosaic.Load(int3(float2(imageSize) * fragmentUV, 0)); + return _colorMosaic[float2(imageSize) * fragmentUV]; } -void sampleColorAtPoint(float2 samplePoint, float2 cellTexCoords, uint viewIndex, float depthAtCenter, inout float kernelSum, inout float4 colorSum) { - float dx = samplePoint.x / imageSize.x; - float dy = samplePoint.y / imageSize.y; - float x = clamp(cellTexCoords.x + dx, 0, 1.0f); - float y = clamp(cellTexCoords.y + dy, 0, 1.0f); +void sampleColorAtPoint(float2 fragUV, float2 delta, uint viewIndex, float fragDepth, inout float kernelSum, inout float3 colorSum) { + float2 uv = clamp(fragUV + delta / imageSize, 0, 1); - // float distance = sqrt(dpxX * dpxX + dpxY * dpxY); - // float weight = exp(-(distance * distance) / (2.0f * sigma * sigma)); - float distanceSq = dot(samplePoint, samplePoint); + float distanceSq = dot(delta, delta); // float weight = exp(-distanceSq / (2.0f * sigma * sigma)); - float2 sampleUV = float2(x, y); - - float depth_sample = Linear01DepthViewBased(sampleUV, viewIndex); - float4 color_sample = getColorMosaic(sampleUV, viewIndex); - if (color_sample.w < 1.0f) { - // only collect colors from known values -> skip holes - // weight = 0.0f; + float4 sampleColor = getColorMosaic(uv, viewIndex); + if (sampleColor.w < 1.0) { + // Discard samples which lie in wholes. return; } - float weight = 0.0f; // default weight is 0.0 - if (depth_sample < depthAtCenter) { // 0.0=front | 1.0=back - // if center is further at the back then skip sample or reduce weight - - weight = 0.0f; - } else { - // reduce weight with distance to center: - float diff = abs(depthAtCenter - depth_sample); - // weight = weight * (diff * diff); - weight = 1.0f; - weight /= distanceSq + 0.01; + // 0.0 -> near plane + // 1.0 -> far plane + float sampleDepth = Linear01DepthViewBased(uv, viewIndex); + if (sampleDepth < fragDepth) { + // If the sampled point is nearer than the original fragment, discard it. + return; } + float diff = abs(fragDepth - sampleDepth); + float weight = 1.0; + // weight = weight * (diff * diff); + weight /= distanceSq + 0.01; + kernelSum += weight; - colorSum += weight * color_sample; + colorSum += weight * sampleColor.rgb; } -float4 fragHDRP(float2 uv, int2 coord) { +float4 frag(int2 coord) { float2 cellCoordinates = getCellCoordinates(float2(coord) / imageSize, gridSize); uint viewIndex = getViewIndex(cellCoordinates, gridSize); - float2 cellTexCoords = getCellTexCoords(cellCoordinates); + float2 fragUV = getCellTexCoords(cellCoordinates); // first and last image in the grid are the left and right camera uint gridCount = gridSize.x * gridSize.y; if (viewIndex == 0 || viewIndex == gridCount - 1) { - return _colorMosaic.Load(int3(coord, 0)); + return _colorMosaic[coord]; } - float px_radius_left = 0; - float px_radius_right = 0; - - float depthAtCenter = Linear01DepthViewBased(cellTexCoords, viewIndex); - float4 cellColor = getColorMosaic(cellTexCoords, viewIndex); // sample the color of the left camera texture + float fragDepth = Linear01DepthViewBased(fragUV, viewIndex); + float4 cellColor = getColorMosaic(fragUV, viewIndex); // sample the color of the left camera texture if( cellColor.w >= 1.0f) { // if the cell color is valid, return the original color return cellColor; } - float kernelSum = 0.0f; - float4 colorSum = float4(0.0f, 0.0f, 0.0f, 0.0f); + float kernelSum = 0.0; + float3 colorSum = float3(0.0, 0.0, 0.0); - // Sample at (1, 0) - { - float2 samplePoint = float2(1.0, 0.0); - sampleColorAtPoint(samplePoint, cellTexCoords, viewIndex, depthAtCenter, kernelSum, colorSum); - } - - // Sample at (-1, 0) - { - float2 samplePoint = float2(-1.0, 0.0); - sampleColorAtPoint(samplePoint, cellTexCoords, viewIndex, depthAtCenter, kernelSum, colorSum); - } - - // Sample at (0, 1) - { - float2 samplePoint = float2(0.0, 1.0); - sampleColorAtPoint(samplePoint, cellTexCoords, viewIndex, depthAtCenter, kernelSum, colorSum); - } - - // Sample at (0, -1) - { - float2 samplePoint = float2(0.0, -1.0); - sampleColorAtPoint(samplePoint, cellTexCoords, viewIndex, depthAtCenter, kernelSum, colorSum); - } + sampleColorAtPoint(fragUV, float2(1.0, 0.0), viewIndex, fragDepth, kernelSum, colorSum); + sampleColorAtPoint(fragUV, float2(-1.0, 0.0), viewIndex, fragDepth, kernelSum, colorSum); + sampleColorAtPoint(fragUV, float2(0.0, 1.0), viewIndex, fragDepth, kernelSum, colorSum); + sampleColorAtPoint(fragUV, float2(0.0, -1.0), viewIndex, fragDepth, kernelSum, colorSum); float a = 1.0; float b = 1.0; @@ -291,9 +257,9 @@ float4 fragHDRP(float2 uv, int2 coord) { for (uint i = 0; i < (uint) radius; i++) { // Sample at current Fibonacci point - sampleColorAtPoint(samplePoint * 0.5f, cellTexCoords, viewIndex, depthAtCenter, kernelSum, colorSum); + sampleColorAtPoint(fragUV, samplePoint * 0.5f, viewIndex, fragDepth, kernelSum, colorSum); - // calculate new Fibonacci point + // Generate next point on the Fibonacci spiral if (i > 1) { float next = a + b; a = b; @@ -313,18 +279,17 @@ float4 fragHDRP(float2 uv, int2 coord) { samplePoint += square; } - if (kernelSum <= 0.01f) { - return float4(1.0f, 0.5f, 0.1f, 1.0f); // return orange if no samples were collected + if (kernelSum == 0.0) { + return float4(1.0, 0.5, 0.1, 1.0); // return orange if no samples were collected } - return float4(colorSum.xyz / kernelSum, 1.0f); // return the average color of the samples + return float4(colorSum / kernelSum, 1.0); // return the average color of the samples } [numthreads(1, 1, 1)] -void kernelFunction (uint3 id : SV_DispatchThreadID) +void main(uint3 id : SV_DispatchThreadID) { float2 coord = float2(id.x, id.y); - float2 uv = coord / imageSize; - float4 color = fragHDRP(uv, coord); + float4 color = frag(coord); Result[coord] = color; -} \ No newline at end of file +} diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 2d0ea67..a9a55a9 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -39,7 +39,7 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff if (fillHoles) { holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); - holeFillingKernel = holeFillingCompShader.FindKernel("kernelFunction"); + holeFillingKernel = holeFillingCompShader.FindKernel("main"); } if (fxaaEnabled) From debf28a43d4aad7265a40facf69893a90939985f Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 17 Aug 2025 19:26:12 +0200 Subject: [PATCH 048/149] use matrix arrays --- Resources/G3DViewGenHoleFilling.compute | 88 ++----------------------- Scripts/G3DHDRPViewGenerationPass.cs | 9 +++ 2 files changed, 16 insertions(+), 81 deletions(-) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index d15f2f1..aed0721 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -13,40 +13,8 @@ SamplerState sampler_colorMosaic; // with cs.SetTexture RWTexture2D Result; - -float4x4 inverseProjMatrix0; -float4x4 inverseProjMatrix1; -float4x4 inverseProjMatrix2; -float4x4 inverseProjMatrix3; -float4x4 inverseProjMatrix4; -float4x4 inverseProjMatrix5; -float4x4 inverseProjMatrix6; -float4x4 inverseProjMatrix7; -float4x4 inverseProjMatrix8; -float4x4 inverseProjMatrix9; -float4x4 inverseProjMatrix10; -float4x4 inverseProjMatrix11; -float4x4 inverseProjMatrix12; -float4x4 inverseProjMatrix13; -float4x4 inverseProjMatrix14; -float4x4 inverseProjMatrix15; - -float4x4 viewMatrix0; -float4x4 viewMatrix1; -float4x4 viewMatrix2; -float4x4 viewMatrix3; -float4x4 viewMatrix4; -float4x4 viewMatrix5; -float4x4 viewMatrix6; -float4x4 viewMatrix7; -float4x4 viewMatrix8; -float4x4 viewMatrix9; -float4x4 viewMatrix10; -float4x4 viewMatrix11; -float4x4 viewMatrix12; -float4x4 viewMatrix13; -float4x4 viewMatrix14; -float4x4 viewMatrix15; +float4x4 viewMatrices[16]; +float4x4 invProjMatrices[16]; @@ -115,54 +83,12 @@ float getCameraLogDepth(float2 uv, int cameraIndex) { } } -float4x4 getInverseViewProjectionMatrix(int viewIndex) { - switch (viewIndex) { - case 0: return inverseProjMatrix0; - case 1: return inverseProjMatrix1; - case 2: return inverseProjMatrix2; - case 3: return inverseProjMatrix3; - case 4: return inverseProjMatrix4; - case 5: return inverseProjMatrix5; - case 6: return inverseProjMatrix6; - case 7: return inverseProjMatrix7; - case 8: return inverseProjMatrix8; - case 9: return inverseProjMatrix9; - case 10: return inverseProjMatrix10; - case 11: return inverseProjMatrix11; - case 12: return inverseProjMatrix12; - case 13: return inverseProjMatrix13; - case 14: return inverseProjMatrix14; - case 15: return inverseProjMatrix15; - default: return float4x4(1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f); - } +float4x4 getInvProjMatrix(int viewIndex) { + return invProjMatrices[viewIndex]; } float4x4 getViewMatrix(int viewIndex) { - switch (viewIndex) { - case 0: return viewMatrix0; - case 1: return viewMatrix1; - case 2: return viewMatrix2; - case 3: return viewMatrix3; - case 4: return viewMatrix4; - case 5: return viewMatrix5; - case 6: return viewMatrix6; - case 7: return viewMatrix7; - case 8: return viewMatrix8; - case 9: return viewMatrix9; - case 10: return viewMatrix10; - case 11: return viewMatrix11; - case 12: return viewMatrix12; - case 13: return viewMatrix13; - case 14: return viewMatrix14; - case 15: return viewMatrix15; - default: return float4x4(1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); - } + return viewMatrices[viewIndex]; } // here UV is treated as a full screen UV coordinate @@ -172,7 +98,7 @@ float2 calculateProjectedFragmentPosition(float2 uv, int viewIndex, float4x4 vie float deviceDepth = logDepth; // Reconstruct the world space positions. - float3 worldPos = ComputeWorldSpacePosition(uv, deviceDepth, getInverseViewProjectionMatrix(viewIndex)); + float3 worldPos = ComputeWorldSpacePosition(uv, deviceDepth, getInvProjMatrix(viewIndex)); float3 NDC = ComputeNormalizedDeviceCoordinatesWithZ(worldPos, viewProjectionMatrix); // convert from clip space to NDC coordinates @@ -183,7 +109,7 @@ float Linear01DepthViewBased(float2 uv, int viewIndex) { float logDepth = getCameraLogDepth(uv, viewIndex); // Sample the depth from the Camera depth texture. float deviceDepth = logDepth; - float3 worldPos = ComputeWorldSpacePosition(uv, deviceDepth, getInverseViewProjectionMatrix(viewIndex)); + float3 worldPos = ComputeWorldSpacePosition(uv, deviceDepth, getInvProjMatrix(viewIndex)); float eyeDepth = LinearEyeDepth(worldPos, getViewMatrix(viewIndex)); return eyeDepth / _ProjectionParams.z; // convert to linear depth in range [0, 1] } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index a9a55a9..d3a12d2 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -104,6 +104,9 @@ protected override void Execute(CustomPassContext ctx) private void runReprojection(CustomPassContext ctx) { // upload all inv view projection matrices + Matrix4x4[] viewMatrices = new Matrix4x4[16]; + Matrix4x4[] invProjMatrices = new Matrix4x4[16]; + for (int i = 0; i < internalCameraCount; i++) { Matrix4x4 projectionMatrixInner = GL.GetGPUProjectionMatrix( @@ -117,8 +120,14 @@ private void runReprojection(CustomPassContext ctx) setMatrix(ctx, invGPUProjMatrix, "inverseProjMatrix" + i); setMatrix(ctx, cameras[i].worldToCameraMatrix, "viewMatrix" + i); + + viewMatrices[i] = cameras[i].worldToCameraMatrix; + invProjMatrices[i] = invGPUProjMatrix; } + ctx.cmd.SetComputeMatrixArrayParam(holeFillingCompShader, "viewMatrices", viewMatrices); + ctx.cmd.SetComputeMatrixArrayParam(holeFillingCompShader, "invProjMatrices", invProjMatrices); + addViewProjectionMatrix(ctx, cameras[0], "leftViewProjMatrix"); addViewProjectionMatrix(ctx, cameras[internalCameraCount / 2], "middleViewProjMatrix"); addViewProjectionMatrix(ctx, cameras[internalCameraCount - 1], "rightViewProjMatrix"); From 67e408860baa0425b9e87e8e30f9143a974d864c Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 17 Aug 2025 19:32:35 +0200 Subject: [PATCH 049/149] allow hole filling radius to be 0 --- G3DCamera.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 58a1423..fad5453 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -254,7 +254,7 @@ private float scaledHalfCameraWidthAtStart public bool applyFXAA; public bool debugRendering; - [Range(1, 64)] + [Range(0, 64)] public int holeFillingRadius; private Material viewGenerationMaterial; #if G3D_HDRP From 7fc0b0dbfe4b4a583a2c760e4d72f8028f5d1d47 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 17 Aug 2025 19:51:01 +0200 Subject: [PATCH 050/149] sample in plus pattern --- Resources/G3DViewGenHoleFilling.compute | 68 ++++++++++++++----------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/Resources/G3DViewGenHoleFilling.compute b/Resources/G3DViewGenHoleFilling.compute index aed0721..9750f18 100644 --- a/Resources/G3DViewGenHoleFilling.compute +++ b/Resources/G3DViewGenHoleFilling.compute @@ -120,9 +120,9 @@ float4 getColorMosaic(float2 uv, int viewIndex) { } void sampleColorAtPoint(float2 fragUV, float2 delta, uint viewIndex, float fragDepth, inout float kernelSum, inout float3 colorSum) { - float2 uv = clamp(fragUV + delta / imageSize, 0, 1); + float2 uv = clamp(fragUV + delta / imageSize, float2(0.0, 0.0), float2(1.0, 1.0)); - float distanceSq = dot(delta, delta); + // float distanceSq = dot(delta, delta); // float weight = exp(-distanceSq / (2.0f * sigma * sigma)); float4 sampleColor = getColorMosaic(uv, viewIndex); @@ -139,10 +139,9 @@ void sampleColorAtPoint(float2 fragUV, float2 delta, uint viewIndex, float fragD return; } - float diff = abs(fragDepth - sampleDepth); float weight = 1.0; - // weight = weight * (diff * diff); - weight /= distanceSq + 0.01; + weight *= exp(-length(delta)); + weight *= exp(-(fragDepth - sampleDepth)); kernelSum += weight; colorSum += weight * sampleColor.rgb; @@ -161,7 +160,7 @@ float4 frag(int2 coord) { float fragDepth = Linear01DepthViewBased(fragUV, viewIndex); float4 cellColor = getColorMosaic(fragUV, viewIndex); // sample the color of the left camera texture - if( cellColor.w >= 1.0f) { + if( cellColor.w >= 1.0) { // if the cell color is valid, return the original color return cellColor; } @@ -169,41 +168,50 @@ float4 frag(int2 coord) { float kernelSum = 0.0; float3 colorSum = float3(0.0, 0.0, 0.0); + sampleColorAtPoint(fragUV, float2(0.0, 0.0), viewIndex, fragDepth, kernelSum, colorSum); + sampleColorAtPoint(fragUV, float2(1.0, 0.0), viewIndex, fragDepth, kernelSum, colorSum); sampleColorAtPoint(fragUV, float2(-1.0, 0.0), viewIndex, fragDepth, kernelSum, colorSum); sampleColorAtPoint(fragUV, float2(0.0, 1.0), viewIndex, fragDepth, kernelSum, colorSum); sampleColorAtPoint(fragUV, float2(0.0, -1.0), viewIndex, fragDepth, kernelSum, colorSum); - float a = 1.0; - float b = 1.0; - float2 samplePoint = float2(0.0, 0.0); - float side = 1.0; + for (int i = 0; i < radius; i++) { + sampleColorAtPoint(fragUV, float2(i, 0.0), viewIndex, fragDepth, kernelSum, colorSum); + sampleColorAtPoint(fragUV, float2(-i, 0.0), viewIndex, fragDepth, kernelSum, colorSum); + sampleColorAtPoint(fragUV, float2(0.0, i), viewIndex, fragDepth, kernelSum, colorSum); + sampleColorAtPoint(fragUV, float2(0.0, -i), viewIndex, fragDepth, kernelSum, colorSum); + } + + // float a = 1.0; + // float b = 1.0; + // float2 samplePoint = float2(0.0, 0.0); + // float side = 1.0; - float2 stride = float2(1.0, 1.0); - for (uint i = 0; i < (uint) radius; i++) { + // float2 stride = float2(1.0, 1.0); + // for (uint i = 0; i < (uint) radius; i++) { - // Sample at current Fibonacci point - sampleColorAtPoint(fragUV, samplePoint * 0.5f, viewIndex, fragDepth, kernelSum, colorSum); + // // Sample at current Fibonacci point + // sampleColorAtPoint(fragUV, samplePoint * 0.5f, viewIndex, fragDepth, kernelSum, colorSum); - // Generate next point on the Fibonacci spiral - if (i > 1) { - float next = a + b; - a = b; - b = next; - side = b; - } + // // Generate next point on the Fibonacci spiral + // if (i > 1) { + // float next = a + b; + // a = b; + // b = next; + // side = b; + // } - float2 square = float2(side, side); + // float2 square = float2(side, side); - if ((i >> 1) & 1 != 0) { - square.x *= -1.0; - } - if (((i + 1) >> 1) & 1 != 0) { - square.y *= -1.0; - } + // if ((i >> 1) & 1 != 0) { + // square.x *= -1.0; + // } + // if (((i + 1) >> 1) & 1 != 0) { + // square.y *= -1.0; + // } - samplePoint += square; - } + // samplePoint += square; + // } if (kernelSum == 0.0) { return float4(1.0, 0.5, 0.1, 1.0); // return orange if no samples were collected From 8b0ae076aeb85e37469ed7c8da378b5c41db0644 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 18 Aug 2025 16:21:02 +0200 Subject: [PATCH 051/149] formated code; fix if hole filling is disabled --- Scripts/G3DHDRPViewGenerationPass.cs | 29 ++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index d3a12d2..c79ad34 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -106,7 +106,7 @@ private void runReprojection(CustomPassContext ctx) // upload all inv view projection matrices Matrix4x4[] viewMatrices = new Matrix4x4[16]; Matrix4x4[] invProjMatrices = new Matrix4x4[16]; - + for (int i = 0; i < internalCameraCount; i++) { Matrix4x4 projectionMatrixInner = GL.GetGPUProjectionMatrix( @@ -125,8 +125,15 @@ private void runReprojection(CustomPassContext ctx) invProjMatrices[i] = invGPUProjMatrix; } - ctx.cmd.SetComputeMatrixArrayParam(holeFillingCompShader, "viewMatrices", viewMatrices); - ctx.cmd.SetComputeMatrixArrayParam(holeFillingCompShader, "invProjMatrices", invProjMatrices); + if (fillHoles) + { + ctx.cmd.SetComputeMatrixArrayParam(holeFillingCompShader, "viewMatrices", viewMatrices); + ctx.cmd.SetComputeMatrixArrayParam( + holeFillingCompShader, + "invProjMatrices", + invProjMatrices + ); + } addViewProjectionMatrix(ctx, cameras[0], "leftViewProjMatrix"); addViewProjectionMatrix(ctx, cameras[internalCameraCount / 2], "middleViewProjMatrix"); @@ -173,12 +180,22 @@ private void runHoleFilling(CustomPassContext ctx) "_colorMosaic", mosaicImageHandle ); - ctx.cmd.SetComputeIntParams(holeFillingCompShader, "gridSize", new int[] {4, 4}); + ctx.cmd.SetComputeIntParams(holeFillingCompShader, "gridSize", new int[] { 4, 4 }); ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); - ctx.cmd.SetComputeIntParams(holeFillingCompShader, "imageSize", new int[] {mosaicImageHandle.rt.width, mosaicImageHandle.rt.height}); + ctx.cmd.SetComputeIntParams( + holeFillingCompShader, + "imageSize", + new int[] { mosaicImageHandle.rt.width, mosaicImageHandle.rt.height } + ); - ctx.cmd.DispatchCompute(holeFillingCompShader, holeFillingKernel, mosaicImageHandle.rt.width, mosaicImageHandle.rt.height, 1); + ctx.cmd.DispatchCompute( + holeFillingCompShader, + holeFillingKernel, + mosaicImageHandle.rt.width, + mosaicImageHandle.rt.height, + 1 + ); // Blit the result to the mosaic image CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); From 6b0f64b733bb0fac998b50a5f48c4ee4eb6dc36f Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 18 Aug 2025 16:37:25 +0200 Subject: [PATCH 052/149] added 27 inch display calibration --- .../Display_27-Inch_17D.txt | 33 +++++++++++++++++++ .../Display_27-Inch_17D.txt.meta | 7 ++++ 2 files changed, 40 insertions(+) create mode 100644 Multiview Display Calibrations/Display_27-Inch_17D.txt create mode 100644 Multiview Display Calibrations/Display_27-Inch_17D.txt.meta diff --git a/Multiview Display Calibrations/Display_27-Inch_17D.txt b/Multiview Display Calibrations/Display_27-Inch_17D.txt new file mode 100644 index 0000000..00336b3 --- /dev/null +++ b/Multiview Display Calibrations/Display_27-Inch_17D.txt @@ -0,0 +1,33 @@ + +[MonitorConfiguration] +Name=DP +SerialNumber=S18.001EF +PhysicalSizeInch=27.000000 +DistanceSensorToPanelCenterMM=0.000000 +HorizontalResolution=5120 +VerticalResolution=2880 +SensorMountingPosition=0 +ConfigCode=17d +NativeViewcount=17 +AngleRatioNumerator=4 +AngleRatioDenominator=5 +LeftLensOrientation=0 +isBGR=false +ViewOffsetAtBaseDistance=3 +BlackBorderDefault=0 +BlackSpaceDefault=0 +MaxAllowedHorizontalAngle=15.000000 +MaxAllowedVerticalAngle=20.000000 +MinAllowedDistanceMM=600.000000 +MaxAllowedDistanceMM=1000.000000 +BasicWorkingDistanceMM=837 +MinWorkingDistanceMM=547 +MaxWorkingDistanceMM=1019 +ZoneWidthAtMinWorkingDistance=217 +ZoneWidthAtMaxWorkingDistance=-669 +zCorrectionFunctionFactor=71.611902 +zCorrectionFunctionBase=0.768900 +ZCompensationValueAtMinimumWorkingDistance=41 +ZCompensationValueAtMaximumWorkingDistance=-14 +LatencyCorrectionTimeShift=0.000000 +ApertureAngle=14.9 diff --git a/Multiview Display Calibrations/Display_27-Inch_17D.txt.meta b/Multiview Display Calibrations/Display_27-Inch_17D.txt.meta new file mode 100644 index 0000000..5fe9828 --- /dev/null +++ b/Multiview Display Calibrations/Display_27-Inch_17D.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 877ae41b9ab9e534a8ec1c3137dc8b5a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 8e633ad7a1ba3ee33892d67079f24a91c5ea852d Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 24 Aug 2025 18:33:58 +0200 Subject: [PATCH 053/149] initial --- Resources/G3DFXAA.compute | 182 +++++++----------------- Resources/G3DFXAA_default.compute | 185 +++++++++++++++++++++++++ Resources/G3DFXAA_default.compute.meta | 7 + 3 files changed, 243 insertions(+), 131 deletions(-) create mode 100644 Resources/G3DFXAA_default.compute create mode 100644 Resources/G3DFXAA_default.compute.meta diff --git a/Resources/G3DFXAA.compute b/Resources/G3DFXAA.compute index d87be9e..2f8fefa 100644 --- a/Resources/G3DFXAA.compute +++ b/Resources/G3DFXAA.compute @@ -1,9 +1,4 @@ -// Each #kernel tells which function to compile; you can have many kernels #pragma kernel FXAA -#pragma multi_compile _ ENABLE_ALPHA -#pragma multi_compile _ HDR_INPUT - -#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" @@ -15,12 +10,6 @@ RWTexture2D _OutputTexture; Texture2D _colorMosaic; SamplerState sampler_colorMosaic; - -float4 _HDROutputParams; -#define _PaperWhite _HDROutputParams.z -#define _OneOverPaperWhite _HDROutputParams.w - -#define FXAA_HDR_MAPUNMAP defined(HDR_INPUT) #define FXAA_SPAN_MAX (4.0) #define FXAA_REDUCE_MUL (1.0 / 4.0) #define FXAA_REDUCE_MIN (1.0 / 32.0) @@ -46,121 +35,57 @@ float FetchAlpha(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 co return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).w; } -void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 outColor, uint2 positionSS, float2 positionNDC, float paperWhite, float oneOverPaperWhite) +void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 outColor, uint2 positionSS, float2 positionNDC) { - { - // Edge detection - float4 rgbNW = Load(_InputTexture, positionSS, -1, -1); - float4 rgbNE = Load(_InputTexture, positionSS, 1, -1); - float4 rgbSW = Load(_InputTexture, positionSS, -1, 1); - float4 rgbSE = Load(_InputTexture, positionSS, 1, 1); - -#if FXAA_HDR_MAPUNMAP - // The pixel values we have are already tonemapped but in the range [0, 10000] nits. To run FXAA properly, we need to convert them - // to a SDR range [0; 1]. Since the tonemapped values are not evenly distributed and mostly close to the paperWhite nits value, we can - // normalize by paperWhite to get most of the scene in [0; 1] range. For the remaining pixels, we can use the FastTonemap() to remap - // them to [0, 1] range. - float lumaNW = Luminance(FastTonemap(rgbNW.xyz * oneOverPaperWhite)); - float lumaNE = Luminance(FastTonemap(rgbNE.xyz * oneOverPaperWhite)); - float lumaSW = Luminance(FastTonemap(rgbSW.xyz * oneOverPaperWhite)); - float lumaSE = Luminance(FastTonemap(rgbSE.xyz * oneOverPaperWhite)); - float lumaM = Luminance(FastTonemap(outColor.xyz * oneOverPaperWhite)); -#else - rgbNW.xyz = saturate(rgbNW.xyz); - rgbNE.xyz = saturate(rgbNE.xyz); - rgbSW.xyz = saturate(rgbSW.xyz); - rgbSE.xyz = saturate(rgbSE.xyz); - outColor.xyz = saturate(outColor.xyz); - float lumaNW = Luminance(rgbNW.xyz); - float lumaNE = Luminance(rgbNE.xyz); - float lumaSW = Luminance(rgbSW.xyz); - float lumaSE = Luminance(rgbSE.xyz); - float lumaM = Luminance(outColor.xyz); -#endif - - float2 dir; - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); - - float lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; - float dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); - float rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); - - dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; - - // Blur - float3 rgb03 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); - float3 rgb13 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); - float3 rgb23 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); - float3 rgb33 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); - -#if FXAA_HDR_MAPUNMAP - rgb03 = FastTonemap(rgb03 * oneOverPaperWhite); - rgb13 = FastTonemap(rgb13 * oneOverPaperWhite); - rgb23 = FastTonemap(rgb23 * oneOverPaperWhite); - rgb33 = FastTonemap(rgb33 * oneOverPaperWhite); -#else - rgb03 = saturate(rgb03); - rgb13 = saturate(rgb13); - rgb23 = saturate(rgb23); - rgb33 = saturate(rgb33); -#endif - - float3 rgbA = 0.5 * (rgb13 + rgb23); - float3 rgbB = rgbA * 0.5 + 0.25 * (rgb03 + rgb33); - - float lumaB = Luminance(rgbB); - - float lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); - float lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); - - float3 rgb = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? rgbA : rgbB; - -#if FXAA_HDR_MAPUNMAP - outColor.xyz = FastTonemapInvert(rgb) * paperWhite;; -#else - outColor.xyz = rgb; -#endif - -#ifdef ENABLE_ALPHA - // FXAA for the alpha channel: alpha can be completely decorelated from the RGB channels, so we might fetch different neighbors for the alpha! - // For this reason we have to recompute the fetch direction - lumaNW = rgbNW.w; - lumaNE = rgbNE.w; - lumaSW = rgbSW.w; - lumaSE = rgbSE.w; - lumaM = outColor.w; - - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); - - lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; - dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); - rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); - - dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; - - // Blur - float a03 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); - float a13 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); - float a23 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); - float a33 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); - - a03 = saturate(a03); - a13 = saturate(a13); - a23 = saturate(a23); - a33 = saturate(a33); - - float A = 0.5 * (a13 + a23); - float B = A * 0.5 + 0.25 * (a03 + a33); - - lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); - lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); - - outColor.w = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? A : B; -#endif - - } + // Edge detection + float4 rgbNW = Load(_InputTexture, positionSS, -1, -1); + float4 rgbNE = Load(_InputTexture, positionSS, 1, -1); + float4 rgbSW = Load(_InputTexture, positionSS, -1, 1); + float4 rgbSE = Load(_InputTexture, positionSS, 1, 1); + + rgbNW.xyz = saturate(rgbNW.xyz); + rgbNE.xyz = saturate(rgbNE.xyz); + rgbSW.xyz = saturate(rgbSW.xyz); + rgbSE.xyz = saturate(rgbSE.xyz); + outColor.xyz = saturate(outColor.xyz); + float lumaNW = Luminance(rgbNW.xyz); + float lumaNE = Luminance(rgbNE.xyz); + float lumaSW = Luminance(rgbSW.xyz); + float lumaSE = Luminance(rgbSE.xyz); + float lumaM = Luminance(outColor.xyz); + + float2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + float lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; + float dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + float rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); + + dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; + + // Blur + float3 rgb03 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); + float3 rgb13 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); + float3 rgb23 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); + float3 rgb33 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); + + rgb03 = saturate(rgb03); + rgb13 = saturate(rgb13); + rgb23 = saturate(rgb23); + rgb33 = saturate(rgb33); + + float3 rgbA = 0.5 * (rgb13 + rgb23); + float3 rgbB = rgbA * 0.5 + 0.25 * (rgb03 + rgb33); + + float lumaB = Luminance(rgbB); + + float lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); + float lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); + + float3 rgb = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? rgbA : rgbB; + + outColor.xyz = rgb; } @@ -174,12 +99,7 @@ void FXAA(uint3 dispatchThreadId : SV_DispatchThreadID) float4 outColor = _colorMosaic.Load(float4(positionSS, 0, 0)); float4 beforeFXAA = outColor; - RunFXAA(_colorMosaic, s_linear_clamp_sampler, outColor, positionSS, positionNDC, _PaperWhite, _OneOverPaperWhite); - -// #if defined(ENABLE_ALPHA) -// // When alpha processing is enabled, FXAA should not affect pixels with zero alpha -// outColor.xyz = outColor.a > 0 ? outColor.xyz : beforeFXAA.xyz; -// #endif + RunFXAA(_colorMosaic, s_linear_clamp_sampler, outColor, positionSS, positionNDC); _OutputTexture[positionSS] = outColor; } diff --git a/Resources/G3DFXAA_default.compute b/Resources/G3DFXAA_default.compute new file mode 100644 index 0000000..d87be9e --- /dev/null +++ b/Resources/G3DFXAA_default.compute @@ -0,0 +1,185 @@ +// Each #kernel tells which function to compile; you can have many kernels +#pragma kernel FXAA +#pragma multi_compile _ ENABLE_ALPHA +#pragma multi_compile _ HDR_INPUT + +#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch + +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/PostProcessDefines.hlsl" + +RWTexture2D _OutputTexture; +Texture2D _colorMosaic; +SamplerState sampler_colorMosaic; + + +float4 _HDROutputParams; +#define _PaperWhite _HDROutputParams.z +#define _OneOverPaperWhite _HDROutputParams.w + +#define FXAA_HDR_MAPUNMAP defined(HDR_INPUT) +#define FXAA_SPAN_MAX (4.0) +#define FXAA_REDUCE_MUL (1.0 / 4.0) +#define FXAA_REDUCE_MIN (1.0 / 32.0) +// original: +// #define FXAA_SPAN_MAX (8.0) +// #define FXAA_REDUCE_MUL (1.0 / 8.0) +// #define FXAA_REDUCE_MIN (1.0 / 128.0) + +float3 Fetch(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 coords, float2 offset) +{ + float2 uv = saturate(coords + offset) * _RTHandlePostProcessScale.xy; + return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).xyz; +} + +float4 Load(TEXTURE2D(_InputTexture), int2 icoords, int idx, int idy) +{ + return LOAD_TEXTURE2D(_InputTexture, min(icoords + int2(idx, idy), _PostProcessScreenSize.xy - 1.0)); +} + +float FetchAlpha(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 coords, float2 offset) +{ + float2 uv = saturate(coords + offset) * _RTHandleScale.xy; + return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).w; +} + +void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 outColor, uint2 positionSS, float2 positionNDC, float paperWhite, float oneOverPaperWhite) +{ + { + // Edge detection + float4 rgbNW = Load(_InputTexture, positionSS, -1, -1); + float4 rgbNE = Load(_InputTexture, positionSS, 1, -1); + float4 rgbSW = Load(_InputTexture, positionSS, -1, 1); + float4 rgbSE = Load(_InputTexture, positionSS, 1, 1); + +#if FXAA_HDR_MAPUNMAP + // The pixel values we have are already tonemapped but in the range [0, 10000] nits. To run FXAA properly, we need to convert them + // to a SDR range [0; 1]. Since the tonemapped values are not evenly distributed and mostly close to the paperWhite nits value, we can + // normalize by paperWhite to get most of the scene in [0; 1] range. For the remaining pixels, we can use the FastTonemap() to remap + // them to [0, 1] range. + float lumaNW = Luminance(FastTonemap(rgbNW.xyz * oneOverPaperWhite)); + float lumaNE = Luminance(FastTonemap(rgbNE.xyz * oneOverPaperWhite)); + float lumaSW = Luminance(FastTonemap(rgbSW.xyz * oneOverPaperWhite)); + float lumaSE = Luminance(FastTonemap(rgbSE.xyz * oneOverPaperWhite)); + float lumaM = Luminance(FastTonemap(outColor.xyz * oneOverPaperWhite)); +#else + rgbNW.xyz = saturate(rgbNW.xyz); + rgbNE.xyz = saturate(rgbNE.xyz); + rgbSW.xyz = saturate(rgbSW.xyz); + rgbSE.xyz = saturate(rgbSE.xyz); + outColor.xyz = saturate(outColor.xyz); + float lumaNW = Luminance(rgbNW.xyz); + float lumaNE = Luminance(rgbNE.xyz); + float lumaSW = Luminance(rgbSW.xyz); + float lumaSE = Luminance(rgbSE.xyz); + float lumaM = Luminance(outColor.xyz); +#endif + + float2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + float lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; + float dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + float rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); + + dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; + + // Blur + float3 rgb03 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); + float3 rgb13 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); + float3 rgb23 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); + float3 rgb33 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); + +#if FXAA_HDR_MAPUNMAP + rgb03 = FastTonemap(rgb03 * oneOverPaperWhite); + rgb13 = FastTonemap(rgb13 * oneOverPaperWhite); + rgb23 = FastTonemap(rgb23 * oneOverPaperWhite); + rgb33 = FastTonemap(rgb33 * oneOverPaperWhite); +#else + rgb03 = saturate(rgb03); + rgb13 = saturate(rgb13); + rgb23 = saturate(rgb23); + rgb33 = saturate(rgb33); +#endif + + float3 rgbA = 0.5 * (rgb13 + rgb23); + float3 rgbB = rgbA * 0.5 + 0.25 * (rgb03 + rgb33); + + float lumaB = Luminance(rgbB); + + float lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); + float lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); + + float3 rgb = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? rgbA : rgbB; + +#if FXAA_HDR_MAPUNMAP + outColor.xyz = FastTonemapInvert(rgb) * paperWhite;; +#else + outColor.xyz = rgb; +#endif + +#ifdef ENABLE_ALPHA + // FXAA for the alpha channel: alpha can be completely decorelated from the RGB channels, so we might fetch different neighbors for the alpha! + // For this reason we have to recompute the fetch direction + lumaNW = rgbNW.w; + lumaNE = rgbNE.w; + lumaSW = rgbSW.w; + lumaSE = rgbSE.w; + lumaM = outColor.w; + + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; + dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); + + dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; + + // Blur + float a03 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); + float a13 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); + float a23 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); + float a33 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); + + a03 = saturate(a03); + a13 = saturate(a13); + a23 = saturate(a23); + a33 = saturate(a33); + + float A = 0.5 * (a13 + a23); + float B = A * 0.5 + 0.25 * (a03 + a33); + + lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); + lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); + + outColor.w = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? A : B; +#endif + + } +} + + +[numthreads(8,8,1)] +void FXAA(uint3 dispatchThreadId : SV_DispatchThreadID) +{ + UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z); + uint2 positionSS = dispatchThreadId.xy; + float2 positionNDC = positionSS * _PostProcessScreenSize.zw + (0.5 * _PostProcessScreenSize.zw); + + float4 outColor = _colorMosaic.Load(float4(positionSS, 0, 0)); + float4 beforeFXAA = outColor; + + RunFXAA(_colorMosaic, s_linear_clamp_sampler, outColor, positionSS, positionNDC, _PaperWhite, _OneOverPaperWhite); + +// #if defined(ENABLE_ALPHA) +// // When alpha processing is enabled, FXAA should not affect pixels with zero alpha +// outColor.xyz = outColor.a > 0 ? outColor.xyz : beforeFXAA.xyz; +// #endif + + _OutputTexture[positionSS] = outColor; +} diff --git a/Resources/G3DFXAA_default.compute.meta b/Resources/G3DFXAA_default.compute.meta new file mode 100644 index 0000000..0d43893 --- /dev/null +++ b/Resources/G3DFXAA_default.compute.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 879680f11e9b4584b8d5406bbb551f7e +ComputeShaderImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 8b54eba22a56b46fd702defd24b798bc02b798db Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 24 Aug 2025 18:46:38 +0200 Subject: [PATCH 054/149] use gamma luminance --- Resources/G3DFXAA.compute | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Resources/G3DFXAA.compute b/Resources/G3DFXAA.compute index 2f8fefa..678b567 100644 --- a/Resources/G3DFXAA.compute +++ b/Resources/G3DFXAA.compute @@ -18,6 +18,11 @@ SamplerState sampler_colorMosaic; // #define FXAA_REDUCE_MUL (1.0 / 8.0) // #define FXAA_REDUCE_MIN (1.0 / 128.0) +float LuminanceGamma(float3 c) { + float3 w = float3(0.299, 0.587, 0.114); + return sqrt(dot(c, w)); +} + float3 Fetch(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 coords, float2 offset) { float2 uv = saturate(coords + offset) * _RTHandlePostProcessScale.xy; @@ -48,11 +53,11 @@ void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 rgbSW.xyz = saturate(rgbSW.xyz); rgbSE.xyz = saturate(rgbSE.xyz); outColor.xyz = saturate(outColor.xyz); - float lumaNW = Luminance(rgbNW.xyz); - float lumaNE = Luminance(rgbNE.xyz); - float lumaSW = Luminance(rgbSW.xyz); - float lumaSE = Luminance(rgbSE.xyz); - float lumaM = Luminance(outColor.xyz); + float lumaNW = LuminanceGamma(rgbNW.xyz); + float lumaNE = LuminanceGamma(rgbNE.xyz); + float lumaSW = LuminanceGamma(rgbSW.xyz); + float lumaSE = LuminanceGamma(rgbSE.xyz); + float lumaM = LuminanceGamma(outColor.xyz); float2 dir; dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); @@ -78,7 +83,7 @@ void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 float3 rgbA = 0.5 * (rgb13 + rgb23); float3 rgbB = rgbA * 0.5 + 0.25 * (rgb03 + rgb33); - float lumaB = Luminance(rgbB); + float lumaB = LuminanceGamma(rgbB); float lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); float lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); From 2a21a56b60705bd05d24eb2cadb8daf4db5c1722 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 24 Aug 2025 18:47:55 +0200 Subject: [PATCH 055/149] use 1x1x1 kernel size for now --- Resources/G3DFXAA.compute | 2 +- Scripts/G3DHDRPViewGenerationPass.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/G3DFXAA.compute b/Resources/G3DFXAA.compute index 678b567..f1e95d5 100644 --- a/Resources/G3DFXAA.compute +++ b/Resources/G3DFXAA.compute @@ -94,7 +94,7 @@ void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 } -[numthreads(8,8,1)] +[numthreads(1, 1, 1)] void FXAA(uint3 dispatchThreadId : SV_DispatchThreadID) { UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z); diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index c79ad34..bfe72da 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -228,8 +228,8 @@ private void runFXAA(CustomPassContext ctx) ctx.cmd.DispatchCompute( fxaaCompShader, fxaaKernel, - (mosaicImageHandle.rt.width + 7) / 8, - (mosaicImageHandle.rt.height + 7) / 8, + mosaicImageHandle.rt.width, + mosaicImageHandle.rt.height, 1 ); } From c58d2823f18324e3d7d627d12bd73c30c0b47869 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 24 Aug 2025 20:02:30 +0200 Subject: [PATCH 056/149] do not run fxx on debug view boundaries --- Resources/G3DFXAA.compute | 148 ++++++++++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 37 deletions(-) diff --git a/Resources/G3DFXAA.compute b/Resources/G3DFXAA.compute index f1e95d5..3a32e26 100644 --- a/Resources/G3DFXAA.compute +++ b/Resources/G3DFXAA.compute @@ -26,7 +26,8 @@ float LuminanceGamma(float3 c) { float3 Fetch(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 coords, float2 offset) { float2 uv = saturate(coords + offset) * _RTHandlePostProcessScale.xy; - return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).xyz; + float3 color = SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).xyz; + return saturate(color); } float4 Load(TEXTURE2D(_InputTexture), int2 icoords, int idx, int idy) @@ -42,55 +43,123 @@ float FetchAlpha(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 co void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 outColor, uint2 positionSS, float2 positionNDC) { - // Edge detection - float4 rgbNW = Load(_InputTexture, positionSS, -1, -1); - float4 rgbNE = Load(_InputTexture, positionSS, 1, -1); - float4 rgbSW = Load(_InputTexture, positionSS, -1, 1); - float4 rgbSE = Load(_InputTexture, positionSS, 1, 1); - - rgbNW.xyz = saturate(rgbNW.xyz); - rgbNE.xyz = saturate(rgbNE.xyz); - rgbSW.xyz = saturate(rgbSW.xyz); - rgbSE.xyz = saturate(rgbSE.xyz); - outColor.xyz = saturate(outColor.xyz); - float lumaNW = LuminanceGamma(rgbNW.xyz); - float lumaNE = LuminanceGamma(rgbNE.xyz); - float lumaSW = LuminanceGamma(rgbSW.xyz); - float lumaSE = LuminanceGamma(rgbSE.xyz); - float lumaM = LuminanceGamma(outColor.xyz); + float c = LuminanceGamma(Load(_InputTexture, positionSS, 0, 0).rgb); + float u = LuminanceGamma(Load(_InputTexture, positionSS, 0, -1).rgb); + float d = LuminanceGamma(Load(_InputTexture, positionSS, 0, 1).rgb); + float l = LuminanceGamma(Load(_InputTexture, positionSS, -1, 0).rgb); + float r = LuminanceGamma(Load(_InputTexture, positionSS, 1, 0).rgb); + + // ---- + + float3 rgbM = Load(_InputTexture, positionSS, 0, 0).rgb; + float3 rgbNW = Load(_InputTexture, positionSS, -1, -1).rgb; + float3 rgbNE = Load(_InputTexture, positionSS, 1, -1).rgb; + float3 rgbSW = Load(_InputTexture, positionSS, -1, 1).rgb; + float3 rgbSE = Load(_InputTexture, positionSS, 1, 1).rgb; + + float lumaM = LuminanceGamma(rgbM); + float lumaNW = LuminanceGamma(rgbNW); + float lumaNE = LuminanceGamma(rgbNE); + float lumaSW = LuminanceGamma(rgbSW); + float lumaSE = LuminanceGamma(rgbSE); + + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + float edge = 4.0 * lumaM - lumaNW - lumaNE - lumaSW - lumaSE; + + // ---- + + // if (lumaMax - lumaMin < 0.05) { + // return; + // } + if (edge < 0.05) { + return; + } + + // outColor.rgb = float3(1.0, 0.0, 0.0); + outColor.rgb = (rgbM + rgbNW + rgbNE + rgbSW + rgbSE) / 5.0; + return; + + // float convolution = 4 * c - u - d - l - r; + // float convolution = lumaMax - lumaMin; + // outColor.rgb = float3(convolution, convolution, convolution); + float2 dir; dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); - float lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; - float dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); - float rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * 0.25 * 0.5, 0.001); + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; - // Blur - float3 rgb03 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); + float3 rgb03 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (-1.0 / 3.0 - 0.5)); float3 rgb13 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); float3 rgb23 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); - float3 rgb33 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); - - rgb03 = saturate(rgb03); - rgb13 = saturate(rgb13); - rgb23 = saturate(rgb23); - rgb33 = saturate(rgb33); + float3 rgb33 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (4.0 / 3.0 - 0.5)); float3 rgbA = 0.5 * (rgb13 + rgb23); float3 rgbB = rgbA * 0.5 + 0.25 * (rgb03 + rgb33); float lumaB = LuminanceGamma(rgbB); - float lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); - float lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); - - float3 rgb = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? rgbA : rgbB; - - outColor.xyz = rgb; + if ((lumaB < lumaMin) || (lumaB > lumaMax)) { + outColor.rgb = rgbA; + } + else { + outColor.rgb = rgbB; + } + + // // Edge detection + // float4 rgbNW = Load(_InputTexture, positionSS, -1, -1); + // float4 rgbNE = Load(_InputTexture, positionSS, 1, -1); + // float4 rgbSW = Load(_InputTexture, positionSS, -1, 1); + // float4 rgbSE = Load(_InputTexture, positionSS, 1, 1); + + // rgbNW.xyz = saturate(rgbNW.xyz); + // rgbNE.xyz = saturate(rgbNE.xyz); + // rgbSW.xyz = saturate(rgbSW.xyz); + // rgbSE.xyz = saturate(rgbSE.xyz); + // outColor.xyz = saturate(outColor.xyz); + // float lumaNW = LuminanceGamma(rgbNW.xyz); + // float lumaNE = LuminanceGamma(rgbNE.xyz); + // float lumaSW = LuminanceGamma(rgbSW.xyz); + // float lumaSE = LuminanceGamma(rgbSE.xyz); + // float lumaM = LuminanceGamma(outColor.xyz); + + // float2 dir; + // dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + // dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + // float lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; + // float dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + // float rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); + + // dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; + + // // Blur + // float3 rgb03 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); + // float3 rgb13 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); + // float3 rgb23 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); + // float3 rgb33 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); + + // rgb03 = saturate(rgb03); + // rgb13 = saturate(rgb13); + // rgb23 = saturate(rgb23); + // rgb33 = saturate(rgb33); + + // float3 rgbA = 0.5 * (rgb13 + rgb23); + // float3 rgbB = rgbA * 0.5 + 0.25 * (rgb03 + rgb33); + + // float lumaB = LuminanceGamma(rgbB); + + // float lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); + // float lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); + + // float3 rgb = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? rgbA : rgbB; + + // outColor.xyz = rgb; } @@ -102,9 +171,14 @@ void FXAA(uint3 dispatchThreadId : SV_DispatchThreadID) float2 positionNDC = positionSS * _PostProcessScreenSize.zw + (0.5 * _PostProcessScreenSize.zw); float4 outColor = _colorMosaic.Load(float4(positionSS, 0, 0)); - float4 beforeFXAA = outColor; - RunFXAA(_colorMosaic, s_linear_clamp_sampler, outColor, positionSS, positionNDC); + if (4 * positionSS.x % int(_PostProcessScreenSize.x) == 0 || 4 * positionSS.y % int(_PostProcessScreenSize.y) == 0) { + // do not run fxx on debug view boundaries + // outColor = float4(1.0, 0.0, 1.0, 1.0); + } + else { + RunFXAA(_colorMosaic, s_linear_clamp_sampler, outColor, positionSS, positionNDC); + } _OutputTexture[positionSS] = outColor; } From 57a5c15dc11ec374b123ce717df0e312a2c573ce Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 24 Aug 2025 20:21:36 +0200 Subject: [PATCH 057/149] try to blur more --- Resources/G3DFXAA.compute | 49 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/Resources/G3DFXAA.compute b/Resources/G3DFXAA.compute index 3a32e26..607a8a9 100644 --- a/Resources/G3DFXAA.compute +++ b/Resources/G3DFXAA.compute @@ -77,7 +77,54 @@ void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 } // outColor.rgb = float3(1.0, 0.0, 0.0); - outColor.rgb = (rgbM + rgbNW + rgbNE + rgbSW + rgbSE) / 5.0; + // outColor.rgb = (rgbM + rgbNW + rgbNE + rgbSW + rgbSE) / 5.0; + + outColor.rgb = + 0.2 * rgbM + + 0.2 * rgbNW + + 0.2 * rgbNE + + 0.2 * rgbSW + + 0.2 * rgbSE; + + // outColor.rgb = + // 0.5 * rgbM + // + 0.125 * rgbNW + // + 0.125 * rgbNE + // + 0.125 * rgbSW + // + 0.125 * rgbSE; + + float3 colors[9] = { + Load(_InputTexture, positionSS, -1, -1).rgb, + Load(_InputTexture, positionSS, 0, -1).rgb, + Load(_InputTexture, positionSS, 1, -1).rgb, + Load(_InputTexture, positionSS, -1, 0).rgb, + Load(_InputTexture, positionSS, 0, 0).rgb, + Load(_InputTexture, positionSS, 1, 0).rgb, + Load(_InputTexture, positionSS, -1, 1).rgb, + Load(_InputTexture, positionSS, 0, 1).rgb, + Load(_InputTexture, positionSS, 1, 1).rgb + }; + + float coeffs[9] = { + 1.0 / 16.0, + 1.0 / 8.0, + 1.0 / 16.0, + 1.0 / 8.0, + 1.0 / 4.0, + 1.0 / 8.0, + 1.0 / 16.0, + 1.0 / 8.0, + 1.0 / 16.0 + }; + + float3 conv = 0.0.xxx; + for (uint i = 0; i < 9; i++) { + conv += coeffs[i] * colors[i]; + } + + outColor.rgb = conv; + + // outColor.r = 1.0; return; // float convolution = 4 * c - u - d - l - r; From aadb1bda31f37b63ace1c3c64726235c90957fb2 Mon Sep 17 00:00:00 2001 From: Jim Date: Thu, 28 Aug 2025 17:17:25 +0200 Subject: [PATCH 058/149] smaa edge detection --- Resources/G3DFXAA.compute | 37 + Resources/G3DSMAA.shader | 67 ++ Resources/G3DSMAA.shader.meta | 9 + Resources/SMAA.hlsl | 1361 ++++++++++++++++++++++++++ Resources/SMAA.hlsl.meta | 7 + Scripts/G3DHDRPViewGenerationPass.cs | 11 +- 6 files changed, 1491 insertions(+), 1 deletion(-) create mode 100644 Resources/G3DSMAA.shader create mode 100644 Resources/G3DSMAA.shader.meta create mode 100644 Resources/SMAA.hlsl create mode 100644 Resources/SMAA.hlsl.meta diff --git a/Resources/G3DFXAA.compute b/Resources/G3DFXAA.compute index 607a8a9..ded6040 100644 --- a/Resources/G3DFXAA.compute +++ b/Resources/G3DFXAA.compute @@ -1,3 +1,4 @@ +// #pragma target 4.0 #pragma kernel FXAA #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" @@ -6,6 +7,11 @@ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/PostProcessDefines.hlsl" +// #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) +// #define SMAA_HLSL_4 +// #define SMAA_PRESET_HIGH +// #include "SMAA.hlsl" + RWTexture2D _OutputTexture; Texture2D _colorMosaic; SamplerState sampler_colorMosaic; @@ -41,8 +47,39 @@ float FetchAlpha(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 co return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).w; } +SamplerState PointSampler2 { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; + void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 outColor, uint2 positionSS, float2 positionNDC) { + // bool discarded = false; + + // outColor = _InputTexture.Sample(PointSampler2, positionNDC, 0.0); + // outColor = SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, positionNDC, 0.0); + // outColor = tex2Dlod( + // _InputTextureSampler, + // float4(positionNDC, 0.0, 0.0) + // ); + + // float4 smaaOffsets[3]; + // SMAAEdgeDetectionVS( + // positionNDC, + // smaaOffsets + // ); + // float2 tmptmptmptmp = SMAAColorEdgeDetectionPS( + // positionNDC, + // smaaOffsets, + // _InputTexture, + // discarded + // ); + + // outColor = float4(0.0, 0.0, 0.0, 1.0); + // return; + + // if (true) { + // outColor.x = tmptmptmptmp.x; + // return; + // } + float c = LuminanceGamma(Load(_InputTexture, positionSS, 0, 0).rgb); float u = LuminanceGamma(Load(_InputTexture, positionSS, 0, -1).rgb); float d = LuminanceGamma(Load(_InputTexture, positionSS, 0, 1).rgb); diff --git a/Resources/G3DSMAA.shader b/Resources/G3DSMAA.shader new file mode 100644 index 0000000..2246505 --- /dev/null +++ b/Resources/G3DSMAA.shader @@ -0,0 +1,67 @@ +Shader "G3D/SMAA" { + SubShader { + PackageRequirements { + "com.unity.render-pipelines.high-definition": "unity=2021.3" + } + Tags { + "RenderType"="Opaque" + "RenderPipeline" = "HDRenderPipeline" + } + + Pass { + ZTest Always Cull Off ZWrite Off + + HLSLPROGRAM + // #include "G3DHLSLShaderBasics.hlsl" + + #pragma vertex vert + #pragma fragment frag + #pragma target 3.0 + + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + + #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) + #define SMAA_HLSL_4 + #define SMAA_PRESET_MEDIUM + #include "SMAA.hlsl" + + struct VertAttributes { + uint vertexID : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct v2f { + float2 uv : TEXCOORD0; + float4 screenPos : SV_POSITION; + float4 smaaOffsets[3] : TEXCOORD1; + }; + + Texture2D _mainTex; + SamplerState sampler_mainTex; + + v2f vert(VertAttributes input) { + v2f output; + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + output.uv = GetFullScreenTriangleTexCoord(input.vertexID); + output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + + SMAAEdgeDetectionVS(output.uv, output.smaaOffsets); + + return output; + } + + float4 frag(v2f i) : SV_Target { + float2 edges = SMAAColorEdgeDetectionPS( + i.uv, + i.smaaOffsets, + _mainTex + ); + + return float4(edges, 1.0, 1.0); + } + ENDHLSL + } + } +} diff --git a/Resources/G3DSMAA.shader.meta b/Resources/G3DSMAA.shader.meta new file mode 100644 index 0000000..2d0de00 --- /dev/null +++ b/Resources/G3DSMAA.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3ec3c43ec62784a4895040f7e22f1962 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/SMAA.hlsl b/Resources/SMAA.hlsl new file mode 100644 index 0000000..b587149 --- /dev/null +++ b/Resources/SMAA.hlsl @@ -0,0 +1,1361 @@ +/** + * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) + * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) + * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) + * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) + * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to + * do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +/** + * _______ ___ ___ ___ ___ + * / || \/ | / \ / \ + * | (---- | \ / | / ^ \ / ^ \ + * \ \ | |\/| | / /_\ \ / /_\ \ + * ----) | | | | | / _____ \ / _____ \ + * |_______/ |__| |__| /__/ \__\ /__/ \__\ + * + * E N H A N C E D + * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G + * + * http://www.iryoku.com/smaa/ + * + * Hi, welcome aboard! + * + * Here you'll find instructions to get the shader up and running as fast as + * possible. + * + * IMPORTANTE NOTICE: when updating, remember to update both this file and the + * precomputed textures! They may change from version to version. + * + * The shader has three passes, chained together as follows: + * + * |input|------------------� + * v | + * [ SMAA*EdgeDetection ] | + * v | + * |edgesTex| | + * v | + * [ SMAABlendingWeightCalculation ] | + * v | + * |blendTex| | + * v | + * [ SMAANeighborhoodBlending ] <------� + * v + * |output| + * + * Note that each [pass] has its own vertex and pixel shader. Remember to use + * oversized triangles instead of quads to avoid overshading along the + * diagonal. + * + * You've three edge detection methods to choose from: luma, color or depth. + * They represent different quality/performance and anti-aliasing/sharpness + * tradeoffs, so our recommendation is for you to choose the one that best + * suits your particular scenario: + * + * - Depth edge detection is usually the fastest but it may miss some edges. + * + * - Luma edge detection is usually more expensive than depth edge detection, + * but catches visible edges that depth edge detection can miss. + * + * - Color edge detection is usually the most expensive one but catches + * chroma-only edges. + * + * For quickstarters: just use luma edge detection. + * + * The general advice is to not rush the integration process and ensure each + * step is done correctly (don't try to integrate SMAA T2x with predicated edge + * detection from the start!). Ok then, let's go! + * + * 1. The first step is to create two RGBA temporal render targets for holding + * |edgesTex| and |blendTex|. + * + * In DX10 or DX11, you can use a RG render target for the edges texture. + * In the case of NVIDIA GPUs, using RG render targets seems to actually be + * slower. + * + * On the Xbox 360, you can use the same render target for resolving both + * |edgesTex| and |blendTex|, as they aren't needed simultaneously. + * + * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared + * each frame. Do not forget to clear the alpha channel! + * + * 3. The next step is loading the two supporting precalculated textures, + * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as + * C++ headers, and also as regular DDS files. They'll be needed for the + * 'SMAABlendingWeightCalculation' pass. + * + * If you use the C++ headers, be sure to load them in the format specified + * inside of them. + * + * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 + * respectively, if you have that option in your content processor pipeline. + * When compressing then, you get a non-perceptible quality decrease, and a + * marginal performance increase. + * + * 4. All samplers must be set to linear filtering and clamp. + * + * After you get the technique working, remember that 64-bit inputs have + * half-rate linear filtering on GCN. + * + * If SMAA is applied to 64-bit color buffers, switching to point filtering + * when accesing them will increase the performance. Search for + * 'SMAASamplePoint' to see which textures may benefit from point + * filtering, and where (which is basically the color input in the edge + * detection and resolve passes). + * + * 5. All texture reads and buffer writes must be non-sRGB, with the exception + * of the input read and the output write in + * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in + * this last pass are not possible, the technique will work anyway, but + * will perform antialiasing in gamma space. + * + * IMPORTANT: for best results the input read for the color/luma edge + * detection should *NOT* be sRGB. + * + * 6. Before including SMAA.h you'll have to setup the render target metrics, + * the target and any optional configuration defines. Optionally you can + * use a preset. + * + * You have the following targets available: + * SMAA_HLSL_3 + * SMAA_HLSL_4 + * SMAA_HLSL_4_1 + * SMAA_GLSL_3 * + * SMAA_GLSL_4 * + * + * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). + * + * And four presets: + * SMAA_PRESET_LOW (%60 of the quality) + * SMAA_PRESET_MEDIUM (%80 of the quality) + * SMAA_PRESET_HIGH (%95 of the quality) + * SMAA_PRESET_ULTRA (%99 of the quality) + * + * For example: + * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) + * #define SMAA_HLSL_4 + * #define SMAA_PRESET_HIGH + * #include "SMAA.h" + * + * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a + * uniform variable. The code is designed to minimize the impact of not + * using a constant value, but it is still better to hardcode it. + * + * Depending on how you encoded 'areaTex' and 'searchTex', you may have to + * add (and customize) the following defines before including SMAA.h: + * #define SMAA_AREATEX_SELECT(sample) sample.rg + * #define SMAA_SEARCHTEX_SELECT(sample) sample.r + * + * If your engine is already using porting macros, you can define + * SMAA_CUSTOM_SL, and define the porting functions by yourself. + * + * 7. Then, you'll have to setup the passes as indicated in the scheme above. + * You can take a look into SMAA.fx, to see how we did it for our demo. + * Checkout the function wrappers, you may want to copy-paste them! + * + * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. + * You can use a screenshot from your engine to compare the |edgesTex| + * and |blendTex| produced inside of the engine with the results obtained + * with the reference demo. + * + * 9. After you get the last pass to work, it's time to optimize. You'll have + * to initialize a stencil buffer in the first pass (discard is already in + * the code), then mask execution by using it the second pass. The last + * pass should be executed in all pixels. + * + * + * After this point you can choose to enable predicated thresholding, + * temporal supersampling and motion blur integration: + * + * a) If you want to use predicated thresholding, take a look into + * SMAA_PREDICATION; you'll need to pass an extra texture in the edge + * detection pass. + * + * b) If you want to enable temporal supersampling (SMAA T2x): + * + * 1. The first step is to render using subpixel jitters. I won't go into + * detail, but it's as simple as moving each vertex position in the + * vertex shader, you can check how we do it in our DX10 demo. + * + * 2. Then, you must setup the temporal resolve. You may want to take a look + * into SMAAResolve for resolving 2x modes. After you get it working, you'll + * probably see ghosting everywhere. But fear not, you can enable the + * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. + * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. + * + * 3. The next step is to apply SMAA to each subpixel jittered frame, just as + * done for 1x. + * + * 4. At this point you should already have something usable, but for best + * results the proper area textures must be set depending on current jitter. + * For this, the parameter 'subsampleIndices' of + * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x + * mode: + * + * @SUBSAMPLE_INDICES + * + * | S# | Camera Jitter | subsampleIndices | + * +----+------------------+---------------------+ + * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | + * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | + * + * These jitter positions assume a bottom-to-top y axis. S# stands for the + * sample number. + * + * More information about temporal supersampling here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * c) If you want to enable spatial multisampling (SMAA S2x): + * + * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be + * created with: + * - DX10: see below (*) + * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or + * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN + * + * This allows to ensure that the subsample order matches the table in + * @SUBSAMPLE_INDICES. + * + * (*) In the case of DX10, we refer the reader to: + * - SMAA::detectMSAAOrder and + * - SMAA::msaaReorder + * + * These functions allow to match the standard multisample patterns by + * detecting the subsample order for a specific GPU, and reordering + * them appropriately. + * + * 2. A shader must be run to output each subsample into a separate buffer + * (DX10 is required). You can use SMAASeparate for this purpose, or just do + * it in an existing pass (for example, in the tone mapping pass, which has + * the advantage of feeding tone mapped subsamples to SMAA, which will yield + * better results). + * + * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing + * the results in the final buffer. The second run should alpha blend with + * the existing final buffer using a blending factor of 0.5. + * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point + * b). + * + * d) If you want to enable temporal supersampling on top of SMAA S2x + * (which actually is SMAA 4x): + * + * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is + * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' + * must be set as follows: + * + * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | + * +----+----+--------------------+-------------------+----------------------+ + * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | + * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | + * +----+----+--------------------+-------------------+----------------------+ + * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | + * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | + * + * These jitter positions assume a bottom-to-top y axis. F# stands for the + * frame number. S# stands for the sample number. + * + * 2. After calculating SMAA S2x for current frame (with the new subsample + * indices), previous frame must be reprojected as in SMAA T2x mode (see + * point b). + * + * e) If motion blur is used, you may want to do the edge detection pass + * together with motion blur. This has two advantages: + * + * 1. Pixels under heavy motion can be omitted from the edge detection process. + * For these pixels we can just store "no edge", as motion blur will take + * care of them. + * 2. The center pixel tap is reused. + * + * Note that in this case depth testing should be used instead of stenciling, + * as we have to write all the pixels in the motion blur pass. + * + * That's it! + */ + +//----------------------------------------------------------------------------- +// SMAA Presets + +/** + * Note that if you use one of these presets, the following configuration + * macros will be ignored if set in the "Configurable Defines" section. + */ + +#if defined(SMAA_PRESET_LOW) +#define SMAA_THRESHOLD 0.15 +#define SMAA_MAX_SEARCH_STEPS 4 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_MEDIUM) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 8 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_HIGH) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 16 +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#define SMAA_CORNER_ROUNDING 25 +#elif defined(SMAA_PRESET_ULTRA) +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 +#endif + +//----------------------------------------------------------------------------- +// Configurable Defines + +/** + * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. + * Lowering this value you will be able to detect more edges at the expense of + * performance. + * + * Range: [0, 0.5] + * 0.1 is a reasonable value, and allows to catch most visible edges. + * 0.05 is a rather overkill value, that allows to catch 'em all. + * + * If temporal supersampling is used, 0.2 could be a reasonable value, as low + * contrast edges are properly filtered by just 2x. + */ +#ifndef SMAA_THRESHOLD +#define SMAA_THRESHOLD 0.1 +#endif + +/** + * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. + * + * Range: depends on the depth range of the scene. + */ +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + +/** + * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the + * horizontal/vertical pattern searches, at each side of the pixel. + * + * In number of pixels, it's actually the double. So the maximum line length + * perfectly handled by, for example 16, is 64 (by perfectly, we meant that + * longer lines won't look as good, but still antialiased). + * + * Range: [0, 112] + */ +#ifndef SMAA_MAX_SEARCH_STEPS +#define SMAA_MAX_SEARCH_STEPS 16 +#endif + +/** + * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the + * diagonal pattern searches, at each side of the pixel. In this case we jump + * one pixel at time, instead of two. + * + * Range: [0, 20] + * + * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 + * steps), but it can have a significant impact on older machines. + * + * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. + */ +#ifndef SMAA_MAX_SEARCH_STEPS_DIAG +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#endif + +/** + * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. + * + * Range: [0, 100] + * + * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. + */ +#ifndef SMAA_CORNER_ROUNDING +#define SMAA_CORNER_ROUNDING 25 +#endif + +/** + * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times + * bigger contrast than current edge, current edge will be discarded. + * + * This allows to eliminate spurious crossing edges, and is based on the fact + * that, if there is too much contrast in a direction, that will hide + * perceptually contrast in the other neighbors. + */ +#ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR +#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 +#endif + +/** + * Predicated thresholding allows to better preserve texture details and to + * improve performance, by decreasing the number of detected edges using an + * additional buffer like the light accumulation buffer, object ids or even the + * depth buffer (the depth buffer usage may be limited to indoor or short range + * scenes). + * + * It locally decreases the luma or color threshold if an edge is found in an + * additional buffer (so the global threshold can be higher). + * + * This method was developed by Playstation EDGE MLAA team, and used in + * Killzone 3, by using the light accumulation buffer. More information here: + * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx + */ +#ifndef SMAA_PREDICATION +#define SMAA_PREDICATION 0 +#endif + +/** + * Threshold to be used in the additional predication buffer. + * + * Range: depends on the input, so you'll have to find the magic number that + * works for you. + */ +#ifndef SMAA_PREDICATION_THRESHOLD +#define SMAA_PREDICATION_THRESHOLD 0.01 +#endif + +/** + * How much to scale the global threshold used for luma or color edge + * detection when using predication. + * + * Range: [1, 5] + */ +#ifndef SMAA_PREDICATION_SCALE +#define SMAA_PREDICATION_SCALE 2.0 +#endif + +/** + * How much to locally decrease the threshold. + * + * Range: [0, 1] + */ +#ifndef SMAA_PREDICATION_STRENGTH +#define SMAA_PREDICATION_STRENGTH 0.4 +#endif + +/** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. We use the CryEngine 3 method which also introduces + * velocity weighting. This feature is of extreme importance for totally + * removing ghosting. More information here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * Note that you'll need to setup a velocity buffer for enabling reprojection. + * For static geometry, saving the previous depth buffer is a viable + * alternative. + */ +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +/** + * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to + * remove ghosting trails behind the moving object, which are not removed by + * just using reprojection. Using low values will exhibit ghosting, while using + * high values will disable temporal supersampling under motion. + * + * Behind the scenes, velocity weighting removes temporal supersampling when + * the velocity of the subsamples differs (meaning they are different objects). + * + * Range: [0, 80] + */ +#ifndef SMAA_REPROJECTION_WEIGHT_SCALE +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 +#endif + +/** + * On some compilers, discard cannot be used in vertex shaders. Thus, they need + * to be compiled separately. + */ +#ifndef SMAA_INCLUDE_VS +#define SMAA_INCLUDE_VS 1 +#endif +#ifndef SMAA_INCLUDE_PS +#define SMAA_INCLUDE_PS 1 +#endif + +//----------------------------------------------------------------------------- +// Texture Access Defines + +#ifndef SMAA_AREATEX_SELECT +#if defined(SMAA_HLSL_3) +#define SMAA_AREATEX_SELECT(sample) sample.ra +#else +#define SMAA_AREATEX_SELECT(sample) sample.rg +#endif +#endif + +#ifndef SMAA_SEARCHTEX_SELECT +#define SMAA_SEARCHTEX_SELECT(sample) sample.r +#endif + +#ifndef SMAA_DECODE_VELOCITY +#define SMAA_DECODE_VELOCITY(sample) sample.rg +#endif + +//----------------------------------------------------------------------------- +// Non-Configurable Defines + +#define SMAA_AREATEX_MAX_DISTANCE 16 +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / float2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) +#define SMAA_SEARCHTEX_SIZE float2(66.0, 33.0) +#define SMAA_SEARCHTEX_PACKED_SIZE float2(64.0, 16.0) +#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) + +//----------------------------------------------------------------------------- +// Porting Functions + +#if defined(SMAA_HLSL_3) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) +#define SMAASample(tex, coord) tex2D(tex, coord) +#define SMAASamplePoint(tex, coord) tex2D(tex, coord) +#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#endif +#if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) +SamplerState SMAALinearClampSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +SamplerState SMAAPointClampSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +#define SMAATexture2D(tex) Texture2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(SMAALinearClampSampler, coord, 0) +#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(SMAAPointClampSampler, coord, 0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(SMAALinearClampSampler, coord, 0, offset) +#define SMAASample(tex, coord) tex.Sample(SMAALinearClampSampler, coord) +#define SMAASamplePoint(tex, coord) tex.Sample(SMAAPointClampSampler, coord) +#define SMAASampleOffset(tex, coord, offset) tex.Sample(SMAALinearClampSampler, coord, offset) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#define SMAATexture2DMS2(tex) Texture2DMS tex +#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) +#if defined(SMAA_HLSL_4_1) +#define SMAAGather(tex, coord) tex.Gather(SMAALinearClampSampler, coord, 0) +#endif +#endif +#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) +#define SMAASample(tex, coord) texture(tex, coord) +#define SMAASamplePoint(tex, coord) texture(tex, coord) +#define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) +#define SMAA_FLATTEN +#define SMAA_BRANCH +#define lerp(a, b, t) mix(a, b, t) +#define saturate(a) clamp(a, 0.0, 1.0) +#if defined(SMAA_GLSL_4) +#define mad(a, b, c) fma(a, b, c) +#define SMAAGather(tex, coord) textureGather(tex, coord) +#else +#define mad(a, b, c) (a * b + c) +#endif +#define float2 vec2 +#define float3 vec3 +#define float4 vec4 +#define int2 ivec2 +#define int3 ivec3 +#define int4 ivec4 +#define bool2 bvec2 +#define bool3 bvec3 +#define bool4 bvec4 +#endif + +#if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) +#error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL +#endif + +//----------------------------------------------------------------------------- +// Misc functions + +/** + * Gathers current pixel, and the top-left neighbors. + */ +float3 SMAAGatherNeighbours(float2 texcoord, + float4 offset[3], + SMAATexture2D(tex)) { + #ifdef SMAAGather + return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; + #else + float P = SMAASamplePoint(tex, texcoord).r; + float Pleft = SMAASamplePoint(tex, offset[0].xy).r; + float Ptop = SMAASamplePoint(tex, offset[0].zw).r; + return float3(P, Pleft, Ptop); + #endif +} + +/** + * Adjusts the threshold by means of predication. + */ +float2 SMAACalculatePredicatedThreshold(float2 texcoord, + float4 offset[3], + SMAATexture2D(predicationTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); + float2 delta = abs(neighbours.xx - neighbours.yz); + float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); + return SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges); +} + +/** + * Conditional move: + */ +void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { + SMAA_FLATTEN if (cond.x) variable.x = value.x; + SMAA_FLATTEN if (cond.y) variable.y = value.y; +} + +void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { + SMAAMovc(cond.xy, variable.xy, value.xy); + SMAAMovc(cond.zw, variable.zw, value.zw); +} + + +#if SMAA_INCLUDE_VS +//----------------------------------------------------------------------------- +// Vertex Shaders + +/** + * Edge Detection Vertex Shader + */ +void SMAAEdgeDetectionVS(float2 texcoord, + out float4 offset[3]) { + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); + offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); +} + +/** + * Blend Weight Calculation Vertex Shader + */ +void SMAABlendingWeightCalculationVS(float2 texcoord, + out float2 pixcoord, + out float4 offset[3]) { + pixcoord = texcoord * SMAA_RT_METRICS.zw; + + // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); + + // And these for the searches, they indicate the ends of the loops: + offset[2] = mad(SMAA_RT_METRICS.xxyy, + float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), + float4(offset[0].xz, offset[1].yw)); +} + +/** + * Neighborhood Blending Vertex Shader + */ +void SMAANeighborhoodBlendingVS(float2 texcoord, + out float4 offset) { + offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); +} +#endif // SMAA_INCLUDE_VS + +#if SMAA_INCLUDE_PS +//----------------------------------------------------------------------------- +// Edge Detection Pixel Shaders (First Pass) + +/** + * Luma Edge Detection + * + * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAALumaEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate lumas: + float3 weights = float3(0.2126, 0.7152, 0.0722); + float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); + + float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + + // We do the usual threshold: + float4 delta; + delta.xy = abs(L - float2(Lleft, Ltop)); + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + delta.zw = abs(L - float2(Lright, Lbottom)); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); + float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); + delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAAColorEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate color deltas: + float4 delta; + float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + + float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Depth Edge Detection + */ +float2 SMAADepthEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(depthTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); + float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); + float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); + + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + return edges; +} + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + +/** + * Allows to decode two binary values from a bilinear-filtered access. + */ +float2 SMAADecodeDiagBilinearAccess(float2 e) { + // Bilinear access for fetching 'e' have a 0.25 offset, and we are + // interested in the R and G edges: + // + // +---G---+-------+ + // | x o R x | + // +-------+-------+ + // + // Then, if one of these edge is enabled: + // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 + // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 + // + // This function will unpack the values (mad + mul + round): + // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 + e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); + return round(e); +} + +float4 SMAADecodeDiagBilinearAccess(float4 e) { + e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); + return round(e); +} + +/** + * These functions allows to perform diagonal pattern searches. + */ +float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + float4 coord = float4(texcoord, -1.0, 1.0); + coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + + // @SearchDiag2Optimization + // Fetch both edges at once using bilinear filtering: + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + e = SMAADecodeDiagBilinearAccess(e); + + // Non-optimized version: + // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; + // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; + + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { + float2 weights = float2(0.0, 0.0); + + // Search for the line ends: + float4 d; + float2 end; + if (e.r > 0.0) { + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.x += float(end.y > 0.9); + } else + d.xz = float2(0.0, 0.0); + d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } + + // Search for the line ends: + d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); + if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { + d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); + d.y += float(end.y > 0.9); + } else + d.yw = float2(0.0, 0.0); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } + + return weights; +} +#endif + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { + // The texture is flipped vertically, with left and right cases taking half + // of the space horizontally: + float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); + float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); + + // Scale and bias to access texel centers: + scale += float2(-1.0, 1.0); + bias += float2( 0.5, -0.5); + + // Convert from pixel coordinates to texcoords: + // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) + scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + + // Lookup the search texture: + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + float2 e = float2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); + return mad(SMAA_RT_METRICS.x, offset, texcoord.x); + + // Non-optimized version: + // We correct the previous (-0.25, -0.125) offset we applied: + // texcoord.x += 0.25 * SMAA_RT_METRICS.x; + + // The searches are bias by 1, so adjust the coords accordingly: + // texcoord.x += SMAA_RT_METRICS.x; + + // Disambiguate the length added by the last step: + // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step + // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); + // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); + return mad(SMAA_RT_METRICS.y, offset, texcoord.y); +} + +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; + + weights *= saturate(factor); + #endif +} + +void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; + + weights *= saturate(factor); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +float4 SMAABlendingWeightCalculationPS(float2 texcoord, + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D(areaTex), + SMAATexture2D(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float4 weights = float4(0.0, 0.0, 0.0, 0.0); + + float2 e = SMAASample(edgesTex, texcoord).rg; + + SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 + #endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + float2 d; + + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; + + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; + + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } + + return weights; +} + +//----------------------------------------------------------------------------- +// Neighborhood Blending Pixel Shader (Third Pass) + +float4 SMAANeighborhoodBlendingPS(float2 texcoord, + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + // Fetch the blending weights for current pixel: + float4 a; + a.x = SMAASample(blendTex, offset.xy).a; // Right + a.y = SMAASample(blendTex, offset.zw).g; // Top + a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left + + // Is there any blending weight with a value greater than 0.0? + SMAA_BRANCH + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); + + #if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + + #if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } +} + +//----------------------------------------------------------------------------- +// Temporal Resolve Pixel Shader (Optional Pass) + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + #if SMAA_REPROJECTION + // Velocity is assumed to be calculated for motion blur, so we need to + // inverse it for reprojection: + float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); + + // Fetch current pixel: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + + // Reproject current coordinates and fetch previous pixel: + float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); + + // Attenuate the previous pixel if the velocity is different: + float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; + float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); + + // Blend the pixels according to the calculated weight: + return lerp(current, previous, weight); + #else + // Just blend the pixels: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + float4 previous = SMAASamplePoint(previousColorTex, texcoord); + return lerp(current, previous, 0.5); + #endif +} + +//----------------------------------------------------------------------------- +// Separate Multisamples Pixel Shader (Optional Pass) + +#ifdef SMAALoad +void SMAASeparatePS(float4 position, + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { + int2 pos = int2(position.xy); + target0 = SMAALoad(colorTexMS, pos, 0); + target1 = SMAALoad(colorTexMS, pos, 1); +} +#endif + +//----------------------------------------------------------------------------- +#endif // SMAA_INCLUDE_PS diff --git a/Resources/SMAA.hlsl.meta b/Resources/SMAA.hlsl.meta new file mode 100644 index 0000000..40e1278 --- /dev/null +++ b/Resources/SMAA.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0ea38b8f79a387048ae7316766e7f473 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index bfe72da..6ccddee 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -33,6 +33,8 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass private ComputeShader fxaaCompShader; private int fxaaKernel; public bool fxaaEnabled; + + private Material smaaMaterial; protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { @@ -48,6 +50,8 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff fxaaKernel = fxaaCompShader.FindKernel("FXAA"); } + smaaMaterial = new Material(Shader.Find("G3D/SMAA")); + blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); } @@ -79,7 +83,12 @@ protected override void Execute(CustomPassContext ctx) runHoleFilling(ctx); - runFXAA(ctx); + // runFXAA(ctx); + + smaaMaterial.SetTexture(Shader.PropertyToID("_mainTex"), mosaicImageHandle); + CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTexture, ClearFlag.None); + ctx.cmd.ClearRenderTarget(true, true, Color.clear); + CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: 0); if (debugRendering) { From e7b280e69a31a1107c5d9f5a3e63681cdb7fb364 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 31 Aug 2025 14:49:02 +0200 Subject: [PATCH 059/149] rename tex --- Resources/G3DSMAA.shader | 6 +++--- Scripts/G3DHDRPViewGenerationPass.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Resources/G3DSMAA.shader b/Resources/G3DSMAA.shader index 2246505..ee7f07f 100644 --- a/Resources/G3DSMAA.shader +++ b/Resources/G3DSMAA.shader @@ -37,8 +37,8 @@ Shader "G3D/SMAA" { float4 smaaOffsets[3] : TEXCOORD1; }; - Texture2D _mainTex; - SamplerState sampler_mainTex; + Texture2D MainTex; + SamplerState SamplerMainTex; v2f vert(VertAttributes input) { v2f output; @@ -56,7 +56,7 @@ Shader "G3D/SMAA" { float2 edges = SMAAColorEdgeDetectionPS( i.uv, i.smaaOffsets, - _mainTex + MainTex ); return float4(edges, 1.0, 1.0); diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 6ccddee..4f38fbd 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -85,7 +85,7 @@ protected override void Execute(CustomPassContext ctx) // runFXAA(ctx); - smaaMaterial.SetTexture(Shader.PropertyToID("_mainTex"), mosaicImageHandle); + smaaMaterial.SetTexture(Shader.PropertyToID("MainTex"), mosaicImageHandle); CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTexture, ClearFlag.None); ctx.cmd.ClearRenderTarget(true, true, Color.clear); CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: 0); From bc3384ce4d7653b611a40d1a5d4c13d2da181815 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 31 Aug 2025 14:55:15 +0200 Subject: [PATCH 060/149] pass smaa rt metrics as uniform --- Resources/G3DSMAA.shader | 3 ++- Scripts/G3DHDRPViewGenerationPass.cs | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Resources/G3DSMAA.shader b/Resources/G3DSMAA.shader index ee7f07f..4987bc4 100644 --- a/Resources/G3DSMAA.shader +++ b/Resources/G3DSMAA.shader @@ -21,9 +21,10 @@ Shader "G3D/SMAA" { #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" - #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) #define SMAA_HLSL_4 #define SMAA_PRESET_MEDIUM + // #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) + uniform float4 SMAA_RT_METRICS; #include "SMAA.hlsl" struct VertAttributes { diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 4f38fbd..f8db9e8 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -86,6 +86,12 @@ protected override void Execute(CustomPassContext ctx) // runFXAA(ctx); smaaMaterial.SetTexture(Shader.PropertyToID("MainTex"), mosaicImageHandle); + smaaMaterial.SetVector(Shader.PropertyToID("SMAA_RT_METRICS"), new Vector4( + 1.0f / mosaicImageHandle.rt.width, + 1.0f / mosaicImageHandle.rt.height, + mosaicImageHandle.rt.width, + mosaicImageHandle.rt.height + )); CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTexture, ClearFlag.None); ctx.cmd.ClearRenderTarget(true, true, Color.clear); CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: 0); From f9340e9983d0c876790f0d1a532c18aad3076248 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 31 Aug 2025 15:44:47 +0200 Subject: [PATCH 061/149] add smaa assets --- Resources/G3DSMAA.shader | 28 +++++-- Resources/SMAA.meta | 8 ++ Resources/SMAA/AreaTex.dds | Bin 0 -> 268928 bytes Resources/SMAA/AreaTex.dds.meta | 21 +++++ Resources/{ => SMAA}/SMAA.hlsl | 0 Resources/{ => SMAA}/SMAA.hlsl.meta | 2 +- Resources/SMAA/SearchTex.dds | Bin 0 -> 1152 bytes Resources/SMAA/SearchTex.dds.meta | 21 +++++ Resources/SMAA/SearchTexPNG.png | 3 + Resources/SMAA/SearchTexPNG.png.meta | 117 +++++++++++++++++++++++++++ Scripts/G3DHDRPViewGenerationPass.cs | 3 + 11 files changed, 194 insertions(+), 9 deletions(-) create mode 100644 Resources/SMAA.meta create mode 100644 Resources/SMAA/AreaTex.dds create mode 100644 Resources/SMAA/AreaTex.dds.meta rename Resources/{ => SMAA}/SMAA.hlsl (100%) rename Resources/{ => SMAA}/SMAA.hlsl.meta (75%) create mode 100644 Resources/SMAA/SearchTex.dds create mode 100644 Resources/SMAA/SearchTex.dds.meta create mode 100644 Resources/SMAA/SearchTexPNG.png create mode 100644 Resources/SMAA/SearchTexPNG.png.meta diff --git a/Resources/G3DSMAA.shader b/Resources/G3DSMAA.shader index 4987bc4..1a16df5 100644 --- a/Resources/G3DSMAA.shader +++ b/Resources/G3DSMAA.shader @@ -25,7 +25,7 @@ Shader "G3D/SMAA" { #define SMAA_PRESET_MEDIUM // #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) uniform float4 SMAA_RT_METRICS; - #include "SMAA.hlsl" + #include "SMAA/SMAA.hlsl" struct VertAttributes { uint vertexID : SV_VertexID; @@ -39,7 +39,13 @@ Shader "G3D/SMAA" { }; Texture2D MainTex; - SamplerState SamplerMainTex; + SamplerState sampler_MainTex; + + Texture2D AreaTex; + SamplerState sampler_AreaTex; + + Texture2D SearchTex; + SamplerState sampler_SearchTex; v2f vert(VertAttributes input) { v2f output; @@ -54,13 +60,19 @@ Shader "G3D/SMAA" { } float4 frag(v2f i) : SV_Target { - float2 edges = SMAAColorEdgeDetectionPS( - i.uv, - i.smaaOffsets, - MainTex - ); + // float2 edges = SMAAColorEdgeDetectionPS( + // i.uv, + // i.smaaOffsets, + // MainTex + // ); + + // return float4(edges, 1.0, 1.0); + // return MainTex.Sample(sampler_MainTex, i.uv); + // return AreaTex.Sample(sampler_AreaTex, i.uv); + // return AreaTex.Sample(SMAALinearClampSampler, i.uv); + // return float4(i.uv, 1.0, 1.0); - return float4(edges, 1.0, 1.0); + return float4(SearchTex.Sample(SMAAPointClampSampler, i.uv).xxx, 1.0); } ENDHLSL } diff --git a/Resources/SMAA.meta b/Resources/SMAA.meta new file mode 100644 index 0000000..742ca6a --- /dev/null +++ b/Resources/SMAA.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 444b94d6a6fe9244d827e4c2628ebbee +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/SMAA/AreaTex.dds b/Resources/SMAA/AreaTex.dds new file mode 100644 index 0000000000000000000000000000000000000000..7a3b1f8d1c9b2b59fa71211faa3622d3b9dcc314 GIT binary patch literal 268928 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 literal 0 HcmV?d00001 diff --git a/Resources/SMAA/AreaTex.dds.meta b/Resources/SMAA/AreaTex.dds.meta new file mode 100644 index 0000000..590ff65 --- /dev/null +++ b/Resources/SMAA/AreaTex.dds.meta @@ -0,0 +1,21 @@ +fileFormatVersion: 2 +guid: 310f3856b7122d54294c5ac574692bcd +IHVImageFormatImporter: + externalObjects: {} + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + isReadable: 0 + sRGBTexture: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + ignoreMipmapLimit: 0 + mipmapLimitGroupName: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/SMAA.hlsl b/Resources/SMAA/SMAA.hlsl similarity index 100% rename from Resources/SMAA.hlsl rename to Resources/SMAA/SMAA.hlsl diff --git a/Resources/SMAA.hlsl.meta b/Resources/SMAA/SMAA.hlsl.meta similarity index 75% rename from Resources/SMAA.hlsl.meta rename to Resources/SMAA/SMAA.hlsl.meta index 40e1278..32a7c84 100644 --- a/Resources/SMAA.hlsl.meta +++ b/Resources/SMAA/SMAA.hlsl.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0ea38b8f79a387048ae7316766e7f473 +guid: 4cc99b61f2b669448998d06ccd6e9af6 ShaderIncludeImporter: externalObjects: {} userData: diff --git a/Resources/SMAA/SearchTex.dds b/Resources/SMAA/SearchTex.dds new file mode 100644 index 0000000000000000000000000000000000000000..f48ec89dda75f9e6f61a3265c5b75fa6ee75639e GIT binary patch literal 1152 zcmZ>930A0KU|?Vu;9w8{(hfk(zycyj2MS;0|-ETY=Bbz*vuqFj8gk4)lZ6BX`vQ5s^I1$3qm6fSNtI>B}YG8519-~ N?Wa^fna&("SMAA/AreaTex")); + // Import search tex as PNG because I can't get Unity to work with an R8 DDS file properly. + smaaMaterial.SetTexture("SearchTex", Resources.Load("SMAA/SearchTexPNG")); blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); From 85d12156f3629dfdbdea655d8575b9820f6504eb Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 31 Aug 2025 16:09:44 +0200 Subject: [PATCH 062/149] add BlendingWeightCalculation pass --- G3DCamera.cs | 15 +++++ Resources/G3DSMAA.shader | 92 +++++++++++++++++++++++++--- Scripts/G3DHDRPViewGenerationPass.cs | 22 +++++-- 3 files changed, 114 insertions(+), 15 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index fad5453..8b599cb 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -459,6 +459,21 @@ private void initCustomPass() viewGenerationPass.fillHoles = isFillingHoles; viewGenerationPass.holeFillingRadius = holeFillingRadius; viewGenerationPass.fxaaEnabled = applyFXAA; + + viewGenerationPass.smaaEdgesTex = new RenderTexture( + mainCamera.pixelWidth, + mainCamera.pixelHeight, + 0, + RenderTextureFormat.RG32, + RenderTextureReadWrite.Linear + ); + viewGenerationPass.smaaBlendTex = new RenderTexture( + mainCamera.pixelWidth, + mainCamera.pixelHeight, + 0, + RenderTextureFormat.RG32, + RenderTextureReadWrite.Linear + ); // add autostereo mosaic generation pass RenderTexture mosaicTexture = new RenderTexture( diff --git a/Resources/G3DSMAA.shader b/Resources/G3DSMAA.shader index 1a16df5..d333238 100644 --- a/Resources/G3DSMAA.shader +++ b/Resources/G3DSMAA.shader @@ -9,14 +9,15 @@ Shader "G3D/SMAA" { } Pass { - ZTest Always Cull Off ZWrite Off + Name "EdgeDetection" + ZTest Never Cull Off ZWrite Off HLSLPROGRAM // #include "G3DHLSLShaderBasics.hlsl" #pragma vertex vert #pragma fragment frag - #pragma target 3.0 + #pragma target 4.0 #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" @@ -60,19 +61,92 @@ Shader "G3D/SMAA" { } float4 frag(v2f i) : SV_Target { - // float2 edges = SMAAColorEdgeDetectionPS( - // i.uv, - // i.smaaOffsets, - // MainTex - // ); + float2 edges = SMAAColorEdgeDetectionPS( + i.uv, + i.smaaOffsets, + MainTex + ); - // return float4(edges, 1.0, 1.0); + return float4(edges, 1.0, 1.0); // return MainTex.Sample(sampler_MainTex, i.uv); // return AreaTex.Sample(sampler_AreaTex, i.uv); // return AreaTex.Sample(SMAALinearClampSampler, i.uv); // return float4(i.uv, 1.0, 1.0); - return float4(SearchTex.Sample(SMAAPointClampSampler, i.uv).xxx, 1.0); + // return float4(SearchTex.Sample(SMAAPointClampSampler, i.uv).xxx, 1.0); + } + ENDHLSL + } + + Pass { + Name "BlendingWeightCalculation" + ZTest Never Cull Off ZWrite Off + + HLSLPROGRAM + // #include "G3DHLSLShaderBasics.hlsl" + + #pragma vertex vert + #pragma fragment frag + #pragma target 4.0 + + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + + #define SMAA_HLSL_4 + #define SMAA_PRESET_MEDIUM + // #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) + uniform float4 SMAA_RT_METRICS; + #include "SMAA/SMAA.hlsl" + + struct VertAttributes { + uint vertexID : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct v2f { + float2 uv : TEXCOORD0; + float4 screenPos : SV_POSITION; + float2 smaaPixcoord : TEXCOORD1; + float4 smaaOffsets[3] : TEXCOORD2; + }; + + Texture2D edgesTex; + Texture2D areaTex; + Texture2D searchTex; + + v2f vert(VertAttributes input) { + v2f output; + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + output.uv = GetFullScreenTriangleTexCoord(input.vertexID); + output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + + SMAABlendingWeightCalculationVS(output.uv, output.smaaPixcoord, output.smaaOffsets); + + return output; + } + + float4 frag(v2f i) : SV_Target { + float4 weights = SMAABlendingWeightCalculationPS( + i.uv, + i.smaaPixcoord, + i.smaaOffsets, + edgesTex, + areaTex, + searchTex, + float4(0.0, 0.0, 0.0, 0.0) + ); + return weights; + + // float4 edges = edgesTex.Sample(SMAAPointClampSampler, float2(i.uv.x, 1.0 - i.uv.y)); + // return edges; + + // return MainTex.Sample(sampler_MainTex, i.uv); + // return AreaTex.Sample(sampler_AreaTex, i.uv); + // return AreaTex.Sample(SMAALinearClampSampler, i.uv); + // return float4(i.uv, 1.0, 1.0); + + // return float4(SearchTex.Sample(SMAAPointClampSampler, i.uv).xxx, 1.0); } ENDHLSL } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index c720362..039da79 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -20,6 +20,9 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public RenderTexture computeShaderResultTexture; public RTHandle computeShaderResultTextureHandle; + public RenderTexture smaaEdgesTex; + public RenderTexture smaaBlendTex; + public int holeFillingRadius; public bool fillHoles; @@ -51,9 +54,9 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff } smaaMaterial = new Material(Shader.Find("G3D/SMAA")); - smaaMaterial.SetTexture("AreaTex", Resources.Load("SMAA/AreaTex")); + smaaMaterial.SetTexture("areaTex", Resources.Load("SMAA/AreaTex")); // Import search tex as PNG because I can't get Unity to work with an R8 DDS file properly. - smaaMaterial.SetTexture("SearchTex", Resources.Load("SMAA/SearchTexPNG")); + smaaMaterial.SetTexture("searchTex", Resources.Load("SMAA/SearchTexPNG")); blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); @@ -88,16 +91,23 @@ protected override void Execute(CustomPassContext ctx) // runFXAA(ctx); - smaaMaterial.SetTexture(Shader.PropertyToID("MainTex"), mosaicImageHandle); smaaMaterial.SetVector(Shader.PropertyToID("SMAA_RT_METRICS"), new Vector4( 1.0f / mosaicImageHandle.rt.width, 1.0f / mosaicImageHandle.rt.height, mosaicImageHandle.rt.width, mosaicImageHandle.rt.height )); - CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTexture, ClearFlag.None); - ctx.cmd.ClearRenderTarget(true, true, Color.clear); - CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: 0); + + smaaMaterial.SetTexture(Shader.PropertyToID("MainTex"), mosaicImageHandle); + CoreUtils.SetRenderTarget(ctx.cmd, smaaEdgesTex, ClearFlag.Color); + CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("EdgeDetection")); + + smaaMaterial.SetTexture(Shader.PropertyToID("edgesTex"), smaaEdgesTex); + CoreUtils.SetRenderTarget(ctx.cmd, smaaBlendTex, ClearFlag.Color); + CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation")); + + // ctx.cmd.Blit(smaaEdgesTex, computeShaderResultTexture); + ctx.cmd.Blit(smaaBlendTex, computeShaderResultTexture); if (debugRendering) { From b00414df508480ae0e964c6b02612062c1e36e84 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 31 Aug 2025 16:17:52 +0200 Subject: [PATCH 063/149] add final blend pass --- Resources/G3DSMAA.shader | 88 ++++++++++++++++++++-------- Scripts/G3DHDRPViewGenerationPass.cs | 8 ++- 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/Resources/G3DSMAA.shader b/Resources/G3DSMAA.shader index d333238..c90795c 100644 --- a/Resources/G3DSMAA.shader +++ b/Resources/G3DSMAA.shader @@ -39,14 +39,7 @@ Shader "G3D/SMAA" { float4 smaaOffsets[3] : TEXCOORD1; }; - Texture2D MainTex; - SamplerState sampler_MainTex; - - Texture2D AreaTex; - SamplerState sampler_AreaTex; - - Texture2D SearchTex; - SamplerState sampler_SearchTex; + Texture2D ColorTex; v2f vert(VertAttributes input) { v2f output; @@ -64,16 +57,9 @@ Shader "G3D/SMAA" { float2 edges = SMAAColorEdgeDetectionPS( i.uv, i.smaaOffsets, - MainTex + ColorTex ); - - return float4(edges, 1.0, 1.0); - // return MainTex.Sample(sampler_MainTex, i.uv); - // return AreaTex.Sample(sampler_AreaTex, i.uv); - // return AreaTex.Sample(SMAALinearClampSampler, i.uv); - // return float4(i.uv, 1.0, 1.0); - - // return float4(SearchTex.Sample(SMAAPointClampSampler, i.uv).xxx, 1.0); + return float4(edges, 0.0, 0.0); } ENDHLSL } @@ -127,7 +113,7 @@ Shader "G3D/SMAA" { } float4 frag(v2f i) : SV_Target { - float4 weights = SMAABlendingWeightCalculationPS( + return SMAABlendingWeightCalculationPS( i.uv, i.smaaPixcoord, i.smaaOffsets, @@ -136,19 +122,71 @@ Shader "G3D/SMAA" { searchTex, float4(0.0, 0.0, 0.0, 0.0) ); - return weights; - // float4 edges = edgesTex.Sample(SMAAPointClampSampler, float2(i.uv.x, 1.0 - i.uv.y)); - // return edges; - - // return MainTex.Sample(sampler_MainTex, i.uv); + // return float4(i.uv, 1.0, 1.0); + // return ColorTex.Sample(sampler_MainTex, i.uv); // return AreaTex.Sample(sampler_AreaTex, i.uv); // return AreaTex.Sample(SMAALinearClampSampler, i.uv); - // return float4(i.uv, 1.0, 1.0); - // return float4(SearchTex.Sample(SMAAPointClampSampler, i.uv).xxx, 1.0); } ENDHLSL } + + Pass { + Name "NeighborhoodBlending" + ZTest Never Cull Off ZWrite Off + + HLSLPROGRAM + // #include "G3DHLSLShaderBasics.hlsl" + + #pragma vertex vert + #pragma fragment frag + #pragma target 4.0 + + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + + #define SMAA_HLSL_4 + #define SMAA_PRESET_MEDIUM + // #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) + uniform float4 SMAA_RT_METRICS; + #include "SMAA/SMAA.hlsl" + + struct VertAttributes { + uint vertexID : SV_VertexID; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct v2f { + float2 uv : TEXCOORD0; + float4 screenPos : SV_POSITION; + float4 smaaOffset : TEXCOORD1; + }; + + Texture2D ColorTex; + Texture2D blendTex; + + v2f vert(VertAttributes input) { + v2f output; + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + output.uv = GetFullScreenTriangleTexCoord(input.vertexID); + output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); + + SMAANeighborhoodBlendingVS(output.uv, output.smaaOffset); + + return output; + } + + float4 frag(v2f i) : SV_Target { + return SMAANeighborhoodBlendingPS( + i.uv, + i.smaaOffset, + ColorTex, + blendTex + ); + } + ENDHLSL + } } } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 039da79..5e6e30e 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -98,7 +98,7 @@ protected override void Execute(CustomPassContext ctx) mosaicImageHandle.rt.height )); - smaaMaterial.SetTexture(Shader.PropertyToID("MainTex"), mosaicImageHandle); + smaaMaterial.SetTexture(Shader.PropertyToID("ColorTex"), mosaicImageHandle); CoreUtils.SetRenderTarget(ctx.cmd, smaaEdgesTex, ClearFlag.Color); CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("EdgeDetection")); @@ -106,8 +106,12 @@ protected override void Execute(CustomPassContext ctx) CoreUtils.SetRenderTarget(ctx.cmd, smaaBlendTex, ClearFlag.Color); CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation")); + smaaMaterial.SetTexture(Shader.PropertyToID("blendTex"), smaaBlendTex); + CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTexture, ClearFlag.None); + CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("NeighborhoodBlending")); + // ctx.cmd.Blit(smaaEdgesTex, computeShaderResultTexture); - ctx.cmd.Blit(smaaBlendTex, computeShaderResultTexture); + // ctx.cmd.Blit(smaaBlendTex, computeShaderResultTexture); if (debugRendering) { From 0c9f50bf617f597ac50c96a90e9412da657dc699 Mon Sep 17 00:00:00 2001 From: Jim Date: Sun, 31 Aug 2025 16:29:15 +0200 Subject: [PATCH 064/149] add smaa ui toggle --- G3DCamera.cs | 2 ++ Resources/G3DCameraInspector.uxml | 1 + Scripts/G3DHDRPViewGenerationPass.cs | 48 +++++++++++++++++----------- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 8b599cb..433cf4f 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -252,6 +252,7 @@ private float scaledHalfCameraWidthAtStart private bool useVectorMapViewGeneration = false; public bool isFillingHoles; public bool applyFXAA; + public bool applySMAA; public bool debugRendering; [Range(0, 64)] @@ -459,6 +460,7 @@ private void initCustomPass() viewGenerationPass.fillHoles = isFillingHoles; viewGenerationPass.holeFillingRadius = holeFillingRadius; viewGenerationPass.fxaaEnabled = applyFXAA; + viewGenerationPass.smaaEnabled = applySMAA; viewGenerationPass.smaaEdgesTex = new RenderTexture( mainCamera.pixelWidth, diff --git a/Resources/G3DCameraInspector.uxml b/Resources/G3DCameraInspector.uxml index 2c6b61d..e8d5101 100644 --- a/Resources/G3DCameraInspector.uxml +++ b/Resources/G3DCameraInspector.uxml @@ -11,6 +11,7 @@ + diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 5e6e30e..e8bb326 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -38,6 +38,7 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass public bool fxaaEnabled; private Material smaaMaterial; + public bool smaaEnabled; protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { @@ -91,24 +92,7 @@ protected override void Execute(CustomPassContext ctx) // runFXAA(ctx); - smaaMaterial.SetVector(Shader.PropertyToID("SMAA_RT_METRICS"), new Vector4( - 1.0f / mosaicImageHandle.rt.width, - 1.0f / mosaicImageHandle.rt.height, - mosaicImageHandle.rt.width, - mosaicImageHandle.rt.height - )); - - smaaMaterial.SetTexture(Shader.PropertyToID("ColorTex"), mosaicImageHandle); - CoreUtils.SetRenderTarget(ctx.cmd, smaaEdgesTex, ClearFlag.Color); - CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("EdgeDetection")); - - smaaMaterial.SetTexture(Shader.PropertyToID("edgesTex"), smaaEdgesTex); - CoreUtils.SetRenderTarget(ctx.cmd, smaaBlendTex, ClearFlag.Color); - CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation")); - - smaaMaterial.SetTexture(Shader.PropertyToID("blendTex"), smaaBlendTex); - CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTexture, ClearFlag.None); - CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("NeighborhoodBlending")); + runSMAA(ctx); // ctx.cmd.Blit(smaaEdgesTex, computeShaderResultTexture); // ctx.cmd.Blit(smaaBlendTex, computeShaderResultTexture); @@ -266,6 +250,34 @@ private void runFXAA(CustomPassContext ctx) ); } + private void runSMAA(CustomPassContext ctx) + { + if (!smaaEnabled) + { + ctx.cmd.Blit(mosaicImageHandle, computeShaderResultTexture); + return; + } + + smaaMaterial.SetVector(Shader.PropertyToID("SMAA_RT_METRICS"), new Vector4( + 1.0f / mosaicImageHandle.rt.width, + 1.0f / mosaicImageHandle.rt.height, + mosaicImageHandle.rt.width, + mosaicImageHandle.rt.height + )); + + smaaMaterial.SetTexture(Shader.PropertyToID("ColorTex"), mosaicImageHandle); + CoreUtils.SetRenderTarget(ctx.cmd, smaaEdgesTex, ClearFlag.Color); + CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("EdgeDetection")); + + smaaMaterial.SetTexture(Shader.PropertyToID("edgesTex"), smaaEdgesTex); + CoreUtils.SetRenderTarget(ctx.cmd, smaaBlendTex, ClearFlag.Color); + CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation")); + + smaaMaterial.SetTexture(Shader.PropertyToID("blendTex"), smaaBlendTex); + CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTexture, ClearFlag.None); + CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("NeighborhoodBlending")); + } + /// /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. /// If so returns true, otherwise returns false. From 9c3b7df55146eee0c23d0369f516e886d2074655 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 1 Sep 2025 12:48:11 +0200 Subject: [PATCH 065/149] show smaa in debug render mode --- G3DCamera.cs | 3 +- Scripts/G3DHDRPViewGenerationPass.cs | 44 ++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 433cf4f..6c31edd 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -461,7 +461,7 @@ private void initCustomPass() viewGenerationPass.holeFillingRadius = holeFillingRadius; viewGenerationPass.fxaaEnabled = applyFXAA; viewGenerationPass.smaaEnabled = applySMAA; - + viewGenerationPass.smaaEdgesTex = new RenderTexture( mainCamera.pixelWidth, mainCamera.pixelHeight, @@ -977,6 +977,7 @@ void Update() #if G3D_HDRP viewGenerationPass.holeFillingRadius = holeFillingRadius; + viewGenerationPass.smaaEnabled = applySMAA; #endif } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index e8bb326..1a9287c 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -36,7 +36,7 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass private ComputeShader fxaaCompShader; private int fxaaKernel; public bool fxaaEnabled; - + private Material smaaMaterial; public bool smaaEnabled; @@ -90,7 +90,7 @@ protected override void Execute(CustomPassContext ctx) runHoleFilling(ctx); - // runFXAA(ctx); + runFXAA(ctx); runSMAA(ctx); @@ -99,7 +99,7 @@ protected override void Execute(CustomPassContext ctx) if (debugRendering) { - if (fxaaEnabled == false && fillHoles == false) + if (fxaaEnabled == false && fillHoles == false && smaaEnabled == false) { blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), mosaicImageHandle); } @@ -254,28 +254,46 @@ private void runSMAA(CustomPassContext ctx) { if (!smaaEnabled) { - ctx.cmd.Blit(mosaicImageHandle, computeShaderResultTexture); + // ctx.cmd.Blit(mosaicImageHandle, computeShaderResultTexture); return; } - smaaMaterial.SetVector(Shader.PropertyToID("SMAA_RT_METRICS"), new Vector4( - 1.0f / mosaicImageHandle.rt.width, - 1.0f / mosaicImageHandle.rt.height, - mosaicImageHandle.rt.width, - mosaicImageHandle.rt.height - )); + smaaMaterial.SetVector( + Shader.PropertyToID("SMAA_RT_METRICS"), + new Vector4( + 1.0f / mosaicImageHandle.rt.width, + 1.0f / mosaicImageHandle.rt.height, + mosaicImageHandle.rt.width, + mosaicImageHandle.rt.height + ) + ); smaaMaterial.SetTexture(Shader.PropertyToID("ColorTex"), mosaicImageHandle); CoreUtils.SetRenderTarget(ctx.cmd, smaaEdgesTex, ClearFlag.Color); - CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("EdgeDetection")); + CoreUtils.DrawFullScreen( + ctx.cmd, + smaaMaterial, + ctx.propertyBlock, + shaderPassId: smaaMaterial.FindPass("EdgeDetection") + ); smaaMaterial.SetTexture(Shader.PropertyToID("edgesTex"), smaaEdgesTex); CoreUtils.SetRenderTarget(ctx.cmd, smaaBlendTex, ClearFlag.Color); - CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation")); + CoreUtils.DrawFullScreen( + ctx.cmd, + smaaMaterial, + ctx.propertyBlock, + shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation") + ); smaaMaterial.SetTexture(Shader.PropertyToID("blendTex"), smaaBlendTex); CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTexture, ClearFlag.None); - CoreUtils.DrawFullScreen(ctx.cmd, smaaMaterial, ctx.propertyBlock, shaderPassId: smaaMaterial.FindPass("NeighborhoodBlending")); + CoreUtils.DrawFullScreen( + ctx.cmd, + smaaMaterial, + ctx.propertyBlock, + shaderPassId: smaaMaterial.FindPass("NeighborhoodBlending") + ); } /// From 5af4d7062ea0fb4f8363b931b902e546e9c5e5dd Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 1 Sep 2025 12:57:03 +0200 Subject: [PATCH 066/149] toggleable fxaa (at runtime) --- G3DCamera.cs | 1 + Scripts/G3DHDRPViewGenerationPass.cs | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 6c31edd..5bb9fb0 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -978,6 +978,7 @@ void Update() #if G3D_HDRP viewGenerationPass.holeFillingRadius = holeFillingRadius; viewGenerationPass.smaaEnabled = applySMAA; + viewGenerationPass.fxaaEnabled = applyFXAA; #endif } diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 1a9287c..6d59587 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -48,11 +48,8 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff holeFillingKernel = holeFillingCompShader.FindKernel("main"); } - if (fxaaEnabled) - { - fxaaCompShader = Resources.Load("G3DFXAA"); - fxaaKernel = fxaaCompShader.FindKernel("FXAA"); - } + fxaaCompShader = Resources.Load("G3DFXAA"); + fxaaKernel = fxaaCompShader.FindKernel("FXAA"); smaaMaterial = new Material(Shader.Find("G3D/SMAA")); smaaMaterial.SetTexture("areaTex", Resources.Load("SMAA/AreaTex")); From aa321c3b263da4ffb32e79099423a157c2b0fdbe Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 2 Sep 2025 15:20:41 +0200 Subject: [PATCH 067/149] working smaa implementation --- G3DCamera.cs | 29 +- Resources/G3DSMAA.shader | 209 +-- Resources/SMAA/AreaTex.dds | Bin 268928 -> 0 bytes Resources/SMAA/AreaTex.dds.meta | 21 - Resources/SMAA/AreaTex.tga | 3 + Resources/SMAA/AreaTex.tga.meta | 127 ++ Resources/SMAA/SMAA.hlsl | 1425 +++++++++-------- Resources/SMAA/SearchTex.dds | Bin 1152 -> 0 bytes Resources/SMAA/SearchTex.dds.meta | 21 - Resources/SMAA/SearchTex.tga | 3 + Resources/SMAA/SearchTex.tga.meta | 127 ++ ...bpixelMorphologicalAntialiasingBridge.hlsl | 122 ++ ...lMorphologicalAntialiasingBridge.hlsl.meta | 7 + Scripts/G3DHDRPViewGenerationPass.cs | 93 +- 14 files changed, 1277 insertions(+), 910 deletions(-) delete mode 100644 Resources/SMAA/AreaTex.dds delete mode 100644 Resources/SMAA/AreaTex.dds.meta create mode 100644 Resources/SMAA/AreaTex.tga create mode 100644 Resources/SMAA/AreaTex.tga.meta delete mode 100644 Resources/SMAA/SearchTex.dds delete mode 100644 Resources/SMAA/SearchTex.dds.meta create mode 100644 Resources/SMAA/SearchTex.tga create mode 100644 Resources/SMAA/SearchTex.tga.meta create mode 100644 Resources/SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl create mode 100644 Resources/SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index 5bb9fb0..1b762c9 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -460,22 +460,10 @@ private void initCustomPass() viewGenerationPass.fillHoles = isFillingHoles; viewGenerationPass.holeFillingRadius = holeFillingRadius; viewGenerationPass.fxaaEnabled = applyFXAA; - viewGenerationPass.smaaEnabled = applySMAA; - - viewGenerationPass.smaaEdgesTex = new RenderTexture( - mainCamera.pixelWidth, - mainCamera.pixelHeight, - 0, - RenderTextureFormat.RG32, - RenderTextureReadWrite.Linear - ); - viewGenerationPass.smaaBlendTex = new RenderTexture( - mainCamera.pixelWidth, - mainCamera.pixelHeight, - 0, - RenderTextureFormat.RG32, - RenderTextureReadWrite.Linear - ); + if (applySMAA) + { + viewGenerationPass.enableSMAA(mainCamera.pixelWidth, mainCamera.pixelHeight); + } // add autostereo mosaic generation pass RenderTexture mosaicTexture = new RenderTexture( @@ -977,7 +965,14 @@ void Update() #if G3D_HDRP viewGenerationPass.holeFillingRadius = holeFillingRadius; - viewGenerationPass.smaaEnabled = applySMAA; + if (applySMAA) + { + viewGenerationPass.enableSMAA(mainCamera.pixelWidth, mainCamera.pixelHeight); + } + else + { + viewGenerationPass.disableSMAA(); + } viewGenerationPass.fxaaEnabled = applyFXAA; #endif } diff --git a/Resources/G3DSMAA.shader b/Resources/G3DSMAA.shader index c90795c..f3cc984 100644 --- a/Resources/G3DSMAA.shader +++ b/Resources/G3DSMAA.shader @@ -1,191 +1,78 @@ Shader "G3D/SMAA" { + Properties + { + [HideInInspector] _StencilRef("_StencilRef", Int) = 4 + [HideInInspector] _StencilMask("_StencilMask", Int) = 4 + } + + HLSLINCLUDE + + #pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch + #pragma multi_compile_fragment SMAA_PRESET_LOW SMAA_PRESET_MEDIUM SMAA_PRESET_HIGH + #pragma editor_sync_compilation + + ENDHLSL + SubShader { PackageRequirements { "com.unity.render-pipelines.high-definition": "unity=2021.3" } Tags { - "RenderType"="Opaque" "RenderPipeline" = "HDRenderPipeline" } + Cull Off ZWrite Off ZTest Always + Pass { Name "EdgeDetection" - ZTest Never Cull Off ZWrite Off - HLSLPROGRAM - // #include "G3DHLSLShaderBasics.hlsl" - - #pragma vertex vert - #pragma fragment frag - #pragma target 4.0 - - #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" - #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" - - #define SMAA_HLSL_4 - #define SMAA_PRESET_MEDIUM - // #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) - uniform float4 SMAA_RT_METRICS; - #include "SMAA/SMAA.hlsl" - - struct VertAttributes { - uint vertexID : SV_VertexID; - UNITY_VERTEX_INPUT_INSTANCE_ID - }; - - struct v2f { - float2 uv : TEXCOORD0; - float4 screenPos : SV_POSITION; - float4 smaaOffsets[3] : TEXCOORD1; - }; - - Texture2D ColorTex; - - v2f vert(VertAttributes input) { - v2f output; - UNITY_SETUP_INSTANCE_ID(input); - UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); - output.uv = GetFullScreenTriangleTexCoord(input.vertexID); - output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); - - SMAAEdgeDetectionVS(output.uv, output.smaaOffsets); - - return output; - } - - float4 frag(v2f i) : SV_Target { - float2 edges = SMAAColorEdgeDetectionPS( - i.uv, - i.smaaOffsets, - ColorTex - ); - return float4(edges, 0.0, 0.0); + Stencil + { + WriteMask [_StencilMask] + Ref [_StencilRef] + Comp Always + Pass Replace } + + HLSLPROGRAM + + #pragma vertex VertEdge + #pragma fragment FragEdge + #include "SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl" + ENDHLSL } Pass { Name "BlendingWeightCalculation" - ZTest Never Cull Off ZWrite Off - HLSLPROGRAM - // #include "G3DHLSLShaderBasics.hlsl" - - #pragma vertex vert - #pragma fragment frag - #pragma target 4.0 - - #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" - #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" - - #define SMAA_HLSL_4 - #define SMAA_PRESET_MEDIUM - // #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) - uniform float4 SMAA_RT_METRICS; - #include "SMAA/SMAA.hlsl" - - struct VertAttributes { - uint vertexID : SV_VertexID; - UNITY_VERTEX_INPUT_INSTANCE_ID - }; - - struct v2f { - float2 uv : TEXCOORD0; - float4 screenPos : SV_POSITION; - float2 smaaPixcoord : TEXCOORD1; - float4 smaaOffsets[3] : TEXCOORD2; - }; - - Texture2D edgesTex; - Texture2D areaTex; - Texture2D searchTex; - - v2f vert(VertAttributes input) { - v2f output; - UNITY_SETUP_INSTANCE_ID(input); - UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); - output.uv = GetFullScreenTriangleTexCoord(input.vertexID); - output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); - - SMAABlendingWeightCalculationVS(output.uv, output.smaaPixcoord, output.smaaOffsets); - - return output; - } - - float4 frag(v2f i) : SV_Target { - return SMAABlendingWeightCalculationPS( - i.uv, - i.smaaPixcoord, - i.smaaOffsets, - edgesTex, - areaTex, - searchTex, - float4(0.0, 0.0, 0.0, 0.0) - ); - - // return float4(i.uv, 1.0, 1.0); - // return ColorTex.Sample(sampler_MainTex, i.uv); - // return AreaTex.Sample(sampler_AreaTex, i.uv); - // return AreaTex.Sample(SMAALinearClampSampler, i.uv); - // return float4(SearchTex.Sample(SMAAPointClampSampler, i.uv).xxx, 1.0); + Stencil + { + WriteMask[_StencilMask] + ReadMask [_StencilMask] + Ref [_StencilRef] + Comp Equal + Pass Replace } + + HLSLPROGRAM + + #pragma vertex VertBlend + #pragma fragment FragBlend + #include "SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl" + ENDHLSL } Pass { Name "NeighborhoodBlending" - ZTest Never Cull Off ZWrite Off HLSLPROGRAM - // #include "G3DHLSLShaderBasics.hlsl" - - #pragma vertex vert - #pragma fragment frag - #pragma target 4.0 - - #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" - #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" - - #define SMAA_HLSL_4 - #define SMAA_PRESET_MEDIUM - // #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) - uniform float4 SMAA_RT_METRICS; - #include "SMAA/SMAA.hlsl" - - struct VertAttributes { - uint vertexID : SV_VertexID; - UNITY_VERTEX_INPUT_INSTANCE_ID - }; - - struct v2f { - float2 uv : TEXCOORD0; - float4 screenPos : SV_POSITION; - float4 smaaOffset : TEXCOORD1; - }; - - Texture2D ColorTex; - Texture2D blendTex; - - v2f vert(VertAttributes input) { - v2f output; - UNITY_SETUP_INSTANCE_ID(input); - UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); - output.uv = GetFullScreenTriangleTexCoord(input.vertexID); - output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID); - - SMAANeighborhoodBlendingVS(output.uv, output.smaaOffset); - - return output; - } - - float4 frag(v2f i) : SV_Target { - return SMAANeighborhoodBlendingPS( - i.uv, - i.smaaOffset, - ColorTex, - blendTex - ); - } + + #pragma vertex VertNeighbor + #pragma fragment FragNeighbor + #include "SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl" + ENDHLSL } } diff --git a/Resources/SMAA/AreaTex.dds b/Resources/SMAA/AreaTex.dds deleted file mode 100644 index 7a3b1f8d1c9b2b59fa71211faa3622d3b9dcc314..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 268928 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 diff --git a/Resources/SMAA/AreaTex.dds.meta b/Resources/SMAA/AreaTex.dds.meta deleted file mode 100644 index 590ff65..0000000 --- a/Resources/SMAA/AreaTex.dds.meta +++ /dev/null @@ -1,21 +0,0 @@ -fileFormatVersion: 2 -guid: 310f3856b7122d54294c5ac574692bcd -IHVImageFormatImporter: - externalObjects: {} - textureSettings: - serializedVersion: 2 - filterMode: 1 - aniso: 1 - mipBias: 0 - wrapU: 0 - wrapV: 0 - wrapW: 0 - isReadable: 0 - sRGBTexture: 1 - streamingMipmaps: 0 - streamingMipmapsPriority: 0 - ignoreMipmapLimit: 0 - mipmapLimitGroupName: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Resources/SMAA/AreaTex.tga b/Resources/SMAA/AreaTex.tga new file mode 100644 index 0000000..bcd6c7e --- /dev/null +++ b/Resources/SMAA/AreaTex.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84779b2c0dd6ee236bccd3a2046404ffeb8fda5bf4b24857f59ec5c3bd1270cf +size 358444 diff --git a/Resources/SMAA/AreaTex.tga.meta b/Resources/SMAA/AreaTex.tga.meta new file mode 100644 index 0000000..8c6c576 --- /dev/null +++ b/Resources/SMAA/AreaTex.tga.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 29f6a64cb105bd840ad23aebd9f72496 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 0 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/SMAA/SMAA.hlsl b/Resources/SMAA/SMAA.hlsl index b587149..0133ba7 100644 --- a/Resources/SMAA/SMAA.hlsl +++ b/Resources/SMAA/SMAA.hlsl @@ -1,3 +1,5 @@ +// Ported to Unity & tweaked by Thomas Hourdel (thomas@hourdel.com) + /** * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) @@ -27,279 +29,279 @@ */ -/** - * _______ ___ ___ ___ ___ - * / || \/ | / \ / \ - * | (---- | \ / | / ^ \ / ^ \ - * \ \ | |\/| | / /_\ \ / /_\ \ - * ----) | | | | | / _____ \ / _____ \ - * |_______/ |__| |__| /__/ \__\ /__/ \__\ - * - * E N H A N C E D - * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G - * - * http://www.iryoku.com/smaa/ - * - * Hi, welcome aboard! - * - * Here you'll find instructions to get the shader up and running as fast as - * possible. - * - * IMPORTANTE NOTICE: when updating, remember to update both this file and the - * precomputed textures! They may change from version to version. - * - * The shader has three passes, chained together as follows: - * - * |input|------------------� - * v | - * [ SMAA*EdgeDetection ] | - * v | - * |edgesTex| | - * v | - * [ SMAABlendingWeightCalculation ] | - * v | - * |blendTex| | - * v | - * [ SMAANeighborhoodBlending ] <------� - * v - * |output| - * - * Note that each [pass] has its own vertex and pixel shader. Remember to use - * oversized triangles instead of quads to avoid overshading along the - * diagonal. - * - * You've three edge detection methods to choose from: luma, color or depth. - * They represent different quality/performance and anti-aliasing/sharpness - * tradeoffs, so our recommendation is for you to choose the one that best - * suits your particular scenario: - * - * - Depth edge detection is usually the fastest but it may miss some edges. - * - * - Luma edge detection is usually more expensive than depth edge detection, - * but catches visible edges that depth edge detection can miss. - * - * - Color edge detection is usually the most expensive one but catches - * chroma-only edges. - * - * For quickstarters: just use luma edge detection. - * - * The general advice is to not rush the integration process and ensure each - * step is done correctly (don't try to integrate SMAA T2x with predicated edge - * detection from the start!). Ok then, let's go! - * - * 1. The first step is to create two RGBA temporal render targets for holding - * |edgesTex| and |blendTex|. - * - * In DX10 or DX11, you can use a RG render target for the edges texture. - * In the case of NVIDIA GPUs, using RG render targets seems to actually be - * slower. - * - * On the Xbox 360, you can use the same render target for resolving both - * |edgesTex| and |blendTex|, as they aren't needed simultaneously. - * - * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared - * each frame. Do not forget to clear the alpha channel! - * - * 3. The next step is loading the two supporting precalculated textures, - * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as - * C++ headers, and also as regular DDS files. They'll be needed for the - * 'SMAABlendingWeightCalculation' pass. - * - * If you use the C++ headers, be sure to load them in the format specified - * inside of them. - * - * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 - * respectively, if you have that option in your content processor pipeline. - * When compressing then, you get a non-perceptible quality decrease, and a - * marginal performance increase. - * - * 4. All samplers must be set to linear filtering and clamp. - * - * After you get the technique working, remember that 64-bit inputs have - * half-rate linear filtering on GCN. - * - * If SMAA is applied to 64-bit color buffers, switching to point filtering - * when accesing them will increase the performance. Search for - * 'SMAASamplePoint' to see which textures may benefit from point - * filtering, and where (which is basically the color input in the edge - * detection and resolve passes). - * - * 5. All texture reads and buffer writes must be non-sRGB, with the exception - * of the input read and the output write in - * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in - * this last pass are not possible, the technique will work anyway, but - * will perform antialiasing in gamma space. - * - * IMPORTANT: for best results the input read for the color/luma edge - * detection should *NOT* be sRGB. - * - * 6. Before including SMAA.h you'll have to setup the render target metrics, - * the target and any optional configuration defines. Optionally you can - * use a preset. - * - * You have the following targets available: - * SMAA_HLSL_3 - * SMAA_HLSL_4 - * SMAA_HLSL_4_1 - * SMAA_GLSL_3 * - * SMAA_GLSL_4 * - * - * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). - * - * And four presets: - * SMAA_PRESET_LOW (%60 of the quality) - * SMAA_PRESET_MEDIUM (%80 of the quality) - * SMAA_PRESET_HIGH (%95 of the quality) - * SMAA_PRESET_ULTRA (%99 of the quality) - * - * For example: - * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) - * #define SMAA_HLSL_4 - * #define SMAA_PRESET_HIGH - * #include "SMAA.h" - * - * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a - * uniform variable. The code is designed to minimize the impact of not - * using a constant value, but it is still better to hardcode it. - * - * Depending on how you encoded 'areaTex' and 'searchTex', you may have to - * add (and customize) the following defines before including SMAA.h: - * #define SMAA_AREATEX_SELECT(sample) sample.rg - * #define SMAA_SEARCHTEX_SELECT(sample) sample.r - * - * If your engine is already using porting macros, you can define - * SMAA_CUSTOM_SL, and define the porting functions by yourself. - * - * 7. Then, you'll have to setup the passes as indicated in the scheme above. - * You can take a look into SMAA.fx, to see how we did it for our demo. - * Checkout the function wrappers, you may want to copy-paste them! - * - * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. - * You can use a screenshot from your engine to compare the |edgesTex| - * and |blendTex| produced inside of the engine with the results obtained - * with the reference demo. - * - * 9. After you get the last pass to work, it's time to optimize. You'll have - * to initialize a stencil buffer in the first pass (discard is already in - * the code), then mask execution by using it the second pass. The last - * pass should be executed in all pixels. - * - * - * After this point you can choose to enable predicated thresholding, - * temporal supersampling and motion blur integration: - * - * a) If you want to use predicated thresholding, take a look into - * SMAA_PREDICATION; you'll need to pass an extra texture in the edge - * detection pass. - * - * b) If you want to enable temporal supersampling (SMAA T2x): - * - * 1. The first step is to render using subpixel jitters. I won't go into - * detail, but it's as simple as moving each vertex position in the - * vertex shader, you can check how we do it in our DX10 demo. - * - * 2. Then, you must setup the temporal resolve. You may want to take a look - * into SMAAResolve for resolving 2x modes. After you get it working, you'll - * probably see ghosting everywhere. But fear not, you can enable the - * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. - * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. - * - * 3. The next step is to apply SMAA to each subpixel jittered frame, just as - * done for 1x. - * - * 4. At this point you should already have something usable, but for best - * results the proper area textures must be set depending on current jitter. - * For this, the parameter 'subsampleIndices' of - * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x - * mode: - * - * @SUBSAMPLE_INDICES - * - * | S# | Camera Jitter | subsampleIndices | - * +----+------------------+---------------------+ - * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | - * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | - * - * These jitter positions assume a bottom-to-top y axis. S# stands for the - * sample number. - * - * More information about temporal supersampling here: - * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf - * - * c) If you want to enable spatial multisampling (SMAA S2x): - * - * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be - * created with: - * - DX10: see below (*) - * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or - * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN - * - * This allows to ensure that the subsample order matches the table in - * @SUBSAMPLE_INDICES. - * - * (*) In the case of DX10, we refer the reader to: - * - SMAA::detectMSAAOrder and - * - SMAA::msaaReorder - * - * These functions allow to match the standard multisample patterns by - * detecting the subsample order for a specific GPU, and reordering - * them appropriately. - * - * 2. A shader must be run to output each subsample into a separate buffer - * (DX10 is required). You can use SMAASeparate for this purpose, or just do - * it in an existing pass (for example, in the tone mapping pass, which has - * the advantage of feeding tone mapped subsamples to SMAA, which will yield - * better results). - * - * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing - * the results in the final buffer. The second run should alpha blend with - * the existing final buffer using a blending factor of 0.5. - * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point - * b). - * - * d) If you want to enable temporal supersampling on top of SMAA S2x - * (which actually is SMAA 4x): - * - * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is - * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' - * must be set as follows: - * - * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | - * +----+----+--------------------+-------------------+----------------------+ - * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | - * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | - * +----+----+--------------------+-------------------+----------------------+ - * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | - * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | - * - * These jitter positions assume a bottom-to-top y axis. F# stands for the - * frame number. S# stands for the sample number. - * - * 2. After calculating SMAA S2x for current frame (with the new subsample - * indices), previous frame must be reprojected as in SMAA T2x mode (see - * point b). - * - * e) If motion blur is used, you may want to do the edge detection pass - * together with motion blur. This has two advantages: - * - * 1. Pixels under heavy motion can be omitted from the edge detection process. - * For these pixels we can just store "no edge", as motion blur will take - * care of them. - * 2. The center pixel tap is reused. - * - * Note that in this case depth testing should be used instead of stenciling, - * as we have to write all the pixels in the motion blur pass. - * - * That's it! - */ - -//----------------------------------------------------------------------------- -// SMAA Presets - -/** - * Note that if you use one of these presets, the following configuration - * macros will be ignored if set in the "Configurable Defines" section. - */ + /** + * _______ ___ ___ ___ ___ + * / || \/ | / \ / \ + * | (---- | \ / | / ^ \ / ^ \ + * \ \ | |\/| | / /_\ \ / /_\ \ + * ----) | | | | | / _____ \ / _____ \ + * |_______/ |__| |__| /__/ \__\ /__/ \__\ + * + * E N H A N C E D + * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G + * + * http://www.iryoku.com/smaa/ + * + * Hi, welcome aboard! + * + * Here you'll find instructions to get the shader up and running as fast as + * possible. + * + * IMPORTANTE NOTICE: when updating, remember to update both this file and the + * precomputed textures! They may change from version to version. + * + * The shader has three passes, chained together as follows: + * + * |input|------------------ + * v | + * [ SMAA*EdgeDetection ] | + * v | + * |edgesTex| | + * v | + * [ SMAABlendingWeightCalculation ] | + * v | + * |blendTex| | + * v | + * [ SMAANeighborhoodBlending ] <------ + * v + * |output| + * + * Note that each [pass] has its own vertex and pixel shader. Remember to use + * oversized triangles instead of quads to avoid overshading along the + * diagonal. + * + * You've three edge detection methods to choose from: luma, color or depth. + * They represent different quality/performance and anti-aliasing/sharpness + * tradeoffs, so our recommendation is for you to choose the one that best + * suits your particular scenario: + * + * - Depth edge detection is usually the fastest but it may miss some edges. + * + * - Luma edge detection is usually more expensive than depth edge detection, + * but catches visible edges that depth edge detection can miss. + * + * - Color edge detection is usually the most expensive one but catches + * chroma-only edges. + * + * For quickstarters: just use luma edge detection. + * + * The general advice is to not rush the integration process and ensure each + * step is done correctly (don't try to integrate SMAA T2x with predicated edge + * detection from the start!). Ok then, let's go! + * + * 1. The first step is to create two RGBA temporal render targets for holding + * |edgesTex| and |blendTex|. + * + * In DX10 or DX11, you can use a RG render target for the edges texture. + * In the case of NVIDIA GPUs, using RG render targets seems to actually be + * slower. + * + * On the Xbox 360, you can use the same render target for resolving both + * |edgesTex| and |blendTex|, as they aren't needed simultaneously. + * + * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared + * each frame. Do not forget to clear the alpha channel! + * + * 3. The next step is loading the two supporting precalculated textures, + * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as + * C++ headers, and also as regular DDS files. They'll be needed for the + * 'SMAABlendingWeightCalculation' pass. + * + * If you use the C++ headers, be sure to load them in the format specified + * inside of them. + * + * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 + * respectively, if you have that option in your content processor pipeline. + * When compressing then, you get a non-perceptible quality decrease, and a + * marginal performance increase. + * + * 4. All samplers must be set to linear filtering and clamp. + * + * After you get the technique working, remember that 64-bit inputs have + * half-rate linear filtering on GCN. + * + * If SMAA is applied to 64-bit color buffers, switching to point filtering + * when accesing them will increase the performance. Search for + * 'SMAASamplePoint' to see which textures may benefit from point + * filtering, and where (which is basically the color input in the edge + * detection and resolve passes). + * + * 5. All texture reads and buffer writes must be non-sRGB, with the exception + * of the input read and the output write in + * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in + * this last pass are not possible, the technique will work anyway, but + * will perform antialiasing in gamma space. + * + * IMPORTANT: for best results the input read for the color/luma edge + * detection should *NOT* be sRGB. + * + * 6. Before including SMAA.h you'll have to setup the render target metrics, + * the target and any optional configuration defines. Optionally you can + * use a preset. + * + * You have the following targets available: + * SMAA_HLSL_3 + * SMAA_HLSL_4 + * SMAA_HLSL_4_1 + * SMAA_GLSL_3 * + * SMAA_GLSL_4 * + * + * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). + * + * And four presets: + * SMAA_PRESET_LOW (%60 of the quality) + * SMAA_PRESET_MEDIUM (%80 of the quality) + * SMAA_PRESET_HIGH (%95 of the quality) + * SMAA_PRESET_ULTRA (%99 of the quality) + * + * For example: + * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) + * #define SMAA_HLSL_4 + * #define SMAA_PRESET_HIGH + * #include "SMAA.h" + * + * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a + * uniform variable. The code is designed to minimize the impact of not + * using a constant value, but it is still better to hardcode it. + * + * Depending on how you encoded 'areaTex' and 'searchTex', you may have to + * add (and customize) the following defines before including SMAA.h: + * #define SMAA_AREATEX_SELECT(sample) sample.rg + * #define SMAA_SEARCHTEX_SELECT(sample) sample.r + * + * If your engine is already using porting macros, you can define + * SMAA_CUSTOM_SL, and define the porting functions by yourself. + * + * 7. Then, you'll have to setup the passes as indicated in the scheme above. + * You can take a look into SMAA.fx, to see how we did it for our demo. + * Checkout the function wrappers, you may want to copy-paste them! + * + * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. + * You can use a screenshot from your engine to compare the |edgesTex| + * and |blendTex| produced inside of the engine with the results obtained + * with the reference demo. + * + * 9. After you get the last pass to work, it's time to optimize. You'll have + * to initialize a stencil buffer in the first pass (discard is already in + * the code), then mask execution by using it the second pass. The last + * pass should be executed in all pixels. + * + * + * After this point you can choose to enable predicated thresholding, + * temporal supersampling and motion blur integration: + * + * a) If you want to use predicated thresholding, take a look into + * SMAA_PREDICATION; you'll need to pass an extra texture in the edge + * detection pass. + * + * b) If you want to enable temporal supersampling (SMAA T2x): + * + * 1. The first step is to render using subpixel jitters. I won't go into + * detail, but it's as simple as moving each vertex position in the + * vertex shader, you can check how we do it in our DX10 demo. + * + * 2. Then, you must setup the temporal resolve. You may want to take a look + * into SMAAResolve for resolving 2x modes. After you get it working, you'll + * probably see ghosting everywhere. But fear not, you can enable the + * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. + * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. + * + * 3. The next step is to apply SMAA to each subpixel jittered frame, just as + * done for 1x. + * + * 4. At this point you should already have something usable, but for best + * results the proper area textures must be set depending on current jitter. + * For this, the parameter 'subsampleIndices' of + * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x + * mode: + * + * @SUBSAMPLE_INDICES + * + * | S# | Camera Jitter | subsampleIndices | + * +----+------------------+---------------------+ + * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | + * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | + * + * These jitter positions assume a bottom-to-top y axis. S# stands for the + * sample number. + * + * More information about temporal supersampling here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * c) If you want to enable spatial multisampling (SMAA S2x): + * + * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be + * created with: + * - DX10: see below (*) + * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or + * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN + * + * This allows to ensure that the subsample order matches the table in + * @SUBSAMPLE_INDICES. + * + * (*) In the case of DX10, we refer the reader to: + * - SMAA::detectMSAAOrder and + * - SMAA::msaaReorder + * + * These functions allow to match the standard multisample patterns by + * detecting the subsample order for a specific GPU, and reordering + * them appropriately. + * + * 2. A shader must be run to output each subsample into a separate buffer + * (DX10 is required). You can use SMAASeparate for this purpose, or just do + * it in an existing pass (for example, in the tone mapping pass, which has + * the advantage of feeding tone mapped subsamples to SMAA, which will yield + * better results). + * + * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing + * the results in the final buffer. The second run should alpha blend with + * the existing final buffer using a blending factor of 0.5. + * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point + * b). + * + * d) If you want to enable temporal supersampling on top of SMAA S2x + * (which actually is SMAA 4x): + * + * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is + * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' + * must be set as follows: + * + * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | + * +----+----+--------------------+-------------------+----------------------+ + * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | + * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | + * +----+----+--------------------+-------------------+----------------------+ + * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | + * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | + * + * These jitter positions assume a bottom-to-top y axis. F# stands for the + * frame number. S# stands for the sample number. + * + * 2. After calculating SMAA S2x for current frame (with the new subsample + * indices), previous frame must be reprojected as in SMAA T2x mode (see + * point b). + * + * e) If motion blur is used, you may want to do the edge detection pass + * together with motion blur. This has two advantages: + * + * 1. Pixels under heavy motion can be omitted from the edge detection process. + * For these pixels we can just store "no edge", as motion blur will take + * care of them. + * 2. The center pixel tap is reused. + * + * Note that in this case depth testing should be used instead of stenciling, + * as we have to write all the pixels in the motion blur pass. + * + * That's it! + */ + + //----------------------------------------------------------------------------- + // SMAA Presets + + /** + * Note that if you use one of these presets, the following configuration + * macros will be ignored if set in the "Configurable Defines" section. + */ #if defined(SMAA_PRESET_LOW) #define SMAA_THRESHOLD 0.15 @@ -323,168 +325,180 @@ #define SMAA_CORNER_ROUNDING 25 #endif -//----------------------------------------------------------------------------- -// Configurable Defines - -/** - * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. - * Lowering this value you will be able to detect more edges at the expense of - * performance. - * - * Range: [0, 0.5] - * 0.1 is a reasonable value, and allows to catch most visible edges. - * 0.05 is a rather overkill value, that allows to catch 'em all. - * - * If temporal supersampling is used, 0.2 could be a reasonable value, as low - * contrast edges are properly filtered by just 2x. - */ + //----------------------------------------------------------------------------- + // Configurable Defines + + /** + * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. + * Lowering this value you will be able to detect more edges at the expense of + * performance. + * + * Range: [0, 0.5] + * 0.1 is a reasonable value, and allows to catch most visible edges. + * 0.05 is a rather overkill value, that allows to catch 'em all. + * + * If temporal supersampling is used, 0.2 could be a reasonable value, as low + * contrast edges are properly filtered by just 2x. + */ #ifndef SMAA_THRESHOLD #define SMAA_THRESHOLD 0.1 #endif -/** - * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. - * - * Range: depends on the depth range of the scene. - */ + /** + * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. + * + * Range: depends on the depth range of the scene. + */ #ifndef SMAA_DEPTH_THRESHOLD #define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) #endif -/** - * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the - * horizontal/vertical pattern searches, at each side of the pixel. - * - * In number of pixels, it's actually the double. So the maximum line length - * perfectly handled by, for example 16, is 64 (by perfectly, we meant that - * longer lines won't look as good, but still antialiased). - * - * Range: [0, 112] - */ + /** + * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the + * horizontal/vertical pattern searches, at each side of the pixel. + * + * In number of pixels, it's actually the double. So the maximum line length + * perfectly handled by, for example 16, is 64 (by perfectly, we meant that + * longer lines won't look as good, but still antialiased). + * + * Range: [0, 112] + */ #ifndef SMAA_MAX_SEARCH_STEPS #define SMAA_MAX_SEARCH_STEPS 16 #endif -/** - * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the - * diagonal pattern searches, at each side of the pixel. In this case we jump - * one pixel at time, instead of two. - * - * Range: [0, 20] - * - * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 - * steps), but it can have a significant impact on older machines. - * - * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. - */ + /** + * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the + * diagonal pattern searches, at each side of the pixel. In this case we jump + * one pixel at time, instead of two. + * + * Range: [0, 20] + * + * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 + * steps), but it can have a significant impact on older machines. + * + * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. + */ #ifndef SMAA_MAX_SEARCH_STEPS_DIAG #define SMAA_MAX_SEARCH_STEPS_DIAG 8 #endif -/** - * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. - * - * Range: [0, 100] - * - * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. - */ + /** + * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. + * + * Range: [0, 100] + * + * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. + */ #ifndef SMAA_CORNER_ROUNDING #define SMAA_CORNER_ROUNDING 25 #endif -/** - * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times - * bigger contrast than current edge, current edge will be discarded. - * - * This allows to eliminate spurious crossing edges, and is based on the fact - * that, if there is too much contrast in a direction, that will hide - * perceptually contrast in the other neighbors. - */ + /** + * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times + * bigger contrast than current edge, current edge will be discarded. + * + * This allows to eliminate spurious crossing edges, and is based on the fact + * that, if there is too much contrast in a direction, that will hide + * perceptually contrast in the other neighbors. + */ #ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR #define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 #endif -/** - * Predicated thresholding allows to better preserve texture details and to - * improve performance, by decreasing the number of detected edges using an - * additional buffer like the light accumulation buffer, object ids or even the - * depth buffer (the depth buffer usage may be limited to indoor or short range - * scenes). - * - * It locally decreases the luma or color threshold if an edge is found in an - * additional buffer (so the global threshold can be higher). - * - * This method was developed by Playstation EDGE MLAA team, and used in - * Killzone 3, by using the light accumulation buffer. More information here: - * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx - */ + /** + * Predicated thresholding allows to better preserve texture details and to + * improve performance, by decreasing the number of detected edges using an + * additional buffer like the light accumulation buffer, object ids or even the + * depth buffer (the depth buffer usage may be limited to indoor or short range + * scenes). + * + * It locally decreases the luma or color threshold if an edge is found in an + * additional buffer (so the global threshold can be higher). + * + * This method was developed by Playstation EDGE MLAA team, and used in + * Killzone 3, by using the light accumulation buffer. More information here: + * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx + */ #ifndef SMAA_PREDICATION #define SMAA_PREDICATION 0 #endif -/** - * Threshold to be used in the additional predication buffer. - * - * Range: depends on the input, so you'll have to find the magic number that - * works for you. - */ + /** + * Threshold to be used in the additional predication buffer. + * + * Range: depends on the input, so you'll have to find the magic number that + * works for you. + */ #ifndef SMAA_PREDICATION_THRESHOLD #define SMAA_PREDICATION_THRESHOLD 0.01 #endif -/** - * How much to scale the global threshold used for luma or color edge - * detection when using predication. - * - * Range: [1, 5] - */ + /** + * How much to scale the global threshold used for luma or color edge + * detection when using predication. + * + * Range: [1, 5] + */ #ifndef SMAA_PREDICATION_SCALE #define SMAA_PREDICATION_SCALE 2.0 #endif -/** - * How much to locally decrease the threshold. - * - * Range: [0, 1] - */ + /** + * How much to locally decrease the threshold. + * + * Range: [0, 1] + */ #ifndef SMAA_PREDICATION_STRENGTH #define SMAA_PREDICATION_STRENGTH 0.4 #endif -/** - * Temporal reprojection allows to remove ghosting artifacts when using - * temporal supersampling. We use the CryEngine 3 method which also introduces - * velocity weighting. This feature is of extreme importance for totally - * removing ghosting. More information here: - * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf - * - * Note that you'll need to setup a velocity buffer for enabling reprojection. - * For static geometry, saving the previous depth buffer is a viable - * alternative. - */ + /** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. We use the CryEngine 3 method which also introduces + * velocity weighting. This feature is of extreme importance for totally + * removing ghosting. More information here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * Note that you'll need to setup a velocity buffer for enabling reprojection. + * For static geometry, saving the previous depth buffer is a viable + * alternative. + */ #ifndef SMAA_REPROJECTION #define SMAA_REPROJECTION 0 #endif -/** - * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to - * remove ghosting trails behind the moving object, which are not removed by - * just using reprojection. Using low values will exhibit ghosting, while using - * high values will disable temporal supersampling under motion. - * - * Behind the scenes, velocity weighting removes temporal supersampling when - * the velocity of the subsamples differs (meaning they are different objects). - * - * Range: [0, 80] - */ + /** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. However, the default reprojection requires a velocity buffer + * in order to function properly. + * + * A velocity buffer might not always be available (hi Unity 5!). To handle such cases + * we provide a UV-based approximation for calculating motion vectors on the fly. + */ +#ifndef SMAA_UV_BASED_REPROJECTION +#define SMAA_UV_BASED_REPROJECTION 0 +#endif + + /** + * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to + * remove ghosting trails behind the moving object, which are not removed by + * just using reprojection. Using low values will exhibit ghosting, while using + * high values will disable temporal supersampling under motion. + * + * Behind the scenes, velocity weighting removes temporal supersampling when + * the velocity of the subsamples differs (meaning they are different objects). + * + * Range: [0, 80] + */ #ifndef SMAA_REPROJECTION_WEIGHT_SCALE #define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 #endif -/** - * On some compilers, discard cannot be used in vertex shaders. Thus, they need - * to be compiled separately. - */ + /** + * On some compilers, discard cannot be used in vertex shaders. Thus, they need + * to be compiled separately. + */ #ifndef SMAA_INCLUDE_VS #define SMAA_INCLUDE_VS 1 #endif @@ -492,8 +506,8 @@ #define SMAA_INCLUDE_PS 1 #endif -//----------------------------------------------------------------------------- -// Texture Access Defines + //----------------------------------------------------------------------------- + // Texture Access Defines #ifndef SMAA_AREATEX_SELECT #if defined(SMAA_HLSL_3) @@ -534,27 +548,28 @@ #define SMAASample(tex, coord) tex2D(tex, coord) #define SMAASamplePoint(tex, coord) tex2D(tex, coord) #define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) -#define SMAA_FLATTEN [flatten] -#define SMAA_BRANCH [branch] +//#define SMAA_FLATTEN [flatten] +//#define SMAA_BRANCH [branch] +#define SMAA_FLATTEN +#define SMAA_BRANCH #endif #if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) -SamplerState SMAALinearClampSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; -SamplerState SMAAPointClampSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; -#define SMAATexture2D(tex) Texture2D tex +//SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +//SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +#define SMAATexture2D(tex) TEXTURE2D(tex) +#define SMAATexture2D_Non_Array(tex) Texture2D tex #define SMAATexturePass2D(tex) tex -#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(SMAALinearClampSampler, coord, 0) -#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(SMAAPointClampSampler, coord, 0) -#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(SMAALinearClampSampler, coord, 0, offset) -#define SMAASample(tex, coord) tex.Sample(SMAALinearClampSampler, coord) -#define SMAASamplePoint(tex, coord) tex.Sample(SMAAPointClampSampler, coord) -#define SMAASampleOffset(tex, coord, offset) tex.Sample(SMAALinearClampSampler, coord, offset) +#define SMAASampleLevelZero(tex, coord) SAMPLE_TEXTURE2D_X_LOD(tex, LinearSampler, ClampAndScaleUVForBilinear(coord), 0) +#define SMAASampleLevelZeroNoRescale(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) +#define SMAASampleLevelZeroPoint(tex, coord) SAMPLE_TEXTURE2D_X_LOD(tex, PointSampler, ClampAndScaleUVForPoint(coord), 0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) SAMPLE_TEXTURE2D_X_LOD(tex, LinearSampler, ClampAndScaleUVForBilinear(coord + offset * SMAA_RT_METRICS.xy), 0) +#define SMAASample(tex, coord) SAMPLE_TEXTURE2D_X(tex, LinearSampler, ClampAndScaleUVForBilinear(coord)) +#define SMAASamplePoint(tex, coord) SAMPLE_TEXTURE2D_X(tex, PointSampler, ClampAndScaleUVForPoint(coord)) +#define SMAASampleOffset(tex, coord, offset) SAMPLE_TEXTURE2D_X(tex, LinearSampler, ClampAndScaleUVForBilinear(coord + offset * SMAA_RT_METRICS.xy)) #define SMAA_FLATTEN [flatten] #define SMAA_BRANCH [branch] #define SMAATexture2DMS2(tex) Texture2DMS tex #define SMAALoad(tex, pos, sample) tex.Load(pos, sample) -#if defined(SMAA_HLSL_4_1) -#define SMAAGather(tex, coord) tex.Gather(SMAALinearClampSampler, coord, 0) -#endif #endif #if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) #define SMAATexture2D(tex) sampler2D tex @@ -597,24 +612,24 @@ SamplerState SMAAPointClampSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clam * Gathers current pixel, and the top-left neighbors. */ float3 SMAAGatherNeighbours(float2 texcoord, - float4 offset[3], - SMAATexture2D(tex)) { - #ifdef SMAAGather + float4 offset[3], + SMAATexture2D(tex)) { +#ifdef SMAAGather return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; - #else +#else float P = SMAASamplePoint(tex, texcoord).r; float Pleft = SMAASamplePoint(tex, offset[0].xy).r; - float Ptop = SMAASamplePoint(tex, offset[0].zw).r; + float Ptop = SMAASamplePoint(tex, offset[0].zw).r; return float3(P, Pleft, Ptop); - #endif +#endif } /** * Adjusts the threshold by means of predication. */ float2 SMAACalculatePredicatedThreshold(float2 texcoord, - float4 offset[3], - SMAATexture2D(predicationTex)) { + float4 offset[3], + SMAATexture2D(predicationTex)) { float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); float2 delta = abs(neighbours.xx - neighbours.yz); float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); @@ -643,9 +658,9 @@ void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { * Edge Detection Vertex Shader */ void SMAAEdgeDetectionVS(float2 texcoord, - out float4 offset[3]) { + out float4 offset[3]) { offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); - offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(1.0, 0.0, 0.0, 1.0), texcoord.xyxy); offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); } @@ -653,26 +668,26 @@ void SMAAEdgeDetectionVS(float2 texcoord, * Blend Weight Calculation Vertex Shader */ void SMAABlendingWeightCalculationVS(float2 texcoord, - out float2 pixcoord, - out float4 offset[3]) { + out float2 pixcoord, + out float4 offset[3]) { pixcoord = texcoord * SMAA_RT_METRICS.zw; // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): - offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); - offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); // And these for the searches, they indicate the ends of the loops: offset[2] = mad(SMAA_RT_METRICS.xxyy, - float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), - float4(offset[0].xz, offset[1].yw)); + float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), + float4(offset[0].xz, offset[1].yw)); } /** * Neighborhood Blending Vertex Shader */ void SMAANeighborhoodBlendingVS(float2 texcoord, - out float4 offset) { - offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); + out float4 offset) { + offset = mad(SMAA_RT_METRICS.xyxy, float4(1.0, 0.0, 0.0, 1.0), texcoord.xyxy); } #endif // SMAA_INCLUDE_VS @@ -687,25 +702,25 @@ void SMAANeighborhoodBlendingVS(float2 texcoord, * thus 'colorTex' should be a non-sRGB texture. */ float2 SMAALumaEdgeDetectionPS(float2 texcoord, - float4 offset[3], - SMAATexture2D(colorTex) - #if SMAA_PREDICATION - , SMAATexture2D(predicationTex) - #endif - ) { + float4 offset[3], + SMAATexture2D(colorTex) +#if SMAA_PREDICATION + , SMAATexture2D(predicationTex) +#endif +) { // Calculate the threshold: - #if SMAA_PREDICATION +#if SMAA_PREDICATION float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); - #else +#else float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); - #endif +#endif // Calculate lumas: float3 weights = float3(0.2126, 0.7152, 0.0722); float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); - float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); // We do the usual threshold: float4 delta; @@ -718,7 +733,7 @@ float2 SMAALumaEdgeDetectionPS(float2 texcoord, // Calculate right and bottom deltas: float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); - float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); delta.zw = abs(L - float2(Lright, Lbottom)); // Calculate the maximum delta in the direct neighborhood: @@ -734,7 +749,9 @@ float2 SMAALumaEdgeDetectionPS(float2 texcoord, float finalDelta = max(maxDelta.x, maxDelta.y); // Local contrast adaptation: +#if !defined(SHADER_API_OPENGL) edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); +#endif return edges; } @@ -746,28 +763,28 @@ float2 SMAALumaEdgeDetectionPS(float2 texcoord, * thus 'colorTex' should be a non-sRGB texture. */ float2 SMAAColorEdgeDetectionPS(float2 texcoord, - float4 offset[3], - SMAATexture2D(colorTex) - #if SMAA_PREDICATION - , SMAATexture2D(predicationTex) - #endif - ) { + float4 offset[3], + SMAATexture2D(colorTex) +#if SMAA_PREDICATION + , SMAATexture2D(predicationTex) +#endif +) { // Calculate the threshold: - #if SMAA_PREDICATION +#if SMAA_PREDICATION float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); - #else +#else float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); - #endif +#endif // Calculate color deltas: float4 delta; - float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + float3 C = PositivePow(SMAASamplePoint(colorTex, texcoord).rgb, GAMMA_FOR_EDGE_DETECTION); - float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 Cleft = PositivePow(SMAASamplePoint(colorTex, offset[0].xy).rgb, GAMMA_FOR_EDGE_DETECTION); float3 t = abs(C - Cleft); delta.x = max(max(t.r, t.g), t.b); - float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + float3 Ctop = PositivePow(SMAASamplePoint(colorTex, offset[0].zw).rgb, GAMMA_FOR_EDGE_DETECTION); t = abs(C - Ctop); delta.y = max(max(t.r, t.g), t.b); @@ -779,11 +796,11 @@ float2 SMAAColorEdgeDetectionPS(float2 texcoord, discard; // Calculate right and bottom deltas: - float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + float3 Cright = PositivePow(SMAASamplePoint(colorTex, offset[1].xy).rgb, GAMMA_FOR_EDGE_DETECTION); t = abs(C - Cright); delta.z = max(max(t.r, t.g), t.b); - float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + float3 Cbottom = PositivePow(SMAASamplePoint(colorTex, offset[1].zw).rgb, GAMMA_FOR_EDGE_DETECTION); t = abs(C - Cbottom); delta.w = max(max(t.r, t.g), t.b); @@ -791,12 +808,12 @@ float2 SMAAColorEdgeDetectionPS(float2 texcoord, float2 maxDelta = max(delta.xy, delta.zw); // Calculate left-left and top-top deltas: - float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; - t = abs(C - Cleftleft); + float3 Cleftleft = PositivePow(SMAASamplePoint(colorTex, offset[2].xy).rgb, GAMMA_FOR_EDGE_DETECTION); + t = abs(Cleft - Cleftleft); delta.z = max(max(t.r, t.g), t.b); - float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; - t = abs(C - Ctoptop); + float3 Ctoptop = PositivePow(SMAASamplePoint(colorTex, offset[2].zw).rgb, GAMMA_FOR_EDGE_DETECTION); + t = abs(Ctop - Ctoptop); delta.w = max(max(t.r, t.g), t.b); // Calculate the final maximum delta: @@ -804,7 +821,9 @@ float2 SMAAColorEdgeDetectionPS(float2 texcoord, float finalDelta = max(maxDelta.x, maxDelta.y); // Local contrast adaptation: +#if !defined(SHADER_API_OPENGL) edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); +#endif return edges; } @@ -813,8 +832,8 @@ float2 SMAAColorEdgeDetectionPS(float2 texcoord, * Depth Edge Detection */ float2 SMAADepthEdgeDetectionPS(float2 texcoord, - float4 offset[3], - SMAATexture2D(depthTex)) { + float4 offset[3], + SMAATexture2D(depthTex)) { float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); @@ -863,7 +882,7 @@ float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float4 coord = float4(texcoord, -1.0, 1.0); float3 t = float3(SMAA_RT_METRICS.xy, 1.0); while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && - coord.w > 0.9) { + coord.w > 0.9) { coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); e = SMAASampleLevelZero(edgesTex, coord.xy).rg; coord.w = dot(e, float2(0.5, 0.5)); @@ -876,7 +895,7 @@ float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization float3 t = float3(SMAA_RT_METRICS.xy, 1.0); while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && - coord.w > 0.9) { + coord.w > 0.9) { coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); // @SearchDiag2Optimization @@ -893,11 +912,11 @@ float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out return coord.zw; } -/** +/** * Similar to SMAAArea, this calculates the area corresponding to a certain * diagonal distance and crossing edges 'e'. */ -float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { +float2 SMAAAreaDiag(SMAATexture2D_Non_Array(areaTex), float2 dist, float2 e, float offset) { float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); // We do a scale and bias for mapping to texel space: @@ -910,76 +929,78 @@ float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; // Do it! - return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); + return SMAA_AREATEX_SELECT(SMAASampleLevelZeroNoRescale(areaTex, texcoord)); } /** * This searches for diagonal patterns and returns the corresponding weights. */ -float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D_Non_Array(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { float2 weights = float2(0.0, 0.0); // Search for the line ends: float4 d; float2 end; if (e.r > 0.0) { - d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); d.x += float(end.y > 0.9); - } else + } + else d.xz = float2(0.0, 0.0); d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); SMAA_BRANCH - if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 - // Fetch the crossing edges: - float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); - float4 c; - c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; - c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; - c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); - - // Non-optimized version: - // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); - // float4 c; - // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; - // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; - // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; - // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; - - // Merge crossing edges at each side into a single value: - float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); - - // Remove the crossing edge if we didn't found the end of the line: - SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); - - // Fetch the areas for this line: - weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); - } + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2(1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(float2(0.9, 0.9), d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } // Search for the line ends: d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); d.y += float(end.y > 0.9); - } else + } + else d.yw = float2(0.0, 0.0); SMAA_BRANCH - if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 - // Fetch the crossing edges: - float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); - float4 c; - c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; - c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; - c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; - float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); - - // Remove the crossing edge if we didn't found the end of the line: - SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); - - // Fetch the areas for this line: - weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; - } + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(0, -1)).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2(1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(float2(0.9, 0.9), d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } return weights; } @@ -990,19 +1011,19 @@ float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), /** * This allows to determine how much length should we add in the last step - * of the searches. It takes the bilinearly interpolated edge (see + * of the searches. It takes the bilinearly interpolated edge (see * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and * crossing edges are active. */ -float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { +float SMAASearchLength(SMAATexture2D_Non_Array(searchTex), float2 e, float offset) { // The texture is flipped vertically, with left and right cases taking half // of the space horizontally: float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); // Scale and bias to access texel centers: - scale += float2(-1.0, 1.0); - bias += float2( 0.5, -0.5); + scale += float2(-1.0, 1.0); + bias += float2(0.5, -0.5); // Convert from pixel coordinates to texcoords: // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) @@ -1010,13 +1031,13 @@ float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; // Lookup the search texture: - return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZeroNoRescale(searchTex, mad(scale, e, bias))); } /** * Horizontal/vertical search functions for the 2nd pass. */ -float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D_Non_Array(searchTex), float2 texcoord, float end) { /** * @PSEUDO_GATHER4 * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to @@ -1025,9 +1046,9 @@ float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 * which edges are active from the four fetched ones. */ float2 e = float2(0.0, 1.0); - while (texcoord.x > end && - e.g > 0.8281 && // Is there some edge not activated? - e.r == 0.0) { // Or is there a crossing edge that breaks the line? + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? e = SMAASampleLevelZero(edgesTex, texcoord).rg; texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); } @@ -1048,11 +1069,11 @@ float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); } -float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D_Non_Array(searchTex), float2 texcoord, float end) { float2 e = float2(0.0, 1.0); - while (texcoord.x < end && - e.g > 0.8281 && // Is there some edge not activated? - e.r == 0.0) { // Or is there a crossing edge that breaks the line? + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? e = SMAASampleLevelZero(edgesTex, texcoord).rg; texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); } @@ -1060,11 +1081,11 @@ float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); } -float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D_Non_Array(searchTex), float2 texcoord, float end) { float2 e = float2(1.0, 0.0); - while (texcoord.y > end && - e.r > 0.8281 && // Is there some edge not activated? - e.g == 0.0) { // Or is there a crossing edge that breaks the line? + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? e = SMAASampleLevelZero(edgesTex, texcoord).rg; texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); } @@ -1072,11 +1093,11 @@ float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 te return mad(SMAA_RT_METRICS.y, offset, texcoord.y); } -float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D_Non_Array(searchTex), float2 texcoord, float end) { float2 e = float2(1.0, 0.0); - while (texcoord.y < end && - e.r > 0.8281 && // Is there some edge not activated? - e.g == 0.0) { // Or is there a crossing edge that breaks the line? + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? e = SMAASampleLevelZero(edgesTex, texcoord).rg; texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); } @@ -1084,14 +1105,14 @@ float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); } -/** +/** * Ok, we have the distance and both crossing edges. So, what are the areas * at each side of current edge? */ -float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { +float2 SMAAArea(SMAATexture2D_Non_Array(areaTex), float2 dist, float e1, float e2, float offset) { // Rounding prevents precision errors of bilinear filtering: float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); - + // We do a scale and bias for mapping to texel space: texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); @@ -1099,164 +1120,187 @@ float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float o texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); // Do it! - return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); + return SMAA_AREATEX_SELECT(SMAASampleLevelZeroNoRescale(areaTex, texcoord)); } //----------------------------------------------------------------------------- // Corner Detection Functions void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { - #if !defined(SMAA_DISABLE_CORNER_DETECTION) +#if !defined(SMAA_DISABLE_CORNER_DETECTION) float2 leftRight = step(d.xy, d.yx); float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. float2 factor = float2(1.0, 1.0); - factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; - factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; weights *= saturate(factor); - #endif +#endif } void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { - #if !defined(SMAA_DISABLE_CORNER_DETECTION) +#if !defined(SMAA_DISABLE_CORNER_DETECTION) float2 leftRight = step(d.xy, d.yx); float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; rounding /= leftRight.x + leftRight.y; float2 factor = float2(1.0, 1.0); - factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; - factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).g; factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; weights *= saturate(factor); - #endif +#endif } + //----------------------------------------------------------------------------- // Blending Weight Calculation Pixel Shader (Second Pass) float4 SMAABlendingWeightCalculationPS(float2 texcoord, - float2 pixcoord, - float4 offset[3], - SMAATexture2D(edgesTex), - SMAATexture2D(areaTex), - SMAATexture2D(searchTex), - float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D_Non_Array(areaTex), + SMAATexture2D_Non_Array(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. float4 weights = float4(0.0, 0.0, 0.0, 0.0); float2 e = SMAASample(edgesTex, texcoord).rg; SMAA_BRANCH - if (e.g > 0.0) { // Edge at north - #if !defined(SMAA_DISABLE_DIAG_DETECTION) - // Diagonals have both north and west edges, so searching for them in - // one of the boundaries is enough. - weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); - - // We give priority to diagonals, so if we find a diagonal we skip - // horizontal/vertical processing. - SMAA_BRANCH - if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 - #endif - - float2 d; - - // Find the distance to the left: - float3 coords; - coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); - coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) - d.x = coords.x; - - // Now fetch the left crossing edges, two at a time using bilinear - // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to - // discern what value each edge has: - float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; - - // Find the distance to the right: - coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); - d.y = coords.z; - - // We want the distances to be in pixel units (doing this here allow to - // better interleave arithmetic and memory accesses): - d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); - - // SMAAArea below needs a sqrt, as the areas texture is compressed - // quadratically: - float2 sqrt_d = sqrt(d); - - // Fetch the right crossing edges: - float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; - - // Ok, we know how this pattern looks like, now it is time for getting - // the actual area: - weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); - - // Fix corners: - coords.y = texcoord.y; - SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); - - #if !defined(SMAA_DISABLE_DIAG_DETECTION) - } else - e.r = 0.0; // Skip vertical processing. - #endif - } + if (e.g > 0.0) { // Edge at north +#if !defined(SMAA_DISABLE_DIAG_DETECTION) +// Diagonals have both north and west edges, so searching for them in +// one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 +#endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + } + else + e.r = 0.0; // Skip vertical processing. +#endif + } SMAA_BRANCH - if (e.r > 0.0) { // Edge at west - float2 d; + if (e.r > 0.0) { // Edge at west + float2 d; - // Find the distance to the top: - float3 coords; - coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); - coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; - d.x = coords.y; + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; - // Fetch the top crossing edges: - float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; - // Find the distance to the bottom: - coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); - d.y = coords.z; + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; - // We want the distances to be in pixel units: - d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); - // SMAAArea below needs a sqrt, as the areas texture is compressed - // quadratically: - float2 sqrt_d = sqrt(d); + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); - // Fetch the bottom crossing edges: - float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; - // Get the area for this direction: - weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); - // Fix corners: - coords.x = texcoord.x; - SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); - } + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } return weights; } +//----------------------------------------------------------------------------- +// UV-based reprojection functions + +#if SMAA_UV_BASED_REPROJECTION +float2 SMAAReproject(float2 texcoord) +{ + // UV to clip-position: + // -- This must be sampled at exactly mip 0 due to possible gradient divergence + // -- as this function is called within a control flow block down below. + float depth = SMAASampleLevelZero(_CameraDepthTexture, texcoord).r; + float3 clipPosition = float3(2. * texcoord - 1., depth); + + // Reproject + float4 previousClipPosition = mul(_ReprojectionMatrix, float4(clipPosition, 1.)); + previousClipPosition.xyz /= previousClipPosition.w; + + // Clip-position to UV + return (.5 * previousClipPosition.xy + .5); +} +#endif + //----------------------------------------------------------------------------- // Neighborhood Blending Pixel Shader (Third Pass) float4 SMAANeighborhoodBlendingPS(float2 texcoord, - float4 offset, - SMAATexture2D(colorTex), - SMAATexture2D(blendTex) - #if SMAA_REPROJECTION - , SMAATexture2D(velocityTex) - #endif - ) { + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) +#if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) +#endif +) { // Fetch the blending weights for current pixel: float4 a; a.x = SMAASample(blendTex, offset.xy).a; // Right @@ -1265,63 +1309,78 @@ float4 SMAANeighborhoodBlendingPS(float2 texcoord, // Is there any blending weight with a value greater than 0.0? SMAA_BRANCH - if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { - float4 color = SMAASampleLevelZero(colorTex, texcoord); - - #if SMAA_REPROJECTION - float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); - - // Pack velocity into the alpha channel: - color.a = sqrt(5.0 * length(velocity)); - #endif + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); - return color; - } else { - bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) - - // Calculate the blending offsets: - float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); - float2 blendingWeight = a.yw; - SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); - SMAAMovc(bool2(h, h), blendingWeight, a.xz); - blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); - - // Calculate the texture coordinates: - float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); +#if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); +#elif SMAA_UV_BASED_REPROJECTION + float2 velocity = texcoord - SMAAReproject(texcoord); +#endif - // We exploit bilinear filtering to mix current pixel with the chosen - // neighbor: - float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); - color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); +#if (SMAA_REPROJECTION || SMAA_UV_BASED_REPROJECTION) + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); +#endif - #if SMAA_REPROJECTION - // Antialias velocity for proper reprojection in a later stage: - float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); - velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + return color; + } + else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + +#if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); +#elif SMAA_UV_BASED_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * (blendingCoord.xy - SMAAReproject(blendingCoord.xy)); + velocity += blendingWeight.y * (blendingCoord.zw - SMAAReproject(blendingCoord.zw)); +#endif - // Pack velocity into the alpha channel: - color.a = sqrt(5.0 * length(velocity)); - #endif +#if (SMAA_REPROJECTION || SMAA_UV_BASED_REPROJECTION) + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); +#endif - return color; - } + return color; + } } //----------------------------------------------------------------------------- // Temporal Resolve Pixel Shader (Optional Pass) float4 SMAAResolvePS(float2 texcoord, - SMAATexture2D(currentColorTex), - SMAATexture2D(previousColorTex) - #if SMAA_REPROJECTION - , SMAATexture2D(velocityTex) - #endif - ) { - #if SMAA_REPROJECTION + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) +#if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) +#endif +) { +#if SMAA_REPROJECTION // Velocity is assumed to be calculated for motion blur, so we need to // inverse it for reprojection: float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); +#elif SMAA_UV_BASED_REPROJECTION + float2 velocity = SMAAReproject(texcoord) - texcoord; +#endif +#if (SMAA_REPROJECTION || SMAA_UV_BASED_REPROJECTION) // Fetch current pixel: float4 current = SMAASamplePoint(currentColorTex, texcoord); @@ -1333,13 +1392,31 @@ float4 SMAAResolvePS(float2 texcoord, float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); // Blend the pixels according to the calculated weight: - return lerp(current, previous, weight); - #else + // return lerp(current, previous, weight); + + // Neighbour clamp + // Contributed by pommak + float4 n0 = SMAASampleOffset(currentColorTex, texcoord, float2(-1, -1)); + float4 n1 = SMAASampleOffset(currentColorTex, texcoord, float2(+1, -1)); + float4 n2 = SMAASampleOffset(currentColorTex, texcoord, float2(-1, +1)); + float4 n3 = SMAASampleOffset(currentColorTex, texcoord, float2(+1, +1)); + float4 cmax = max(n0, max(n1, max(n2, n3))); + float4 cmin = min(n0, min(n1, min(n2, n3))); + float4 avg = 0.25 * (n0 + n1 + n2 + n3); + float4 wk = abs(avg - current); + float blend = saturate(lerp(0.35, 0.85, wk)); + + // Clamp previous to neighbours colors + float4 previousClamped = clamp(previous, cmin, cmax); + + float4 color = lerp(lerp(current, previousClamped, 0.5*weight), previousClamped, weight); + return color; +#else // Just blend the pixels: float4 current = SMAASamplePoint(currentColorTex, texcoord); float4 previous = SMAASamplePoint(previousColorTex, texcoord); return lerp(current, previous, 0.5); - #endif +#endif } //----------------------------------------------------------------------------- @@ -1347,10 +1424,10 @@ float4 SMAAResolvePS(float2 texcoord, #ifdef SMAALoad void SMAASeparatePS(float4 position, - float2 texcoord, - out float4 target0, - out float4 target1, - SMAATexture2DMS2(colorTexMS)) { + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { int2 pos = int2(position.xy); target0 = SMAALoad(colorTexMS, pos, 0); target1 = SMAALoad(colorTexMS, pos, 1); diff --git a/Resources/SMAA/SearchTex.dds b/Resources/SMAA/SearchTex.dds deleted file mode 100644 index f48ec89dda75f9e6f61a3265c5b75fa6ee75639e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1152 zcmZ>930A0KU|?Vu;9w8{(hfk(zycyj2MS;0|-ETY=Bbz*vuqFj8gk4)lZ6BX`vQ5s^I1$3qm6fSNtI>B}YG8519-~ N?Wa^fna&("SMAA/AreaTex")); + smaaMaterial.SetTexture("_AreaTex", Resources.Load("SMAA/AreaTex")); // Import search tex as PNG because I can't get Unity to work with an R8 DDS file properly. - smaaMaterial.SetTexture("searchTex", Resources.Load("SMAA/SearchTexPNG")); + smaaMaterial.SetTexture("_SearchTex", Resources.Load("SMAA/SearchTex")); blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); } + private void CreateSMAATextures(int width, int height) + { + releaseSMAATextures(); + + smaaEdgesTex = new RenderTexture( + width, + height, + 0, + RenderTextureFormat.ARGB32, + RenderTextureReadWrite.Linear + ); + smaaEdgesTex.name = "SMAAEdgesTex"; + smaaEdgesTex.enableRandomWrite = true; + smaaEdgesTex.Create(); + smaaEdgesTexHandle = RTHandles.Alloc(smaaEdgesTex); + + smaaBlendTex = new RenderTexture( + width, + height, + 0, + RenderTextureFormat.ARGB32, + RenderTextureReadWrite.Linear + ); + smaaBlendTex.name = "SMAABlendTex"; + smaaBlendTex.enableRandomWrite = true; + smaaBlendTex.Create(); + smaaBlendTexHandle = RTHandles.Alloc(smaaBlendTex); + } + + public void enableSMAA(int width, int height) + { + smaaEnabled = true; + CreateSMAATextures(width, height); + } + + public void disableSMAA() + { + smaaEnabled = false; + releaseSMAATextures(); + } + + private void releaseSMAATextures() + { + if (smaaEdgesTex) + { + smaaEdgesTex?.Release(); + } + if (smaaBlendTex) + { + smaaBlendTex?.Release(); + } + smaaBlendTexHandle?.Release(); + smaaEdgesTexHandle?.Release(); + } + private void setMatrix(CustomPassContext ctx, Matrix4x4 matrix, string name) { ctx.propertyBlock.SetMatrix(Shader.PropertyToID(name), matrix); @@ -86,14 +143,9 @@ protected override void Execute(CustomPassContext ctx) // color image now in mosaicImageHandle runHoleFilling(ctx); - runFXAA(ctx); - runSMAA(ctx); - // ctx.cmd.Blit(smaaEdgesTex, computeShaderResultTexture); - // ctx.cmd.Blit(smaaBlendTex, computeShaderResultTexture); - if (debugRendering) { if (fxaaEnabled == false && fillHoles == false && smaaEnabled == false) @@ -251,12 +303,12 @@ private void runSMAA(CustomPassContext ctx) { if (!smaaEnabled) { - // ctx.cmd.Blit(mosaicImageHandle, computeShaderResultTexture); return; } + smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); smaaMaterial.SetVector( - Shader.PropertyToID("SMAA_RT_METRICS"), + Shader.PropertyToID("_SMAARTMetrics"), new Vector4( 1.0f / mosaicImageHandle.rt.width, 1.0f / mosaicImageHandle.rt.height, @@ -264,8 +316,12 @@ private void runSMAA(CustomPassContext ctx) mosaicImageHandle.rt.height ) ); + smaaMaterial.SetInt(Shader.PropertyToID("_StencilRef"), (int)(1 << 2)); + smaaMaterial.SetInt(Shader.PropertyToID("_StencilCmp"), (int)(1 << 2)); - smaaMaterial.SetTexture(Shader.PropertyToID("ColorTex"), mosaicImageHandle); + // ----------------------------------------------------------------------------- + // EdgeDetection stage + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); CoreUtils.SetRenderTarget(ctx.cmd, smaaEdgesTex, ClearFlag.Color); CoreUtils.DrawFullScreen( ctx.cmd, @@ -274,7 +330,9 @@ private void runSMAA(CustomPassContext ctx) shaderPassId: smaaMaterial.FindPass("EdgeDetection") ); - smaaMaterial.SetTexture(Shader.PropertyToID("edgesTex"), smaaEdgesTex); + // ----------------------------------------------------------------------------- + // BlendWeights stage + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), smaaEdgesTex); CoreUtils.SetRenderTarget(ctx.cmd, smaaBlendTex, ClearFlag.Color); CoreUtils.DrawFullScreen( ctx.cmd, @@ -283,8 +341,11 @@ private void runSMAA(CustomPassContext ctx) shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation") ); - smaaMaterial.SetTexture(Shader.PropertyToID("blendTex"), smaaBlendTex); - CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTexture, ClearFlag.None); + // ----------------------------------------------------------------------------- + // NeighborhoodBlending stage + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_BlendTex"), smaaBlendTex); + CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTextureHandle, ClearFlag.None); CoreUtils.DrawFullScreen( ctx.cmd, smaaMaterial, From 6a0ba31baf2a13695ec28e0cc455f0556ab99a11 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 2 Sep 2025 15:44:57 +0200 Subject: [PATCH 068/149] removed commented out code --- Resources/G3DFXAA.compute | 79 --------------------------------------- 1 file changed, 79 deletions(-) diff --git a/Resources/G3DFXAA.compute b/Resources/G3DFXAA.compute index ded6040..185819c 100644 --- a/Resources/G3DFXAA.compute +++ b/Resources/G3DFXAA.compute @@ -51,35 +51,6 @@ SamplerState PointSampler2 { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; Addre void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 outColor, uint2 positionSS, float2 positionNDC) { - // bool discarded = false; - - // outColor = _InputTexture.Sample(PointSampler2, positionNDC, 0.0); - // outColor = SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, positionNDC, 0.0); - // outColor = tex2Dlod( - // _InputTextureSampler, - // float4(positionNDC, 0.0, 0.0) - // ); - - // float4 smaaOffsets[3]; - // SMAAEdgeDetectionVS( - // positionNDC, - // smaaOffsets - // ); - // float2 tmptmptmptmp = SMAAColorEdgeDetectionPS( - // positionNDC, - // smaaOffsets, - // _InputTexture, - // discarded - // ); - - // outColor = float4(0.0, 0.0, 0.0, 1.0); - // return; - - // if (true) { - // outColor.x = tmptmptmptmp.x; - // return; - // } - float c = LuminanceGamma(Load(_InputTexture, positionSS, 0, 0).rgb); float u = LuminanceGamma(Load(_InputTexture, positionSS, 0, -1).rgb); float d = LuminanceGamma(Load(_InputTexture, positionSS, 0, 1).rgb); @@ -194,56 +165,6 @@ void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 else { outColor.rgb = rgbB; } - - // // Edge detection - // float4 rgbNW = Load(_InputTexture, positionSS, -1, -1); - // float4 rgbNE = Load(_InputTexture, positionSS, 1, -1); - // float4 rgbSW = Load(_InputTexture, positionSS, -1, 1); - // float4 rgbSE = Load(_InputTexture, positionSS, 1, 1); - - // rgbNW.xyz = saturate(rgbNW.xyz); - // rgbNE.xyz = saturate(rgbNE.xyz); - // rgbSW.xyz = saturate(rgbSW.xyz); - // rgbSE.xyz = saturate(rgbSE.xyz); - // outColor.xyz = saturate(outColor.xyz); - // float lumaNW = LuminanceGamma(rgbNW.xyz); - // float lumaNE = LuminanceGamma(rgbNE.xyz); - // float lumaSW = LuminanceGamma(rgbSW.xyz); - // float lumaSE = LuminanceGamma(rgbSE.xyz); - // float lumaM = LuminanceGamma(outColor.xyz); - - // float2 dir; - // dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); - // dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); - - // float lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; - // float dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); - // float rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); - - // dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; - - // // Blur - // float3 rgb03 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); - // float3 rgb13 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); - // float3 rgb23 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); - // float3 rgb33 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); - - // rgb03 = saturate(rgb03); - // rgb13 = saturate(rgb13); - // rgb23 = saturate(rgb23); - // rgb33 = saturate(rgb33); - - // float3 rgbA = 0.5 * (rgb13 + rgb23); - // float3 rgbB = rgbA * 0.5 + 0.25 * (rgb03 + rgb33); - - // float lumaB = LuminanceGamma(rgbB); - - // float lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); - // float lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); - - // float3 rgb = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? rgbA : rgbB; - - // outColor.xyz = rgb; } From 23935109f95fd12f1d0eeadc93e318d321700d01 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 2 Sep 2025 15:45:17 +0200 Subject: [PATCH 069/149] actually apply anti aliasing to autostereo image --- Scripts/G3DHDRPViewGenerationPass.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index ed58709..3d40af6 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -163,6 +163,21 @@ protected override void Execute(CustomPassContext ctx) CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); } + else + { + if (fxaaEnabled == false && fillHoles == false && smaaEnabled == false) + { + return; + } + + blitMaterial.SetTexture( + Shader.PropertyToID("_mainTex"), + computeShaderResultTexture + ); + + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); + } } } From 0429cf058c6a3e7ae21d5eacfb583a504c568664 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 2 Sep 2025 15:58:26 +0200 Subject: [PATCH 070/149] reset FXAA to state in viewGenerator branch (state right before changes on fxaa branch --- Resources/G3DFXAA.compute | 268 +++++++++++++++++++------------------- 1 file changed, 132 insertions(+), 136 deletions(-) diff --git a/Resources/G3DFXAA.compute b/Resources/G3DFXAA.compute index 185819c..d87be9e 100644 --- a/Resources/G3DFXAA.compute +++ b/Resources/G3DFXAA.compute @@ -1,5 +1,9 @@ -// #pragma target 4.0 +// Each #kernel tells which function to compile; you can have many kernels #pragma kernel FXAA +#pragma multi_compile _ ENABLE_ALPHA +#pragma multi_compile _ HDR_INPUT + +#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" @@ -7,15 +11,16 @@ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/PostProcessDefines.hlsl" -// #define SMAA_RT_METRICS float4(1.0 / 1980.0, 1.0 / 1080.0, 1980.0, 1080.0) -// #define SMAA_HLSL_4 -// #define SMAA_PRESET_HIGH -// #include "SMAA.hlsl" - RWTexture2D _OutputTexture; Texture2D _colorMosaic; SamplerState sampler_colorMosaic; + +float4 _HDROutputParams; +#define _PaperWhite _HDROutputParams.z +#define _OneOverPaperWhite _HDROutputParams.w + +#define FXAA_HDR_MAPUNMAP defined(HDR_INPUT) #define FXAA_SPAN_MAX (4.0) #define FXAA_REDUCE_MUL (1.0 / 4.0) #define FXAA_REDUCE_MIN (1.0 / 32.0) @@ -24,16 +29,10 @@ SamplerState sampler_colorMosaic; // #define FXAA_REDUCE_MUL (1.0 / 8.0) // #define FXAA_REDUCE_MIN (1.0 / 128.0) -float LuminanceGamma(float3 c) { - float3 w = float3(0.299, 0.587, 0.114); - return sqrt(dot(c, w)); -} - float3 Fetch(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 coords, float2 offset) { float2 uv = saturate(coords + offset) * _RTHandlePostProcessScale.xy; - float3 color = SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).xyz; - return saturate(color); + return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).xyz; } float4 Load(TEXTURE2D(_InputTexture), int2 icoords, int idx, int idy) @@ -47,128 +46,125 @@ float FetchAlpha(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), float2 co return SAMPLE_TEXTURE2D_LOD(_InputTexture, _InputTextureSampler, uv, 0.0).w; } -SamplerState PointSampler2 { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; - -void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 outColor, uint2 positionSS, float2 positionNDC) +void RunFXAA(TEXTURE2D_PARAM(_InputTexture, _InputTextureSampler), inout float4 outColor, uint2 positionSS, float2 positionNDC, float paperWhite, float oneOverPaperWhite) { - float c = LuminanceGamma(Load(_InputTexture, positionSS, 0, 0).rgb); - float u = LuminanceGamma(Load(_InputTexture, positionSS, 0, -1).rgb); - float d = LuminanceGamma(Load(_InputTexture, positionSS, 0, 1).rgb); - float l = LuminanceGamma(Load(_InputTexture, positionSS, -1, 0).rgb); - float r = LuminanceGamma(Load(_InputTexture, positionSS, 1, 0).rgb); - - // ---- - - float3 rgbM = Load(_InputTexture, positionSS, 0, 0).rgb; - float3 rgbNW = Load(_InputTexture, positionSS, -1, -1).rgb; - float3 rgbNE = Load(_InputTexture, positionSS, 1, -1).rgb; - float3 rgbSW = Load(_InputTexture, positionSS, -1, 1).rgb; - float3 rgbSE = Load(_InputTexture, positionSS, 1, 1).rgb; - - float lumaM = LuminanceGamma(rgbM); - float lumaNW = LuminanceGamma(rgbNW); - float lumaNE = LuminanceGamma(rgbNE); - float lumaSW = LuminanceGamma(rgbSW); - float lumaSE = LuminanceGamma(rgbSE); - - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); - float edge = 4.0 * lumaM - lumaNW - lumaNE - lumaSW - lumaSE; - - // ---- - - // if (lumaMax - lumaMin < 0.05) { - // return; - // } - if (edge < 0.05) { - return; - } - - // outColor.rgb = float3(1.0, 0.0, 0.0); - // outColor.rgb = (rgbM + rgbNW + rgbNE + rgbSW + rgbSE) / 5.0; - - outColor.rgb = - 0.2 * rgbM - + 0.2 * rgbNW - + 0.2 * rgbNE - + 0.2 * rgbSW - + 0.2 * rgbSE; - - // outColor.rgb = - // 0.5 * rgbM - // + 0.125 * rgbNW - // + 0.125 * rgbNE - // + 0.125 * rgbSW - // + 0.125 * rgbSE; - - float3 colors[9] = { - Load(_InputTexture, positionSS, -1, -1).rgb, - Load(_InputTexture, positionSS, 0, -1).rgb, - Load(_InputTexture, positionSS, 1, -1).rgb, - Load(_InputTexture, positionSS, -1, 0).rgb, - Load(_InputTexture, positionSS, 0, 0).rgb, - Load(_InputTexture, positionSS, 1, 0).rgb, - Load(_InputTexture, positionSS, -1, 1).rgb, - Load(_InputTexture, positionSS, 0, 1).rgb, - Load(_InputTexture, positionSS, 1, 1).rgb - }; - - float coeffs[9] = { - 1.0 / 16.0, - 1.0 / 8.0, - 1.0 / 16.0, - 1.0 / 8.0, - 1.0 / 4.0, - 1.0 / 8.0, - 1.0 / 16.0, - 1.0 / 8.0, - 1.0 / 16.0 - }; - - float3 conv = 0.0.xxx; - for (uint i = 0; i < 9; i++) { - conv += coeffs[i] * colors[i]; - } - - outColor.rgb = conv; - - // outColor.r = 1.0; - return; + { + // Edge detection + float4 rgbNW = Load(_InputTexture, positionSS, -1, -1); + float4 rgbNE = Load(_InputTexture, positionSS, 1, -1); + float4 rgbSW = Load(_InputTexture, positionSS, -1, 1); + float4 rgbSE = Load(_InputTexture, positionSS, 1, 1); + +#if FXAA_HDR_MAPUNMAP + // The pixel values we have are already tonemapped but in the range [0, 10000] nits. To run FXAA properly, we need to convert them + // to a SDR range [0; 1]. Since the tonemapped values are not evenly distributed and mostly close to the paperWhite nits value, we can + // normalize by paperWhite to get most of the scene in [0; 1] range. For the remaining pixels, we can use the FastTonemap() to remap + // them to [0, 1] range. + float lumaNW = Luminance(FastTonemap(rgbNW.xyz * oneOverPaperWhite)); + float lumaNE = Luminance(FastTonemap(rgbNE.xyz * oneOverPaperWhite)); + float lumaSW = Luminance(FastTonemap(rgbSW.xyz * oneOverPaperWhite)); + float lumaSE = Luminance(FastTonemap(rgbSE.xyz * oneOverPaperWhite)); + float lumaM = Luminance(FastTonemap(outColor.xyz * oneOverPaperWhite)); +#else + rgbNW.xyz = saturate(rgbNW.xyz); + rgbNE.xyz = saturate(rgbNE.xyz); + rgbSW.xyz = saturate(rgbSW.xyz); + rgbSE.xyz = saturate(rgbSE.xyz); + outColor.xyz = saturate(outColor.xyz); + float lumaNW = Luminance(rgbNW.xyz); + float lumaNE = Luminance(rgbNE.xyz); + float lumaSW = Luminance(rgbSW.xyz); + float lumaSE = Luminance(rgbSE.xyz); + float lumaM = Luminance(outColor.xyz); +#endif + + float2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + float lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; + float dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + float rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); + + dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; + + // Blur + float3 rgb03 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); + float3 rgb13 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); + float3 rgb23 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); + float3 rgb33 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); + +#if FXAA_HDR_MAPUNMAP + rgb03 = FastTonemap(rgb03 * oneOverPaperWhite); + rgb13 = FastTonemap(rgb13 * oneOverPaperWhite); + rgb23 = FastTonemap(rgb23 * oneOverPaperWhite); + rgb33 = FastTonemap(rgb33 * oneOverPaperWhite); +#else + rgb03 = saturate(rgb03); + rgb13 = saturate(rgb13); + rgb23 = saturate(rgb23); + rgb33 = saturate(rgb33); +#endif + + float3 rgbA = 0.5 * (rgb13 + rgb23); + float3 rgbB = rgbA * 0.5 + 0.25 * (rgb03 + rgb33); + + float lumaB = Luminance(rgbB); + + float lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); + float lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); + + float3 rgb = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? rgbA : rgbB; + +#if FXAA_HDR_MAPUNMAP + outColor.xyz = FastTonemapInvert(rgb) * paperWhite;; +#else + outColor.xyz = rgb; +#endif + +#ifdef ENABLE_ALPHA + // FXAA for the alpha channel: alpha can be completely decorelated from the RGB channels, so we might fetch different neighbors for the alpha! + // For this reason we have to recompute the fetch direction + lumaNW = rgbNW.w; + lumaNE = rgbNE.w; + lumaSW = rgbSW.w; + lumaSE = rgbSE.w; + lumaM = outColor.w; + + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + lumaSum = lumaNW + lumaNE + lumaSW + lumaSE; + dirReduce = max(lumaSum * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + rcpDirMin = rcp(min(abs(dir.x), abs(dir.y)) + dirReduce); + + dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; + + // Blur + float a03 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (0.0 / 3.0 - 0.5)); + float a13 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); + float a23 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); + float a33 = FetchAlpha(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (3.0 / 3.0 - 0.5)); + + a03 = saturate(a03); + a13 = saturate(a13); + a23 = saturate(a23); + a33 = saturate(a33); + + float A = 0.5 * (a13 + a23); + float B = A * 0.5 + 0.25 * (a03 + a33); + + lumaMin = Min3(lumaM, lumaNW, Min3(lumaNE, lumaSW, lumaSE)); + lumaMax = Max3(lumaM, lumaNW, Max3(lumaNE, lumaSW, lumaSE)); + + outColor.w = ((lumaB < lumaMin) || (lumaB > lumaMax)) ? A : B; +#endif - // float convolution = 4 * c - u - d - l - r; - // float convolution = lumaMax - lumaMin; - // outColor.rgb = float3(convolution, convolution, convolution); - - - float2 dir; - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); - - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * 0.25 * 0.5, 0.001); - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); - - dir = min((FXAA_SPAN_MAX).xx, max((-FXAA_SPAN_MAX).xx, dir * rcpDirMin)) * _ScreenSize.zw; - - float3 rgb03 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (-1.0 / 3.0 - 0.5)); - float3 rgb13 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (1.0 / 3.0 - 0.5)); - float3 rgb23 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (2.0 / 3.0 - 0.5)); - float3 rgb33 = Fetch(TEXTURE2D_ARGS(_InputTexture, _InputTextureSampler), positionNDC, dir * (4.0 / 3.0 - 0.5)); - - float3 rgbA = 0.5 * (rgb13 + rgb23); - float3 rgbB = rgbA * 0.5 + 0.25 * (rgb03 + rgb33); - - float lumaB = LuminanceGamma(rgbB); - - if ((lumaB < lumaMin) || (lumaB > lumaMax)) { - outColor.rgb = rgbA; - } - else { - outColor.rgb = rgbB; } } -[numthreads(1, 1, 1)] +[numthreads(8,8,1)] void FXAA(uint3 dispatchThreadId : SV_DispatchThreadID) { UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z); @@ -176,14 +172,14 @@ void FXAA(uint3 dispatchThreadId : SV_DispatchThreadID) float2 positionNDC = positionSS * _PostProcessScreenSize.zw + (0.5 * _PostProcessScreenSize.zw); float4 outColor = _colorMosaic.Load(float4(positionSS, 0, 0)); + float4 beforeFXAA = outColor; - if (4 * positionSS.x % int(_PostProcessScreenSize.x) == 0 || 4 * positionSS.y % int(_PostProcessScreenSize.y) == 0) { - // do not run fxx on debug view boundaries - // outColor = float4(1.0, 0.0, 1.0, 1.0); - } - else { - RunFXAA(_colorMosaic, s_linear_clamp_sampler, outColor, positionSS, positionNDC); - } + RunFXAA(_colorMosaic, s_linear_clamp_sampler, outColor, positionSS, positionNDC, _PaperWhite, _OneOverPaperWhite); + +// #if defined(ENABLE_ALPHA) +// // When alpha processing is enabled, FXAA should not affect pixels with zero alpha +// outColor.xyz = outColor.a > 0 ? outColor.xyz : beforeFXAA.xyz; +// #endif _OutputTexture[positionSS] = outColor; } From 04a24a5b597ac18746e83a4fb219308243ca6abf Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 4 Sep 2025 11:32:55 +0200 Subject: [PATCH 071/149] improved ui for antia view generation; copy which anit aliasing to use from camera --- G3DCamera.cs | 102 ++++++++++++++++----------- Resources/G3DCameraInspector.uxml | 12 ++-- Scripts/G3DHDRPViewGenerationPass.cs | 79 +++++++++++++++++---- Scripts/InspectorG3DCamera.cs | 26 +++++++ 4 files changed, 155 insertions(+), 64 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 1b762c9..2386e9b 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; -using UnityEditor; using UnityEngine; using UnityEngine.Rendering; #if UNITY_EDITOR @@ -106,12 +104,27 @@ public class G3DCamera [Tooltip("If set to true, the views will be flipped horizontally.")] public bool mirrorViews = false; + #region Performance [Tooltip( "Set a percentage value to render only that percentage of the width and height per view. E.g. a reduction of 50% will reduce the rendered size by a factor of 4. Adapt Render Resolution To Views takes precedence." )] [Range(1, 100)] public int renderResolutionScale = 100; + [Tooltip("Only actually renders three views. The rest are generated. IF turned on anti ali")] + public bool generateViews = true; + + [Tooltip( + "Fill small holes in generated views. (Only takes effect if view generation is turned on.)" + )] + public bool isFillingHoles; + + [Range(0, 64)] + public int holeFillingRadius = 8; + private bool useVectorMapViewGeneration = false; + #endregion + + [Tooltip( "Set the dolly zoom effekt. 1 correponds to no dolly zoom. 0 is all the way zoomed in to the focus plane. 3 is all the way zoomed out." )] @@ -145,7 +158,11 @@ public class G3DCamera [Tooltip("If set to true, the library will print debug messages to the console.")] public bool debugMessages = false; - public bool showTestFrame = false; + + [Tooltip( + "If set to true, shows the individual views as a mosaic instead of the autostereo effect." + )] + public bool debugRendering; [Tooltip( "If set to true, the gizmos for the focus distance (green) and eye separation (blue) will be shown." @@ -248,15 +265,6 @@ private float scaledHalfCameraWidthAtStart #endregion - private bool generateViews = true; - private bool useVectorMapViewGeneration = false; - public bool isFillingHoles; - public bool applyFXAA; - public bool applySMAA; - public bool debugRendering; - - [Range(0, 64)] - public int holeFillingRadius; private Material viewGenerationMaterial; #if G3D_HDRP private G3DHDRPViewGenerationPass viewGenerationPass; @@ -435,18 +443,6 @@ private void initCustomPass() viewGenerationPass.materialPassName = "G3DViewGeneration"; viewGenerationPass.cameras = cameras; viewGenerationPass.internalCameraCount = internalCameraCount; - viewGenerationPass.computeShaderResultTexture = new RenderTexture( - mainCamera.pixelWidth, - mainCamera.pixelHeight, - 0, - RenderTextureFormat.ARGB32, - RenderTextureReadWrite.Linear - ); - viewGenerationPass.computeShaderResultTexture.enableRandomWrite = true; - viewGenerationPass.computeShaderResultTexture.Create(); - viewGenerationPass.computeShaderResultTextureHandle = RTHandles.Alloc( - viewGenerationPass.computeShaderResultTexture - ); for (int i = 0; i < internalCameraCount; i++) { viewGenerationPass.fullscreenPassMaterial.SetTexture( @@ -459,11 +455,11 @@ private void initCustomPass() viewGenerationPass.debugRendering = debugRendering; viewGenerationPass.fillHoles = isFillingHoles; viewGenerationPass.holeFillingRadius = holeFillingRadius; - viewGenerationPass.fxaaEnabled = applyFXAA; - if (applySMAA) - { - viewGenerationPass.enableSMAA(mainCamera.pixelWidth, mainCamera.pixelHeight); - } + AntialiasingMode aaMode = getCameraAAMode(); + viewGenerationPass.init( + new Vector2Int(mainCamera.pixelWidth, mainCamera.pixelHeight), + aaMode + ); // add autostereo mosaic generation pass RenderTexture mosaicTexture = new RenderTexture( @@ -778,6 +774,25 @@ private Vector2Int getDisplayResolutionFromCalibrationFile() return new Vector2Int(HorizontalResolution, VerticalResolution); } + private AntialiasingMode getCameraAAMode() + { + AntialiasingMode aaMode = AntialiasingMode.None; + if ( + antialiasingMode + == HDAdditionalCameraData.AntialiasingMode.SubpixelMorphologicalAntiAliasing + ) + { + aaMode = AntialiasingMode.SMAA; + } + else if ( + antialiasingMode == HDAdditionalCameraData.AntialiasingMode.FastApproximateAntialiasing + ) + { + aaMode = AntialiasingMode.FXAA; + } + return aaMode; + } + private void initLibrary() { string applicationName = Application.productName; @@ -964,16 +979,14 @@ void Update() } #if G3D_HDRP - viewGenerationPass.holeFillingRadius = holeFillingRadius; - if (applySMAA) - { - viewGenerationPass.enableSMAA(mainCamera.pixelWidth, mainCamera.pixelHeight); - } - else + if (generateViews) { - viewGenerationPass.disableSMAA(); + viewGenerationPass.holeFillingRadius = holeFillingRadius; + viewGenerationPass.fillHoles = isFillingHoles; + + AntialiasingMode aaMode = getCameraAAMode(); + viewGenerationPass.setAntiAliasingMode(aaMode); } - viewGenerationPass.fxaaEnabled = applyFXAA; #endif } @@ -1046,7 +1059,7 @@ private void updateShaderParameters() material?.SetInt(shaderHandles.mstart, shaderParameters.mstart); // test frame and stripe - material?.SetInt(shaderHandles.showTestFrame, showTestFrame ? 1 : 0); + material?.SetInt(shaderHandles.showTestFrame, 0); material?.SetInt(shaderHandles.showTestStripe, shaderParameters.showTestStripe); material?.SetInt(shaderHandles.testGapWidth, shaderParameters.testGapWidth); @@ -1140,15 +1153,18 @@ void updateCameras() camera.nearClipPlane = mainCamera.nearClipPlane; camera.projectionMatrix = mainCamera.projectionMatrix; camera.transform.localRotation = cameraParent.transform.localRotation; + if (generateViews == false) + { #if G3D_URP - camera.GetUniversalAdditionalCameraData().antialiasing = antialiasingMode; + camera.GetUniversalAdditionalCameraData().antialiasing = antialiasingMode; #endif #if G3D_HDRP - HDAdditionalCameraData hdAdditionalCameraData = - camera.gameObject.GetComponent(); - if (hdAdditionalCameraData != null) - hdAdditionalCameraData.antialiasing = antialiasingMode; + HDAdditionalCameraData hdAdditionalCameraData = + camera.gameObject.GetComponent(); + if (hdAdditionalCameraData != null) + hdAdditionalCameraData.antialiasing = antialiasingMode; #endif + } float localCameraOffset = calculateCameraOffset( i, diff --git a/Resources/G3DCameraInspector.uxml b/Resources/G3DCameraInspector.uxml index e8d5101..6571fae 100644 --- a/Resources/G3DCameraInspector.uxml +++ b/Resources/G3DCameraInspector.uxml @@ -8,11 +8,12 @@ - - - - - + + + + + + @@ -23,6 +24,5 @@ - diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index 3d40af6..ccb59e2 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -4,6 +4,13 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; +internal enum AntialiasingMode +{ + None, + FXAA, + SMAA +} + internal class G3DHDRPViewGenerationPass : FullScreenCustomPass { public RTHandle leftColorMapHandle; @@ -37,10 +44,11 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass private ComputeShader fxaaCompShader; private int fxaaKernel; - public bool fxaaEnabled; private Material smaaMaterial; - private bool smaaEnabled; + private AntialiasingMode antialiasingMode = AntialiasingMode.None; + + public Vector2Int renderResolution = new Vector2Int(1920, 1080); protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { @@ -62,6 +70,34 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); } + public void init(Vector2Int resolution, AntialiasingMode mode) + { + renderResolution = resolution; + CreateComputeShaderResultTexture(); + setAntiAliasingMode(mode); + } + + private void CreateComputeShaderResultTexture() + { + // release old texture if it exists + if (computeShaderResultTexture) + { + computeShaderResultTexture?.Release(); + } + computeShaderResultTextureHandle?.Release(); + + computeShaderResultTexture = new RenderTexture( + renderResolution.x, + renderResolution.y, + 0, + RenderTextureFormat.ARGB32, + RenderTextureReadWrite.Linear + ); + computeShaderResultTexture.enableRandomWrite = true; + computeShaderResultTexture.Create(); + computeShaderResultTextureHandle = RTHandles.Alloc(computeShaderResultTexture); + } + private void CreateSMAATextures(int width, int height) { releaseSMAATextures(); @@ -91,16 +127,23 @@ private void CreateSMAATextures(int width, int height) smaaBlendTexHandle = RTHandles.Alloc(smaaBlendTex); } - public void enableSMAA(int width, int height) + public void setAntiAliasingMode(AntialiasingMode mode) { - smaaEnabled = true; - CreateSMAATextures(width, height); - } + AntialiasingMode oldMode = antialiasingMode; + if (oldMode == mode) + { + return; + } - public void disableSMAA() - { - smaaEnabled = false; - releaseSMAATextures(); + antialiasingMode = mode; + if (mode == AntialiasingMode.None || mode == AntialiasingMode.FXAA) + { + releaseSMAATextures(); + } + else if (mode == AntialiasingMode.SMAA) + { + CreateSMAATextures(renderResolution.x, renderResolution.y); + } } private void releaseSMAATextures() @@ -148,7 +191,7 @@ protected override void Execute(CustomPassContext ctx) if (debugRendering) { - if (fxaaEnabled == false && fillHoles == false && smaaEnabled == false) + if (fillHoles == false && antialiasingMode == AntialiasingMode.None) { blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), mosaicImageHandle); } @@ -165,7 +208,7 @@ protected override void Execute(CustomPassContext ctx) } else { - if (fxaaEnabled == false && fillHoles == false && smaaEnabled == false) + if (fillHoles == false && antialiasingMode == AntialiasingMode.None) { return; } @@ -284,10 +327,11 @@ private void runHoleFilling(CustomPassContext ctx) private void runFXAA(CustomPassContext ctx) { - if (fxaaEnabled == false) + if (antialiasingMode != AntialiasingMode.FXAA) { return; } + Tonemapping tonemapping = ctx.hdCamera.volumeStack.GetComponent(); float paperWhite = tonemapping.paperWhite.value; Vector4 hdroutParameters = new Vector4(0, 1000, paperWhite, 1f / paperWhite); @@ -316,7 +360,7 @@ private void runFXAA(CustomPassContext ctx) private void runSMAA(CustomPassContext ctx) { - if (!smaaEnabled) + if (antialiasingMode != AntialiasingMode.SMAA) { return; } @@ -399,7 +443,12 @@ out var mosaicCamera return true; } - protected override void Cleanup() { } + protected override void Cleanup() + { + releaseSMAATextures(); + computeShaderResultTextureHandle?.Release(); + computeShaderResultTexture?.Release(); + } } internal class G3DHDRPViewGenerationMosaicPass : FullScreenCustomPass diff --git a/Scripts/InspectorG3DCamera.cs b/Scripts/InspectorG3DCamera.cs index 4440429..0bad662 100644 --- a/Scripts/InspectorG3DCamera.cs +++ b/Scripts/InspectorG3DCamera.cs @@ -10,10 +10,13 @@ public class InspectorG3DCamera : Editor public VisualTreeAsset inspectorXML; private PropertyField modeField; + private PropertyField generateViewsField; private VisualElement dioramaInspector; private VisualElement multiviewInspector; + private VisualElement viewGenerationContainer; + public override VisualElement CreateInspectorGUI() { // Create a new VisualElement to be the root of our Inspector UI. @@ -43,12 +46,35 @@ public override VisualElement CreateInspectorGUI() dioramaInspector = mainInspector.Q("Diorama"); multiviewInspector = mainInspector.Q("Multiview"); + viewGenerationContainer = mainInspector.Q("viewGenerationContainer"); + generateViewsField = mainInspector.Q("generateViews"); + generateViewsField.RegisterValueChangeCallback( + (evt) => + { + bool newMode = evt.changedProperty.boolValue; + setViewgenerationDisplay(newMode); + } + ); + // setup UI setDisplayMode((target as G3DCamera).mode); + setViewgenerationDisplay((target as G3DCamera).generateViews); return mainInspector; } + private void setViewgenerationDisplay(bool enabled) + { + if (enabled) + { + viewGenerationContainer.style.display = DisplayStyle.Flex; + } + else + { + viewGenerationContainer.style.display = DisplayStyle.None; + } + } + private void setDisplayMode(G3DCameraMode mode) { if (mode == G3DCameraMode.DIORAMA) From ef39b454c7e3e8541f5eb3f7b019a97e750546e7 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 4 Sep 2025 11:44:58 +0200 Subject: [PATCH 072/149] read smaa quality level from main camera --- ...bpixelMorphologicalAntialiasingBridge.hlsl | 1 - Scripts/G3DHDRPViewGenerationPass.cs | 21 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Resources/SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl b/Resources/SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl index f093329..c06ca2d 100644 --- a/Resources/SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl +++ b/Resources/SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl @@ -19,7 +19,6 @@ float4 _SMAARTMetrics; #define LinearSampler s_linear_clamp_sampler #define PointSampler s_point_clamp_sampler #define GAMMA_FOR_EDGE_DETECTION (1/2.2) -#define SMAA_PRESET_HIGH #include "SMAA.hlsl" diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/G3DHDRPViewGenerationPass.cs index ccb59e2..2319a5d 100644 --- a/Scripts/G3DHDRPViewGenerationPass.cs +++ b/Scripts/G3DHDRPViewGenerationPass.cs @@ -180,6 +180,7 @@ private void addViewProjectionMatrix(CustomPassContext ctx, Camera camera, strin protected override void Execute(CustomPassContext ctx) { var camera = ctx.hdCamera.camera; + HDAdditionalCameraData hdCamera = camera.GetComponent(); if (isMainG3DCamera(camera)) { runReprojection(ctx); @@ -187,7 +188,7 @@ protected override void Execute(CustomPassContext ctx) runHoleFilling(ctx); runFXAA(ctx); - runSMAA(ctx); + runSMAA(ctx, hdCamera); if (debugRendering) { @@ -358,7 +359,7 @@ private void runFXAA(CustomPassContext ctx) ); } - private void runSMAA(CustomPassContext ctx) + private void runSMAA(CustomPassContext ctx, HDAdditionalCameraData hdCamera) { if (antialiasingMode != AntialiasingMode.SMAA) { @@ -378,6 +379,22 @@ private void runSMAA(CustomPassContext ctx) smaaMaterial.SetInt(Shader.PropertyToID("_StencilRef"), (int)(1 << 2)); smaaMaterial.SetInt(Shader.PropertyToID("_StencilCmp"), (int)(1 << 2)); + switch (hdCamera.SMAAQuality) + { + case HDAdditionalCameraData.SMAAQualityLevel.Low: + smaaMaterial.EnableKeyword("SMAA_PRESET_LOW"); + break; + case HDAdditionalCameraData.SMAAQualityLevel.Medium: + smaaMaterial.EnableKeyword("SMAA_PRESET_MEDIUM"); + break; + case HDAdditionalCameraData.SMAAQualityLevel.High: + smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); + break; + default: + smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); + break; + } + // ----------------------------------------------------------------------------- // EdgeDetection stage ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); From b6c99dc706cf11a61275b94ad0730a3b4be00b60 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 4 Sep 2025 12:03:05 +0200 Subject: [PATCH 073/149] moved smaa shader --- Resources/{ => SMAA}/G3DSMAA.shader | 0 Resources/{ => SMAA}/G3DSMAA.shader.meta | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Resources/{ => SMAA}/G3DSMAA.shader (100%) rename Resources/{ => SMAA}/G3DSMAA.shader.meta (100%) diff --git a/Resources/G3DSMAA.shader b/Resources/SMAA/G3DSMAA.shader similarity index 100% rename from Resources/G3DSMAA.shader rename to Resources/SMAA/G3DSMAA.shader diff --git a/Resources/G3DSMAA.shader.meta b/Resources/SMAA/G3DSMAA.shader.meta similarity index 100% rename from Resources/G3DSMAA.shader.meta rename to Resources/SMAA/G3DSMAA.shader.meta From 083a360b925709e396e605815d7c55f8a64ce77d Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 5 Sep 2025 16:12:49 +0200 Subject: [PATCH 074/149] small bug fixes --- G3DCamera.cs | 6 +++++- Resources/SMAA/G3DSMAA.shader | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 2386e9b..ab00986 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -639,7 +639,11 @@ public void setupCameras() { viewSeparation = 0.031f; } - focusPlaneObject.transform.localPosition = new Vector3(0, 0, scaledFocusDistance); + + if (focusPlaneObject != null) + { + focusPlaneObject.transform.localPosition = new Vector3(0, 0, scaledFocusDistance); + } return; } diff --git a/Resources/SMAA/G3DSMAA.shader b/Resources/SMAA/G3DSMAA.shader index f3cc984..46fc258 100644 --- a/Resources/SMAA/G3DSMAA.shader +++ b/Resources/SMAA/G3DSMAA.shader @@ -38,7 +38,7 @@ Shader "G3D/SMAA" { #pragma vertex VertEdge #pragma fragment FragEdge - #include "SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl" + #include "SubpixelMorphologicalAntialiasingBridge.hlsl" ENDHLSL } @@ -59,7 +59,7 @@ Shader "G3D/SMAA" { #pragma vertex VertBlend #pragma fragment FragBlend - #include "SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl" + #include "SubpixelMorphologicalAntialiasingBridge.hlsl" ENDHLSL } @@ -71,7 +71,7 @@ Shader "G3D/SMAA" { #pragma vertex VertNeighbor #pragma fragment FragNeighbor - #include "SMAA/SubpixelMorphologicalAntialiasingBridge.hlsl" + #include "SubpixelMorphologicalAntialiasingBridge.hlsl" ENDHLSL } From 8b70057ce0283c5f7d5e685bbfd2e7555eb86e61 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 5 Sep 2025 16:31:57 +0200 Subject: [PATCH 075/149] readme update --- README.md | 35 ++++++++++++++++++++++--------- Resources/G3DCameraInspector.uxml | 2 +- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 5f08a85..75e6d33 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,19 @@ In general it renders two or more camera views per frame and combines them into # Parameters -| Name | Description | -| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- | -| Calibration file | The calibration file used to calibrate the cameras. This value has to be set for all modes (multi view and diorama) to work. | -| Calibration Path | Path to diorama mode display calibration files. | -| Mode | Switch between Diorama and Multiview modes. | -| Scene scale factor | Scales display calibration to fit larger scenes. | -| Mirror views | Mirrors the individual views horizontally (e.g. needed for Holobox displays). | -| Dolly zoom | Mimics a dolly zoom effect. | -| View offset scale | Scales the view disparity (e.g pushes 3d cameras closer together/ further apart). | +| Name | Description | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| Calibration file | The calibration file used to calibrate the cameras. This value has to be set for all modes (multi view and diorama) to work. | +| Calibration Path | Path to diorama mode display calibration files. | +| Mode | Switch between Diorama and Multiview modes. | +| Scene scale factor | Scales display calibration to fit larger scenes. | +| Mirror views | Mirrors the individual views horizontally (e.g. needed for Holobox displays). | +| Dolly zoom | Mimics a dolly zoom effect. | +| View offset scale | Scales the view disparity (e.g pushes 3d cameras closer together/ further apart). | +| Generate Views | If set to true, only the outer most and the middle view will be rendered. The rest is generated based on these. | +| Fill holes | If set to true, small holes in the generated views will be filled. | +| Hole filling radius | How far around the missing pixels the algorithm searches for a usable pixel. Larger values result in better results but take longer. | +| Debug rendering | Disable autostereo effect rendering. Render views as grid. | ### Calibration files @@ -52,7 +56,6 @@ Mimics a dolly zoom effect by scaling the camera position and field of view. Thi | Show gizmos | Render helper gizmos in scene. | | Gizmo size | Size of the gizmos. | | Debug messages | Print debug messages from the head tracking library to the console. | -| Show test frame | Show a red/ green test frame. | ### Render resolution scale @@ -66,6 +69,18 @@ The plugin renders the scene from newly created cameras (usually one for each ey The camera the script is attached to is used as the main camera, displays the final image, but does not render anything itself. The new cameras are created as children of the main camera at runtime. +# View Generation + +When this is enabled only the outer most and the middle view are rendered. The other views are generated based on these two views. + +## Anti Aliasing + +Only FXAA and SMAA work when view generation is enabled. When the cameras anti aliasing is set to TAA no anti aliasing is applied to the rendered views. + +## Bloom + +Bloom does not work correctly when view generation is enabled. Bloom results in weird halo like effects around objects in the generated views. It is recommended to disable bloom when using view generation. + # Switching render pipelines If you switch between render pipelines (e.g. from URP to built-in) you need to ensure the old render pipeline package is removed from the project. Otherwise this plugin will show wrong behavior. diff --git a/Resources/G3DCameraInspector.uxml b/Resources/G3DCameraInspector.uxml index 6571fae..07364b3 100644 --- a/Resources/G3DCameraInspector.uxml +++ b/Resources/G3DCameraInspector.uxml @@ -10,7 +10,7 @@ - + From 0a276ba63559f761ec2b5b6473c192f08830905f Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 8 Sep 2025 11:24:57 +0200 Subject: [PATCH 076/149] restructured script file tree --- Scripts/HeadTracking.meta | 8 ++++++++ Scripts/{ => HeadTracking}/Exceptions.meta | 0 .../Exceptions/G3D_AlreadyInitializedException.cs | 0 .../Exceptions/G3D_AlreadyInitializedException.cs.meta | 0 .../Exceptions/G3D_IndexOutOfRangeException.cs | 0 .../Exceptions/G3D_IndexOutOfRangeException.cs.meta | 0 .../Exceptions/G3D_NotInitializedException.cs | 0 .../Exceptions/G3D_NotInitializedException.cs.meta | 0 Scripts/{ => HeadTracking}/HeadTrackingPretender.cs | 0 Scripts/{ => HeadTracking}/HeadTrackingPretender.cs.meta | 0 Scripts/{ => HeadTracking}/Interfaces.meta | 0 .../Interfaces/ITNewErrorMessageCallback.cs | 0 .../Interfaces/ITNewErrorMessageCallback.cs.meta | 0 .../Interfaces/ITNewHeadPositionCallback.cs | 0 .../Interfaces/ITNewHeadPositionCallback.cs.meta | 0 .../Interfaces/ITNewShaderParametersCallback.cs | 0 .../Interfaces/ITNewShaderParametersCallback.cs.meta | 0 Scripts/{ => HeadTracking}/LibInterface.cs | 0 Scripts/{ => HeadTracking}/LibInterface.cs.meta | 0 Scripts/RenderPipelines.meta | 8 ++++++++ Scripts/RenderPipelines/HDRP.meta | 8 ++++++++ Scripts/{ => RenderPipelines/HDRP}/G3DHDRPCustomPass.cs | 0 .../{ => RenderPipelines/HDRP}/G3DHDRPCustomPass.cs.meta | 0 .../{ => RenderPipelines/HDRP}/G3DHDRPDepthMapPrePass.cs | 0 .../HDRP}/G3DHDRPDepthMapPrePass.cs.meta | 0 .../HDRP}/G3DHDRPViewGenerationPass.cs | 0 .../HDRP}/G3DHDRPViewGenerationPass.cs.meta | 0 Scripts/RenderPipelines/URP.meta | 8 ++++++++ .../URP}/G3DUrpScriptableRenderPass.cs | 0 .../URP}/G3DUrpScriptableRenderPass.cs.meta | 0 30 files changed, 32 insertions(+) create mode 100644 Scripts/HeadTracking.meta rename Scripts/{ => HeadTracking}/Exceptions.meta (100%) rename Scripts/{ => HeadTracking}/Exceptions/G3D_AlreadyInitializedException.cs (100%) rename Scripts/{ => HeadTracking}/Exceptions/G3D_AlreadyInitializedException.cs.meta (100%) rename Scripts/{ => HeadTracking}/Exceptions/G3D_IndexOutOfRangeException.cs (100%) rename Scripts/{ => HeadTracking}/Exceptions/G3D_IndexOutOfRangeException.cs.meta (100%) rename Scripts/{ => HeadTracking}/Exceptions/G3D_NotInitializedException.cs (100%) rename Scripts/{ => HeadTracking}/Exceptions/G3D_NotInitializedException.cs.meta (100%) rename Scripts/{ => HeadTracking}/HeadTrackingPretender.cs (100%) rename Scripts/{ => HeadTracking}/HeadTrackingPretender.cs.meta (100%) rename Scripts/{ => HeadTracking}/Interfaces.meta (100%) rename Scripts/{ => HeadTracking}/Interfaces/ITNewErrorMessageCallback.cs (100%) rename Scripts/{ => HeadTracking}/Interfaces/ITNewErrorMessageCallback.cs.meta (100%) rename Scripts/{ => HeadTracking}/Interfaces/ITNewHeadPositionCallback.cs (100%) rename Scripts/{ => HeadTracking}/Interfaces/ITNewHeadPositionCallback.cs.meta (100%) rename Scripts/{ => HeadTracking}/Interfaces/ITNewShaderParametersCallback.cs (100%) rename Scripts/{ => HeadTracking}/Interfaces/ITNewShaderParametersCallback.cs.meta (100%) rename Scripts/{ => HeadTracking}/LibInterface.cs (100%) rename Scripts/{ => HeadTracking}/LibInterface.cs.meta (100%) create mode 100644 Scripts/RenderPipelines.meta create mode 100644 Scripts/RenderPipelines/HDRP.meta rename Scripts/{ => RenderPipelines/HDRP}/G3DHDRPCustomPass.cs (100%) rename Scripts/{ => RenderPipelines/HDRP}/G3DHDRPCustomPass.cs.meta (100%) rename Scripts/{ => RenderPipelines/HDRP}/G3DHDRPDepthMapPrePass.cs (100%) rename Scripts/{ => RenderPipelines/HDRP}/G3DHDRPDepthMapPrePass.cs.meta (100%) rename Scripts/{ => RenderPipelines/HDRP}/G3DHDRPViewGenerationPass.cs (100%) rename Scripts/{ => RenderPipelines/HDRP}/G3DHDRPViewGenerationPass.cs.meta (100%) create mode 100644 Scripts/RenderPipelines/URP.meta rename Scripts/{ => RenderPipelines/URP}/G3DUrpScriptableRenderPass.cs (100%) rename Scripts/{ => RenderPipelines/URP}/G3DUrpScriptableRenderPass.cs.meta (100%) diff --git a/Scripts/HeadTracking.meta b/Scripts/HeadTracking.meta new file mode 100644 index 0000000..a7ba442 --- /dev/null +++ b/Scripts/HeadTracking.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 76f510add19ac1c4ea2b15abf35ef11f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Exceptions.meta b/Scripts/HeadTracking/Exceptions.meta similarity index 100% rename from Scripts/Exceptions.meta rename to Scripts/HeadTracking/Exceptions.meta diff --git a/Scripts/Exceptions/G3D_AlreadyInitializedException.cs b/Scripts/HeadTracking/Exceptions/G3D_AlreadyInitializedException.cs similarity index 100% rename from Scripts/Exceptions/G3D_AlreadyInitializedException.cs rename to Scripts/HeadTracking/Exceptions/G3D_AlreadyInitializedException.cs diff --git a/Scripts/Exceptions/G3D_AlreadyInitializedException.cs.meta b/Scripts/HeadTracking/Exceptions/G3D_AlreadyInitializedException.cs.meta similarity index 100% rename from Scripts/Exceptions/G3D_AlreadyInitializedException.cs.meta rename to Scripts/HeadTracking/Exceptions/G3D_AlreadyInitializedException.cs.meta diff --git a/Scripts/Exceptions/G3D_IndexOutOfRangeException.cs b/Scripts/HeadTracking/Exceptions/G3D_IndexOutOfRangeException.cs similarity index 100% rename from Scripts/Exceptions/G3D_IndexOutOfRangeException.cs rename to Scripts/HeadTracking/Exceptions/G3D_IndexOutOfRangeException.cs diff --git a/Scripts/Exceptions/G3D_IndexOutOfRangeException.cs.meta b/Scripts/HeadTracking/Exceptions/G3D_IndexOutOfRangeException.cs.meta similarity index 100% rename from Scripts/Exceptions/G3D_IndexOutOfRangeException.cs.meta rename to Scripts/HeadTracking/Exceptions/G3D_IndexOutOfRangeException.cs.meta diff --git a/Scripts/Exceptions/G3D_NotInitializedException.cs b/Scripts/HeadTracking/Exceptions/G3D_NotInitializedException.cs similarity index 100% rename from Scripts/Exceptions/G3D_NotInitializedException.cs rename to Scripts/HeadTracking/Exceptions/G3D_NotInitializedException.cs diff --git a/Scripts/Exceptions/G3D_NotInitializedException.cs.meta b/Scripts/HeadTracking/Exceptions/G3D_NotInitializedException.cs.meta similarity index 100% rename from Scripts/Exceptions/G3D_NotInitializedException.cs.meta rename to Scripts/HeadTracking/Exceptions/G3D_NotInitializedException.cs.meta diff --git a/Scripts/HeadTrackingPretender.cs b/Scripts/HeadTracking/HeadTrackingPretender.cs similarity index 100% rename from Scripts/HeadTrackingPretender.cs rename to Scripts/HeadTracking/HeadTrackingPretender.cs diff --git a/Scripts/HeadTrackingPretender.cs.meta b/Scripts/HeadTracking/HeadTrackingPretender.cs.meta similarity index 100% rename from Scripts/HeadTrackingPretender.cs.meta rename to Scripts/HeadTracking/HeadTrackingPretender.cs.meta diff --git a/Scripts/Interfaces.meta b/Scripts/HeadTracking/Interfaces.meta similarity index 100% rename from Scripts/Interfaces.meta rename to Scripts/HeadTracking/Interfaces.meta diff --git a/Scripts/Interfaces/ITNewErrorMessageCallback.cs b/Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs similarity index 100% rename from Scripts/Interfaces/ITNewErrorMessageCallback.cs rename to Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs diff --git a/Scripts/Interfaces/ITNewErrorMessageCallback.cs.meta b/Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs.meta similarity index 100% rename from Scripts/Interfaces/ITNewErrorMessageCallback.cs.meta rename to Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs.meta diff --git a/Scripts/Interfaces/ITNewHeadPositionCallback.cs b/Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs similarity index 100% rename from Scripts/Interfaces/ITNewHeadPositionCallback.cs rename to Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs diff --git a/Scripts/Interfaces/ITNewHeadPositionCallback.cs.meta b/Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs.meta similarity index 100% rename from Scripts/Interfaces/ITNewHeadPositionCallback.cs.meta rename to Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs.meta diff --git a/Scripts/Interfaces/ITNewShaderParametersCallback.cs b/Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs similarity index 100% rename from Scripts/Interfaces/ITNewShaderParametersCallback.cs rename to Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs diff --git a/Scripts/Interfaces/ITNewShaderParametersCallback.cs.meta b/Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs.meta similarity index 100% rename from Scripts/Interfaces/ITNewShaderParametersCallback.cs.meta rename to Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs.meta diff --git a/Scripts/LibInterface.cs b/Scripts/HeadTracking/LibInterface.cs similarity index 100% rename from Scripts/LibInterface.cs rename to Scripts/HeadTracking/LibInterface.cs diff --git a/Scripts/LibInterface.cs.meta b/Scripts/HeadTracking/LibInterface.cs.meta similarity index 100% rename from Scripts/LibInterface.cs.meta rename to Scripts/HeadTracking/LibInterface.cs.meta diff --git a/Scripts/RenderPipelines.meta b/Scripts/RenderPipelines.meta new file mode 100644 index 0000000..efb3c70 --- /dev/null +++ b/Scripts/RenderPipelines.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 66ef204f809f9ed41aedc15a23a08dcd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/RenderPipelines/HDRP.meta b/Scripts/RenderPipelines/HDRP.meta new file mode 100644 index 0000000..225c959 --- /dev/null +++ b/Scripts/RenderPipelines/HDRP.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e314c3df7ae97444aaf0a8f52ffcdefd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/G3DHDRPCustomPass.cs b/Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs similarity index 100% rename from Scripts/G3DHDRPCustomPass.cs rename to Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs diff --git a/Scripts/G3DHDRPCustomPass.cs.meta b/Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs.meta similarity index 100% rename from Scripts/G3DHDRPCustomPass.cs.meta rename to Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs.meta diff --git a/Scripts/G3DHDRPDepthMapPrePass.cs b/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs similarity index 100% rename from Scripts/G3DHDRPDepthMapPrePass.cs rename to Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs diff --git a/Scripts/G3DHDRPDepthMapPrePass.cs.meta b/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs.meta similarity index 100% rename from Scripts/G3DHDRPDepthMapPrePass.cs.meta rename to Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs.meta diff --git a/Scripts/G3DHDRPViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs similarity index 100% rename from Scripts/G3DHDRPViewGenerationPass.cs rename to Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs diff --git a/Scripts/G3DHDRPViewGenerationPass.cs.meta b/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs.meta similarity index 100% rename from Scripts/G3DHDRPViewGenerationPass.cs.meta rename to Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs.meta diff --git a/Scripts/RenderPipelines/URP.meta b/Scripts/RenderPipelines/URP.meta new file mode 100644 index 0000000..8ad0343 --- /dev/null +++ b/Scripts/RenderPipelines/URP.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 719e99827783dcc48b940341669991e8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/G3DUrpScriptableRenderPass.cs b/Scripts/RenderPipelines/URP/G3DUrpScriptableRenderPass.cs similarity index 100% rename from Scripts/G3DUrpScriptableRenderPass.cs rename to Scripts/RenderPipelines/URP/G3DUrpScriptableRenderPass.cs diff --git a/Scripts/G3DUrpScriptableRenderPass.cs.meta b/Scripts/RenderPipelines/URP/G3DUrpScriptableRenderPass.cs.meta similarity index 100% rename from Scripts/G3DUrpScriptableRenderPass.cs.meta rename to Scripts/RenderPipelines/URP/G3DUrpScriptableRenderPass.cs.meta From 6e76596827a0868fe2b81f0199d310c5fd069ced Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 5 Dec 2025 11:40:23 +0100 Subject: [PATCH 077/149] fixed error if hdrp is not present --- G3DCamera.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/G3DCamera.cs b/G3DCamera.cs index ab00986..4e43a29 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -778,6 +778,7 @@ private Vector2Int getDisplayResolutionFromCalibrationFile() return new Vector2Int(HorizontalResolution, VerticalResolution); } +#if G3D_HDRP private AntialiasingMode getCameraAAMode() { AntialiasingMode aaMode = AntialiasingMode.None; @@ -796,6 +797,7 @@ private AntialiasingMode getCameraAAMode() } return aaMode; } +#endif private void initLibrary() { From bbe713520a08e3668a2889cbb97402a44e0d8428 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 5 Dec 2025 12:50:52 +0100 Subject: [PATCH 078/149] fixed inspector --- G3DCamera.cs | 13 +++---------- Resources/G3DCameraInspector.uxml | 20 ++++++++------------ Scripts/InspectorG3DCamera.cs | 21 --------------------- 3 files changed, 11 insertions(+), 43 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index fc480ac..ddace0b 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using UnityEngine; using UnityEngine.Rendering; #if UNITY_EDITOR @@ -1287,16 +1288,8 @@ private void addRenderTextureToCamera( int width = Screen.width; int height = Screen.height; - if (adaptRenderResolutionToViews) - { - // TODO: This is a temporary fix for the resolution scaling issue. Use an actually correct formula here. - width = width / internalCameraCount; - } - else - { - width = (int)(width * ((float)renderResolutionScale / 100f)); - height = (int)(height * ((float)renderResolutionScale / 100f)); - } + width = (int)(width * ((float)renderResolutionScale / 100f)); + height = (int)(height * ((float)renderResolutionScale / 100f)); renderTextures[renderTextureIndex] = new RenderTexture(width, height, 0) { diff --git a/Resources/G3DCameraInspector.uxml b/Resources/G3DCameraInspector.uxml index 28416c7..1fec539 100644 --- a/Resources/G3DCameraInspector.uxml +++ b/Resources/G3DCameraInspector.uxml @@ -1,9 +1,4 @@ - - - - - - + @@ -22,12 +17,13 @@ - - - - - - + + + + + + + diff --git a/Scripts/InspectorG3DCamera.cs b/Scripts/InspectorG3DCamera.cs index 02ea4e3..1f6cefe 100644 --- a/Scripts/InspectorG3DCamera.cs +++ b/Scripts/InspectorG3DCamera.cs @@ -23,7 +23,6 @@ public class InspectorG3DCamera : Editor private static bool isAdvancedSettingsVisible = false; private Foldout advancedSettingsFoldout; - private VisualElement viewGenerationContainer; public override VisualElement CreateInspectorGUI() @@ -102,7 +101,6 @@ public override VisualElement CreateInspectorGUI() ); // setup UI - setDisplayMode((target as G3DCamera).mode); setViewgenerationDisplay((target as G3DCamera).generateViews); return mainInspector; @@ -119,24 +117,5 @@ private void setViewgenerationDisplay(bool enabled) viewGenerationContainer.style.display = DisplayStyle.None; } } - - private void setDisplayMode(G3DCameraMode mode) - { - if (mode == G3DCameraMode.DIORAMA) - { - dioramaInspector.style.display = DisplayStyle.Flex; - multiviewInspector.style.display = DisplayStyle.None; - } - else if (mode == G3DCameraMode.MULTIVIEW) - { - dioramaInspector.style.display = DisplayStyle.None; - multiviewInspector.style.display = DisplayStyle.Flex; - } - else - { - dioramaInspector.style.display = DisplayStyle.None; - multiviewInspector.style.display = DisplayStyle.None; - } - } } #endif From 422cfa044a45a25f5b69ca1cd7a7c488b3d3d548 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 5 Dec 2025 15:05:40 +0100 Subject: [PATCH 079/149] fixed shader issues --- G3DCamera.cs | 2 +- Resources/G3DShader.shader | 6 +++--- Resources/G3DShaderMultiview.shader | 4 ++-- Resources/G3DShaderMultiviewMosaic.shader | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index ddace0b..85b7abf 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -439,7 +439,7 @@ private void initCustomPass() RenderTexture depthTexture = new RenderTexture( (int)width, (int)height, - 0, + 16, RenderTextureFormat.Depth ); depthTexture.Create(); diff --git a/Resources/G3DShader.shader b/Resources/G3DShader.shader index 9b78d95..86ab3de 100644 --- a/Resources/G3DShader.shader +++ b/Resources/G3DShader.shader @@ -26,12 +26,12 @@ Shader "G3D/Autostereo" float4 frag (v2f i) : SV_Target { // Start der Berechnung von dynamische Daten - int xScreenCoords = int(i.screenPos.x) + viewport_pos_x; // transform x position from viewport to screen coordinates + int xScreenCoords = int(i.screenPos.x) + v_pos_x; // transform x position from viewport to screen coordinates // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left) // The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity. - int yScreenCoords = int(i.screenPos.y) + viewport_pos_y; // transform y position from viewport to screen coordinates + int yScreenCoords = int(i.screenPos.y) + v_pos_y; // transform y position from viewport to screen coordinates if (isleft == 0) { - yScreenCoords = screen_height - yScreenCoords ; // invertieren für rechts geneigte Linse + yScreenCoords = s_height - yScreenCoords; // invertieren für rechts geneigte Linse } int yw = int(yScreenCoords * zwinkel) / nwinkel; // Winkelberechnung für die Renderberechnung diff --git a/Resources/G3DShaderMultiview.shader b/Resources/G3DShaderMultiview.shader index 5f08ddf..eafb498 100644 --- a/Resources/G3DShaderMultiview.shader +++ b/Resources/G3DShaderMultiview.shader @@ -88,9 +88,9 @@ Shader "G3D/AutostereoMultiview" { return sampleFromView(nativeViewCount - 0 - 1, i.uv); - float yPos = screen_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) + float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) - float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(viewport_pos_x, viewport_pos_y); + float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); int3 viewIndices = getSubPixelViewIndices(computedScreenPos); float2 uvCoords = i.uv; diff --git a/Resources/G3DShaderMultiviewMosaic.shader b/Resources/G3DShaderMultiviewMosaic.shader index 8bcc803..4b7478f 100644 --- a/Resources/G3DShaderMultiviewMosaic.shader +++ b/Resources/G3DShaderMultiviewMosaic.shader @@ -41,9 +41,9 @@ Shader "G3D/AutostereoMultiviewMosaic" float4 frag (v2f i) : SV_Target { - float yPos = screen_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) + float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) - float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(viewport_pos_x, viewport_pos_y); + float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); int3 viewIndices = getSubPixelViewIndices(computedScreenPos); float2 uvCoords = i.uv; From 8b2f48e322358d16bc7cbf6759f747b6aa4474ff Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 10 Dec 2025 10:50:05 +0100 Subject: [PATCH 080/149] update rendertextures when window size changes --- G3DCamera.cs | 133 +++++++++++++----- .../HDRP/G3DHDRPViewGenerationPass.cs | 36 +++-- 2 files changed, 119 insertions(+), 50 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 85b7abf..7f10de7 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -274,8 +274,17 @@ private float scaledHalfCameraWidthAtStart private Material viewGenerationMaterial; #if G3D_HDRP private G3DHDRPViewGenerationPass viewGenerationPass; + private G3DHDRPDepthMapPrePass depthMosaicPass; + + /// + /// Used for view generation mosaic rendering. + /// + private RenderTexture mosaicTexture; + private RTHandle rtHandleMosaic; #endif + private RenderTexture[] colorRenderTextures = null; + // TODO Handle viewport resizing/ moving #region Initialization @@ -419,32 +428,13 @@ private void initCustomPass() if (generateViews) { // add depth mosaic generation pass - G3DHDRPDepthMapPrePass depthMosaicPass = + depthMosaicPass = customPassVolume.AddPassOfType(typeof(G3DHDRPDepthMapPrePass)) as G3DHDRPDepthMapPrePass; depthMosaicPass.cameras = cameras; depthMosaicPass.internalCameraCount = internalCameraCount; - depthMosaicPass.indivDepthTextures = new RenderTexture[internalCameraCount]; - for (int i = 0; i < internalCameraCount; i++) - { - // TODO update render texture sizes when screen size changes - - float width = Screen.width; - float height = Screen.height; - - width = width * (renderResolutionScale / 100f); - height = height * (renderResolutionScale / 100f); - - RenderTexture depthTexture = new RenderTexture( - (int)width, - (int)height, - 16, - RenderTextureFormat.Depth - ); - depthTexture.Create(); - depthMosaicPass.indivDepthTextures[i] = depthTexture; - } + recreateDepthTextures(); // add multiview generation pass viewGenerationPass = @@ -477,14 +467,7 @@ private void initCustomPass() ); // add autostereo mosaic generation pass - RenderTexture mosaicTexture = new RenderTexture( - mainCamera.pixelWidth, - mainCamera.pixelHeight, - 0, - RenderTextureFormat.ARGB32, - RenderTextureReadWrite.sRGB - ); - mosaicTexture.enableRandomWrite = true; + recreateMosaicTexture(); if (debugRendering == false) { @@ -494,10 +477,6 @@ private void initCustomPass() finalAutostereoGeneration.fullscreenPassMaterial = material; finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; } - - RTHandle rtHandleMosaic = RTHandles.Alloc(mosaicTexture); - material.SetTexture("_colorMosaic", rtHandleMosaic); - viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } else { @@ -507,6 +486,64 @@ private void initCustomPass() customPass.materialPassName = "G3DFullScreen3D"; } } + + private void recreateDepthTextures() + { + if (depthMosaicPass.indivDepthTextures != null) + { + for (int i = 0; i < depthMosaicPass.indivDepthTextures.Length; i++) + { + if (depthMosaicPass.indivDepthTextures[i] != null) + { + depthMosaicPass.indivDepthTextures[i].Release(); + } + } + } + + depthMosaicPass.indivDepthTextures = new RenderTexture[internalCameraCount]; + for (int i = 0; i < internalCameraCount; i++) + { + float width = Screen.width; + float height = Screen.height; + + width = width * (renderResolutionScale / 100f); + height = height * (renderResolutionScale / 100f); + + RenderTexture depthTexture = new RenderTexture( + (int)width, + (int)height, + 16, + RenderTextureFormat.Depth + ); + depthTexture.Create(); + depthMosaicPass.indivDepthTextures[i] = depthTexture; + } + } + + private void recreateMosaicTexture() + { + if (mosaicTexture != null) + { + mosaicTexture.Release(); + } + + mosaicTexture = new RenderTexture( + mainCamera.pixelWidth, + mainCamera.pixelHeight, + 0, + RenderTextureFormat.ARGB32, + RenderTextureReadWrite.sRGB + ); + mosaicTexture.enableRandomWrite = true; + + if (rtHandleMosaic != null) + { + RTHandles.Release(rtHandleMosaic); + } + rtHandleMosaic = RTHandles.Alloc(mosaicTexture); + material.SetTexture("_colorMosaic", rtHandleMosaic); + viewGenerationPass.mosaicImageHandle = rtHandleMosaic; + } #endif void OnApplicationQuit() @@ -1324,20 +1361,40 @@ public void updateShaderRenderTextures() for (int i = 0; i < MAX_CAMERAS; i++) cameras[i].targetTexture?.Release(); - RenderTexture[] renderTextures = new RenderTexture[internalCameraCount]; + for (int i = 0; i < colorRenderTextures?.Length; i++) + { + if (colorRenderTextures[i] != null) + colorRenderTextures[i].Release(); + } + + colorRenderTextures = new RenderTexture[internalCameraCount]; if (generateViews) { - addRenderTextureToCamera(renderTextures, 0, 0, "_leftCamTex"); // left camera - addRenderTextureToCamera(renderTextures, 1, internalCameraCount / 2, "_middleCamTex"); // middle camera - addRenderTextureToCamera(renderTextures, 2, internalCameraCount - 1, "_rightCamTex"); // right camera + addRenderTextureToCamera(colorRenderTextures, 0, 0, "_leftCamTex"); // left camera + addRenderTextureToCamera( + colorRenderTextures, + 1, + internalCameraCount / 2, + "_middleCamTex" + ); // middle camera + addRenderTextureToCamera( + colorRenderTextures, + 2, + internalCameraCount - 1, + "_rightCamTex" + ); // right camera + + recreateDepthTextures(); + recreateMosaicTexture(); + viewGenerationPass.updateRenderResolution(new Vector2Int(Screen.width, Screen.height)); } else { //set only those we need for (int i = 0; i < internalCameraCount; i++) { - addRenderTextureToCamera(renderTextures, i, i); + addRenderTextureToCamera(colorRenderTextures, i, i); } } } diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs index 2319a5d..84c999f 100644 --- a/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs +++ b/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs @@ -77,13 +77,26 @@ public void init(Vector2Int resolution, AntialiasingMode mode) setAntiAliasingMode(mode); } - private void CreateComputeShaderResultTexture() + public void updateRenderResolution(Vector2Int resolution) { - // release old texture if it exists - if (computeShaderResultTexture) + if (renderResolution.x == resolution.x && renderResolution.y == resolution.y) + { + return; + } + + renderResolution = resolution; + CreateComputeShaderResultTexture(); + + if (antialiasingMode == AntialiasingMode.SMAA) { - computeShaderResultTexture?.Release(); + CreateSMAATextures(renderResolution.x, renderResolution.y); } + } + + private void CreateComputeShaderResultTexture() + { + // release old texture if it exists + computeShaderResultTexture?.Release(); computeShaderResultTextureHandle?.Release(); computeShaderResultTexture = new RenderTexture( @@ -96,6 +109,11 @@ private void CreateComputeShaderResultTexture() computeShaderResultTexture.enableRandomWrite = true; computeShaderResultTexture.Create(); computeShaderResultTextureHandle = RTHandles.Alloc(computeShaderResultTexture); + + if (blitMaterial != null) + { + blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); + } } private void CreateSMAATextures(int width, int height) @@ -148,14 +166,8 @@ public void setAntiAliasingMode(AntialiasingMode mode) private void releaseSMAATextures() { - if (smaaEdgesTex) - { - smaaEdgesTex?.Release(); - } - if (smaaBlendTex) - { - smaaBlendTex?.Release(); - } + smaaEdgesTex?.Release(); + smaaBlendTex?.Release(); smaaBlendTexHandle?.Release(); smaaEdgesTexHandle?.Release(); } From 8bdd6bbc1a2cead63fa3e8e797de2275dd7528d3 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 12 Dec 2025 12:04:53 +0100 Subject: [PATCH 081/149] fix merge --- G3DCamera.cs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index ad66b7f..b134010 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -381,26 +381,6 @@ void Start() } } -#if G3D_HDRP - // init fullscreen postprocessing for hd render pipeline - var customPassVolume = gameObject.AddComponent(); - customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; - customPassVolume.isGlobal = true; - // Make the volume invisible in the inspector - customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; - customPass = customPassVolume.AddPassOfType(typeof(G3DHDRPCustomPass)) as G3DHDRPCustomPass; - customPass.fullscreenPassMaterial = material; - customPass.materialPassName = "G3DFullScreen3D"; - - antialiasingMode = mainCamera.GetComponent().antialiasing; -#endif - -#if G3D_URP - customPass = new G3DUrpScriptableRenderPass(material); - antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing; - mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None; -#endif - headtrackingHandler = new HeadtrackingHandler(focusDistance); updateCameras(); From 367e5c91ee9d037cd18b99693bf4e750704f4fe5 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 17 Dec 2025 09:56:08 +0100 Subject: [PATCH 082/149] fix depth maps --- G3DCamera.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/G3DCamera.cs b/G3DCamera.cs index b134010..dd6d297 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -1389,6 +1389,14 @@ public void updateShaderRenderTextures() ); // right camera recreateDepthTextures(); + for (int i = 0; i < internalCameraCount; i++) + { + viewGenerationPass.fullscreenPassMaterial.SetTexture( + "_depthMap" + i, + depthMosaicPass.indivDepthTextures[i], + RenderTextureSubElement.Depth + ); + } recreateMosaicTexture(); viewGenerationPass.updateRenderResolution(new Vector2Int(Screen.width, Screen.height)); } From 081156761fe508c0435602c231dae76f542b2c56 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 17 Dec 2025 15:52:47 +0100 Subject: [PATCH 083/149] use culling mask of main cam for depth pass --- G3DCamera.cs | 4 ++++ Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index dd6d297..9373134 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -288,6 +288,7 @@ private float scaledHalfCameraWidthAtStart #endif private RenderTexture[] colorRenderTextures = null; + private int mainCamCullingMask = -1; // TODO Handle viewport resizing/ moving @@ -305,6 +306,7 @@ void Start() } mainCamera = GetComponent(); + mainCamCullingMask = mainCamera.cullingMask; oldRenderResolutionScale = renderResolutionScale; setupCameras(); @@ -437,6 +439,7 @@ private void initCustomPass() depthMosaicPass = customPassVolume.AddPassOfType(typeof(G3DHDRPDepthMapPrePass)) as G3DHDRPDepthMapPrePass; + depthMosaicPass.cullingMask = mainCamCullingMask; depthMosaicPass.cameras = cameras; depthMosaicPass.internalCameraCount = internalCameraCount; @@ -797,6 +800,7 @@ private void initCamerasAndParents() cameras[i].clearFlags = mainCamera.clearFlags; cameras[i].backgroundColor = mainCamera.backgroundColor; cameras[i].targetDisplay = mainCamera.targetDisplay; + cameras[i].cullingMask = mainCamera.cullingMask; #if G3D_HDRP cameras[i].gameObject.AddComponent(); diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs b/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs index b7c23ee..bc25ed8 100644 --- a/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs +++ b/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs @@ -14,6 +14,8 @@ internal class G3DHDRPDepthMapPrePass : FullScreenCustomPass public bool excludeLayer = false; public int layerToExclude = 3; + public int cullingMask = -1; + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } protected override void Execute(CustomPassContext ctx) @@ -24,6 +26,8 @@ protected override void Execute(CustomPassContext ctx) return; } + LayerMask depthLayerMask = (LayerMask)cullingMask; + // render depth maps for (int i = 0; i < internalCameraCount; i++) { @@ -56,8 +60,8 @@ protected override void Execute(CustomPassContext ctx) bakingCamera, indivDepthTextures[i], ClearFlag.Depth, - bakingCamera.cullingMask, - overrideRenderState: overrideDepthTest + depthLayerMask + // overrideRenderState: overrideDepthTest ); } } From fb488373922fc41cac295a1ea53680d351448655 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 8 Jan 2026 15:29:28 +0100 Subject: [PATCH 084/149] fix issue with camera disabled on startup a disabled camera on startup can cause the custom pass material to loose its render texture handles. reinitializing them if the camera gets enabled prevents this issue --- G3DCamera.cs | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 29ba3ff..4cc233e 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -452,8 +452,6 @@ private void initCustomPass() customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationPass)) as G3DHDRPViewGenerationPass; viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); - viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); - viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; viewGenerationPass.materialPassName = "G3DViewGeneration"; @@ -1020,8 +1018,29 @@ private void OnBeginCamera(ScriptableRenderContext context, Camera cam) #endregion #region Updates + private bool mainCamInactiveLastFrame = false; + void Update() { + if (mainCamera.enabled == false) + { + // enable all cameras if main camera is disabled + for (int i = 0; i < internalCameraCount; i++) + { + cameras[i].gameObject.SetActive(false); + } + mainCamInactiveLastFrame = true; + return; + } + + if (mainCamInactiveLastFrame) + { + // recreate shader render textures when main camera was inactive last frame + updateShaderRenderTextures(); + } + + mainCamInactiveLastFrame = false; + // update the shader parameters (only in diorama mode) if (mode == G3DCameraMode.DIORAMA) { @@ -1174,6 +1193,12 @@ private void updateShaderParameters() } material?.SetInt(Shader.PropertyToID("mosaic_rows"), 4); material?.SetInt(Shader.PropertyToID("mosaic_columns"), 4); + + if (generateViews) + { + viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); + viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); + } } } @@ -1408,7 +1433,7 @@ public void updateShaderRenderTextures() recreateDepthTextures(); for (int i = 0; i < internalCameraCount; i++) { - viewGenerationPass.fullscreenPassMaterial.SetTexture( + viewGenerationMaterial.SetTexture( "_depthMap" + i, depthMosaicPass.indivDepthTextures[i], RenderTextureSubElement.Depth From a4b8e712530e8b7e6a074605174defea49ba32c5 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 12 Jan 2026 15:40:06 +0100 Subject: [PATCH 085/149] removed erroneous return --- Resources/G3DShaderMultiview.shader | 2 -- 1 file changed, 2 deletions(-) diff --git a/Resources/G3DShaderMultiview.shader b/Resources/G3DShaderMultiview.shader index eafb498..9df6928 100644 --- a/Resources/G3DShaderMultiview.shader +++ b/Resources/G3DShaderMultiview.shader @@ -86,8 +86,6 @@ Shader "G3D/AutostereoMultiview" float4 frag (v2f i) : SV_Target { - return sampleFromView(nativeViewCount - 0 - 1, i.uv); - float yPos = s_height - i.screenPos.y; // invert y coordinate to account for different coordinates between glsl and hlsl (original shader written in glsl) float2 computedScreenPos = float2(i.screenPos.x, i.screenPos.y) + float2(v_pos_x, v_pos_y); From ee42971df4736406d58ffb537dbd3d7bbb1cff64 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 9 Feb 2026 11:01:07 +0100 Subject: [PATCH 086/149] fix merge --- G3DCameraMosaicMultiview.cs | 4 +++- Resources/G3DShaderMultiviewMosaic.shader | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/G3DCameraMosaicMultiview.cs b/G3DCameraMosaicMultiview.cs index 27be0aa..5577436 100644 --- a/G3DCameraMosaicMultiview.cs +++ b/G3DCameraMosaicMultiview.cs @@ -282,12 +282,15 @@ private void setCorrectMosaicTexture() { case MosaicMode.RenderTexture: material.SetTexture("mosaictexture", renderTexture, RenderTextureSubElement.Color); + material.SetTexture("_colorMosaic", renderTexture, RenderTextureSubElement.Color); break; case MosaicMode.Video: material.SetTexture("mosaictexture", renderTexture, RenderTextureSubElement.Color); + material.SetTexture("_colorMosaic", renderTexture, RenderTextureSubElement.Color); break; case MosaicMode.Image: material.SetTexture("mosaictexture", image); + material.SetTexture("_colorMosaic", image); break; } } @@ -296,7 +299,6 @@ public void reinitializeShader() { material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); setCorrectMosaicTexture(); - material.SetTexture("_colorMosaic", mosaicTexture, RenderTextureSubElement.Color); updateScreenViewportProperties(); updateShaderParameters(); diff --git a/Resources/G3DShaderMultiviewMosaic.shader b/Resources/G3DShaderMultiviewMosaic.shader index 605f09d..8fc617b 100644 --- a/Resources/G3DShaderMultiviewMosaic.shader +++ b/Resources/G3DShaderMultiviewMosaic.shader @@ -66,17 +66,17 @@ Shader "G3D/AutostereoMultiviewMosaic" // 250 corresponds to a black view if(viewIndices.x != 250) { float2 mappedUVCoords = calculateUVForMosaic(viewIndices.x, uvCoords); - float4 tmpColorX = mosaictexture.Sample(samplermosaictexture, mappedUVCoords); + float4 tmpColorX = _colorMosaic.Sample(sampler_colorMosaic, mappedUVCoords); color.x = tmpColorX.x; } if(viewIndices.y != 250) { float2 mappedUVCoords = calculateUVForMosaic(viewIndices.y, uvCoords); - float4 tmpColorY = mosaictexture.Sample(samplermosaictexture, mappedUVCoords); + float4 tmpColorY = _colorMosaic.Sample(sampler_colorMosaic, mappedUVCoords); color.y = tmpColorY.y; } if(viewIndices.z != 250) { float2 mappedUVCoords = calculateUVForMosaic(viewIndices.z, uvCoords); - float4 tmpColorZ = mosaictexture.Sample(samplermosaictexture, mappedUVCoords); + float4 tmpColorZ = _colorMosaic.Sample(sampler_colorMosaic, mappedUVCoords); color.z = tmpColorZ.z; } } From 5fcd88e59bac28f2fc47c146832fe26608fe844a Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 23 Feb 2026 14:52:56 +0100 Subject: [PATCH 087/149] use custom rthandlesystem; reset rthandle reference size --- G3DCamera.cs | 96 +++++++++---------- .../RenderPipelines/HDRP/G3DHDRPCustomPass.cs | 12 +++ .../HDRP/G3DHDRPDepthMapPrePass.cs | 52 +++++++++- .../HDRP/G3DHDRPViewGenerationPass.cs | 32 ++++--- 4 files changed, 123 insertions(+), 69 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 72aaed6..effc242 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -364,7 +364,7 @@ void Start() invertIndexMap, invertIndexMapIndices ); - + if (useVectorMapViewGeneration) { int invert = shaderParameters.leftLensOrientation == 1 ? 1 : -1; @@ -411,8 +411,6 @@ private void initCustomPass() depthMosaicPass.cameras = cameras; depthMosaicPass.internalCameraCount = internalCameraCount; - recreateDepthTextures(); - // add multiview generation pass viewGenerationPass = customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationPass)) @@ -423,14 +421,8 @@ private void initCustomPass() viewGenerationPass.materialPassName = "G3DViewGeneration"; viewGenerationPass.cameras = cameras; viewGenerationPass.internalCameraCount = internalCameraCount; - for (int i = 0; i < internalCameraCount; i++) - { - viewGenerationPass.fullscreenPassMaterial.SetTexture( - "_depthMap" + i, - depthMosaicPass.indivDepthTextures[i], - RenderTextureSubElement.Depth - ); - } + + recreateDepthTextures(); viewGenerationPass.indivDepthMaps = depthMosaicPass.indivDepthTextures; viewGenerationPass.debugRendering = debugRendering; viewGenerationPass.fillHoles = isFillingHoles; @@ -464,34 +456,15 @@ private void initCustomPass() private void recreateDepthTextures() { - if (depthMosaicPass.indivDepthTextures != null) - { - for (int i = 0; i < depthMosaicPass.indivDepthTextures.Length; i++) - { - if (depthMosaicPass.indivDepthTextures[i] != null) - { - depthMosaicPass.indivDepthTextures[i].Release(); - } - } - } + depthMosaicPass.recreateDepthTextures(renderResolutionScale); - depthMosaicPass.indivDepthTextures = new RenderTexture[internalCameraCount]; for (int i = 0; i < internalCameraCount; i++) { - float width = Screen.width; - float height = Screen.height; - - width = width * (renderResolutionScale / 100f); - height = height * (renderResolutionScale / 100f); - - RenderTexture depthTexture = new RenderTexture( - (int)width, - (int)height, - 16, - RenderTextureFormat.Depth + viewGenerationPass.fullscreenPassMaterial.SetTexture( + "_depthMap" + i, + depthMosaicPass.indivDepthTextures[i], + RenderTextureSubElement.Depth ); - depthTexture.Create(); - depthMosaicPass.indivDepthTextures[i] = depthTexture; } } @@ -513,9 +486,9 @@ private void recreateMosaicTexture() if (rtHandleMosaic != null) { - RTHandles.Release(rtHandleMosaic); + G3DHDRPCustomPass.GetRTHandleSystem().Release(rtHandleMosaic); } - rtHandleMosaic = RTHandles.Alloc(mosaicTexture); + rtHandleMosaic = G3DHDRPCustomPass.GetRTHandleSystem().Alloc(mosaicTexture); material.SetTexture("_colorMosaic", rtHandleMosaic); viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } @@ -593,6 +566,14 @@ void OnValidate() } } + /// + /// Us this to run OnValidate after parameters changed from a script. + /// + public void Validate() + { + OnValidate(); + } + public void loadShaderParametersFromCalibrationFile() { if (calibrationFile == null) @@ -1006,9 +987,14 @@ private void OnBeginCamera(ScriptableRenderContext context, Camera cam) #region Updates private bool mainCamInactiveLastFrame = false; + private bool windowMovedLastFrame = false; + private bool windowResizedLastFrame = false; void Update() { + windowMovedLastFrame = windowMoved(); + windowResizedLastFrame = windowResized(); + if (mainCamera.enabled == false) { // enable all cameras if main camera is disabled @@ -1042,20 +1028,32 @@ void Update() updateCameras(); updateShaderParameters(); - bool screenSizeChanged = false; - if (windowResized() || windowMoved()) + if (windowResizedLastFrame || windowMovedLastFrame) { updateScreenViewportProperties(); - screenSizeChanged = true; } - if ( - screenSizeChanged - || cameraCountChanged - || oldRenderResolutionScale != renderResolutionScale - ) + bool recreatedRenderTextures = false; + + if (windowResizedLastFrame) + { + recreatedRenderTextures = true; +#if G3D_HDRP + G3DHDRPCustomPass.GetRTHandleSystem().ResetReferenceSize(Screen.width, Screen.height); + // TODO this is only needed to reset the size of the cameras internal render targets + // Find a way to avoid this line... + RTHandles.ResetReferenceSize(Screen.width, Screen.height); +#endif + } + + if (cameraCountChanged || oldRenderResolutionScale != renderResolutionScale) { oldRenderResolutionScale = renderResolutionScale; + recreatedRenderTextures = true; + } + + if (recreatedRenderTextures) + { updateShaderRenderTextures(); } @@ -1424,14 +1422,6 @@ public void updateShaderRenderTextures() ); // right camera recreateDepthTextures(); - for (int i = 0; i < internalCameraCount; i++) - { - viewGenerationMaterial.SetTexture( - "_depthMap" + i, - depthMosaicPass.indivDepthTextures[i], - RenderTextureSubElement.Depth - ); - } recreateMosaicTexture(); viewGenerationPass.updateRenderResolution(new Vector2Int(Screen.width, Screen.height)); } diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs b/Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs index 87b2456..31438c0 100644 --- a/Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs +++ b/Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs @@ -5,6 +5,18 @@ internal class G3DHDRPCustomPass : FullScreenCustomPass { + private static RTHandleSystem m_RTHandleSystem; + + public static RTHandleSystem GetRTHandleSystem() + { + if (m_RTHandleSystem == null) + { + m_RTHandleSystem = new RTHandleSystem(); + m_RTHandleSystem.Initialize(Screen.width, Screen.height); + } + return m_RTHandleSystem; + } + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } protected override void Execute(CustomPassContext ctx) diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs b/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs index bc25ed8..5162b27 100644 --- a/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs +++ b/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs @@ -9,7 +9,7 @@ internal class G3DHDRPDepthMapPrePass : FullScreenCustomPass public List cameras; public int internalCameraCount = 16; - public RenderTexture[] indivDepthTextures; + public RTHandle[] indivDepthTextures; public bool excludeLayer = false; public int layerToExclude = 3; @@ -18,6 +18,11 @@ internal class G3DHDRPDepthMapPrePass : FullScreenCustomPass protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } + protected override void Cleanup() + { + cleanupDepthTextures(); + } + protected override void Execute(CustomPassContext ctx) { var camera = ctx.hdCamera.camera; @@ -36,7 +41,7 @@ protected override void Execute(CustomPassContext ctx) // We need to be careful about the aspect ratio of render textures when doing the culling, otherwise it could result in objects poping: bakingCamera.aspect = Mathf.Max( bakingCamera.aspect, - indivDepthTextures[i].width / (float)indivDepthTextures[i].height + indivDepthTextures[i].referenceSize.x / (float)indivDepthTextures[i].referenceSize.y ); bakingCamera.TryGetCullingParameters(out var cullingParams); cullingParams.cullingOptions = CullingOptions.None; @@ -66,6 +71,47 @@ protected override void Execute(CustomPassContext ctx) } } + private void cleanupDepthTextures() + { + if (indivDepthTextures == null) + { + return; + } + for (int i = 0; i < indivDepthTextures.Length; i++) + { + if (indivDepthTextures[i] == null) + { + continue; + } + G3DHDRPCustomPass.GetRTHandleSystem().Release(indivDepthTextures[i]); + } + } + + public void recreateDepthTextures(float renderResolutionScale = 1.0f) + { + cleanupDepthTextures(); + + indivDepthTextures = new RTHandle[internalCameraCount]; + for (int i = 0; i < internalCameraCount; i++) + { + float width = Screen.width; + float height = Screen.height; + + width = width * (renderResolutionScale / 100f); + height = height * (renderResolutionScale / 100f); + + RenderTexture depthTexture = new RenderTexture( + (int)width, + (int)height, + 16, + RenderTextureFormat.Depth + ); + depthTexture.Create(); + RTHandle handle = G3DHDRPCustomPass.GetRTHandleSystem().Alloc(depthTexture); + indivDepthTextures[i] = handle; + } + } + /// /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. /// If so returns true, otherwise returns false. @@ -95,8 +141,6 @@ out var mosaicCamera return true; } - - protected override void Cleanup() { } } diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs index 84c999f..4989000 100644 --- a/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs +++ b/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs @@ -40,7 +40,7 @@ internal class G3DHDRPViewGenerationPass : FullScreenCustomPass private Material blitMaterial; - public RenderTexture[] indivDepthMaps; + public RTHandle[] indivDepthMaps; private ComputeShader fxaaCompShader; private int fxaaKernel; @@ -105,10 +105,14 @@ private void CreateComputeShaderResultTexture() 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear - ); - computeShaderResultTexture.enableRandomWrite = true; + ) + { + enableRandomWrite = true + }; computeShaderResultTexture.Create(); - computeShaderResultTextureHandle = RTHandles.Alloc(computeShaderResultTexture); + computeShaderResultTextureHandle = G3DHDRPCustomPass + .GetRTHandleSystem() + .Alloc(computeShaderResultTexture); if (blitMaterial != null) { @@ -126,11 +130,13 @@ private void CreateSMAATextures(int width, int height) 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear - ); - smaaEdgesTex.name = "SMAAEdgesTex"; - smaaEdgesTex.enableRandomWrite = true; + ) + { + name = "SMAAEdgesTex", + enableRandomWrite = true + }; smaaEdgesTex.Create(); - smaaEdgesTexHandle = RTHandles.Alloc(smaaEdgesTex); + smaaEdgesTexHandle = G3DHDRPCustomPass.GetRTHandleSystem().Alloc(smaaEdgesTex); smaaBlendTex = new RenderTexture( width, @@ -138,11 +144,13 @@ private void CreateSMAATextures(int width, int height) 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear - ); - smaaBlendTex.name = "SMAABlendTex"; - smaaBlendTex.enableRandomWrite = true; + ) + { + name = "SMAABlendTex", + enableRandomWrite = true + }; smaaBlendTex.Create(); - smaaBlendTexHandle = RTHandles.Alloc(smaaBlendTex); + smaaBlendTexHandle = G3DHDRPCustomPass.GetRTHandleSystem().Alloc(smaaBlendTex); } public void setAntiAliasingMode(AntialiasingMode mode) From 71b2c3340b17cd598db9955229a244be7f32de85 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Mon, 23 Feb 2026 14:59:10 +0100 Subject: [PATCH 088/149] hide hdrp function in ifdef --- G3DCamera.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index effc242..0e68aec 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -1420,10 +1420,11 @@ public void updateShaderRenderTextures() internalCameraCount - 1, "_rightCamTex" ); // right camera - +#if G3D_HDRP recreateDepthTextures(); recreateMosaicTexture(); viewGenerationPass.updateRenderResolution(new Vector2Int(Screen.width, Screen.height)); +#endif } else { From 1065c497ef1fb036d3e4e96d1483b4c5950866ee Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 24 Feb 2026 11:12:03 +0100 Subject: [PATCH 089/149] disable sdk component on the fly --- G3DCamera.cs | 112 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 45 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 0e68aec..94a8221 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -247,6 +247,8 @@ private float scaledHalfCameraWidthAtStart private RenderTexture[] colorRenderTextures = null; private int mainCamCullingMask = -1; + private float originalMainFOV; + private CameraClearFlags originalMainClearFlags; // TODO Handle viewport resizing/ moving @@ -265,20 +267,10 @@ void Start() calibrationPath = calibrationPathOverwrite; } - mainCamera = GetComponent(); - mainCamCullingMask = mainCamera.cullingMask; + InitMainCamera(); oldRenderResolutionScale = renderResolutionScale; setupCameras(); - // create a focus plane object at focus distance from camera. - // then parent the camera parent to that object - // this way we can place the camera parent relative to the focus plane - - // disable rendering on main camera after other cameras have been created and settings have been copied over - // otherwise the secondary cameras are initialized wrong - mainCamera.cullingMask = 0; //disable rendering of the main camera - mainCamera.clearFlags = CameraClearFlags.Color; - reinitializeShader(); #if G3D_HDRP initCustomPass(); @@ -391,6 +383,18 @@ void Start() } } + private void InitMainCamera() + { + if (mainCamera != null) + { + return; + } + mainCamera = GetComponent(); + mainCamCullingMask = mainCamera.cullingMask; + originalMainFOV = mainCamera.fieldOfView; + originalMainClearFlags = mainCamera.clearFlags; + } + #if G3D_HDRP private void initCustomPass() { @@ -499,6 +503,51 @@ void OnApplicationQuit() deinitLibrary(); } + private void OnEnable() + { + InitMainCamera(); + + mainCamera.cullingMask = 0; //disable rendering of the main camera + mainCamera.clearFlags = CameraClearFlags.Color; + +#if G3D_URP + RenderPipelineManager.beginCameraRendering += OnBeginCamera; +#endif + } + + private void OnDisable() + { + // disable all cameras when the script is disabled + for (int i = 0; i < MAX_CAMERAS; i++) + { + cameras[i].gameObject.SetActive(false); + } + + mainCamera.cullingMask = mainCamCullingMask; + mainCamera.fieldOfView = originalMainFOV; + mainCamera.clearFlags = originalMainClearFlags; + +#if G3D_URP + RenderPipelineManager.beginCameraRendering -= OnBeginCamera; +#endif + } + +#if G3D_URP + private void OnBeginCamera(ScriptableRenderContext context, Camera cam) + { + // Use the EnqueuePass method to inject a custom render pass + cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass); + + if (mainCamera.GetUniversalAdditionalCameraData().renderPostProcessing) + { + for (int i = 0; i < MAX_CAMERAS; i++) + { + cameras[i].GetUniversalAdditionalCameraData().renderPostProcessing = true; + } + } + } +#endif + /// /// this variable is onle here to track changes made to the public calibration file from the editor. /// @@ -646,7 +695,7 @@ public void setupCameras(bool calledFromValidate = false) { if (mainCamera == null) { - mainCamera = GetComponent(); + InitMainCamera(); } if (Application.isPlaying && !calledFromValidate) { @@ -763,10 +812,10 @@ private void initCamerasAndParents() cameras[i].transform.SetParent(cameraParent.transform, true); cameras[i].gameObject.SetActive(false); cameras[i].transform.localRotation = Quaternion.identity; - cameras[i].clearFlags = mainCamera.clearFlags; + cameras[i].clearFlags = originalMainClearFlags; cameras[i].backgroundColor = mainCamera.backgroundColor; cameras[i].targetDisplay = mainCamera.targetDisplay; - cameras[i].cullingMask = mainCamera.cullingMask; + cameras[i].cullingMask = mainCamCullingMask; #if G3D_HDRP cameras[i].gameObject.AddComponent(); @@ -956,33 +1005,6 @@ private void reinitializeShader() material = new Material(Shader.Find("G3D/Autostereo")); } } - -#if G3D_URP - private void OnEnable() - { - RenderPipelineManager.beginCameraRendering += OnBeginCamera; - } - - private void OnDisable() - { - RenderPipelineManager.beginCameraRendering -= OnBeginCamera; - } - - private void OnBeginCamera(ScriptableRenderContext context, Camera cam) - { - // Use the EnqueuePass method to inject a custom render pass - cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass); - - if (mainCamera.GetUniversalAdditionalCameraData().renderPostProcessing) - { - for (int i = 0; i < MAX_CAMERAS; i++) - { - cameras[i].GetUniversalAdditionalCameraData().renderPostProcessing = true; - } - } - } -#endif - #endregion #region Updates @@ -1006,10 +1028,12 @@ void Update() return; } + bool recreatedRenderTextures = false; + if (mainCamInactiveLastFrame) { // recreate shader render textures when main camera was inactive last frame - updateShaderRenderTextures(); + recreatedRenderTextures = true; } mainCamInactiveLastFrame = false; @@ -1033,8 +1057,6 @@ void Update() updateScreenViewportProperties(); } - bool recreatedRenderTextures = false; - if (windowResizedLastFrame) { recreatedRenderTextures = true; @@ -1704,7 +1726,7 @@ void OnDrawGizmos() if (mainCamera == null) { - mainCamera = GetComponent(); + InitMainCamera(); if (mainCamera == null) { Debug.LogError( From 133e3a700c8c3bfdfa9728fc0c6dad4c10b2d05a Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 27 Feb 2026 11:41:37 +0100 Subject: [PATCH 090/149] copy dlss settngs to internal cameras make OnDisable public --- G3DCamera.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 94a8221..99fdbbd 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -515,7 +515,7 @@ private void OnEnable() #endif } - private void OnDisable() + public void OnDisable() { // disable all cameras when the script is disabled for (int i = 0; i < MAX_CAMERAS; i++) @@ -819,6 +819,22 @@ private void initCamerasAndParents() #if G3D_HDRP cameras[i].gameObject.AddComponent(); + + HDAdditionalCameraData hdData = cameras[i].GetComponent(); + HDAdditionalCameraData hdDataMain = + mainCamera.GetComponent(); + + hdData.allowDynamicResolution = hdDataMain.allowDynamicResolution; + hdData.allowDeepLearningSuperSampling = hdDataMain.allowDeepLearningSuperSampling; + hdData.deepLearningSuperSamplingUseCustomQualitySettings = + hdDataMain.deepLearningSuperSamplingUseCustomQualitySettings; + hdData.deepLearningSuperSamplingQuality = + hdDataMain.deepLearningSuperSamplingQuality; + hdData.deepLearningSuperSamplingUseCustomAttributes = + hdDataMain.deepLearningSuperSamplingUseCustomAttributes; + hdData.deepLearningSuperSamplingUseOptimalSettings = + hdDataMain.deepLearningSuperSamplingUseOptimalSettings; + #endif } } From 9b3eec4b54c3f670bdfbff39e60d3d307c666cd6 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 27 Feb 2026 15:19:10 +0100 Subject: [PATCH 091/149] fix --- Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs b/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs index 5162b27..dbc8cca 100644 --- a/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs +++ b/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs @@ -18,10 +18,7 @@ internal class G3DHDRPDepthMapPrePass : FullScreenCustomPass protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } - protected override void Cleanup() - { - cleanupDepthTextures(); - } + protected override void Cleanup() { } protected override void Execute(CustomPassContext ctx) { From 75fd958d6413b8eeebd2dd2c17c164c0d1201bc4 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 27 Feb 2026 15:54:23 +0100 Subject: [PATCH 092/149] remove dlss settings from extra cameras --- G3DCamera.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 99fdbbd..d2c6313 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -819,22 +819,6 @@ private void initCamerasAndParents() #if G3D_HDRP cameras[i].gameObject.AddComponent(); - - HDAdditionalCameraData hdData = cameras[i].GetComponent(); - HDAdditionalCameraData hdDataMain = - mainCamera.GetComponent(); - - hdData.allowDynamicResolution = hdDataMain.allowDynamicResolution; - hdData.allowDeepLearningSuperSampling = hdDataMain.allowDeepLearningSuperSampling; - hdData.deepLearningSuperSamplingUseCustomQualitySettings = - hdDataMain.deepLearningSuperSamplingUseCustomQualitySettings; - hdData.deepLearningSuperSamplingQuality = - hdDataMain.deepLearningSuperSamplingQuality; - hdData.deepLearningSuperSamplingUseCustomAttributes = - hdDataMain.deepLearningSuperSamplingUseCustomAttributes; - hdData.deepLearningSuperSamplingUseOptimalSettings = - hdDataMain.deepLearningSuperSamplingUseOptimalSettings; - #endif } } From 35c250bf880b450f0991d187fd86fb03540a9ddb Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 11 Mar 2026 12:08:00 +0100 Subject: [PATCH 093/149] fix for cameras are null --- G3DCamera.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index d2c6313..4674c69 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -517,10 +517,13 @@ private void OnEnable() public void OnDisable() { - // disable all cameras when the script is disabled - for (int i = 0; i < MAX_CAMERAS; i++) + if (cameras != null && cameras.Count > 0) { - cameras[i].gameObject.SetActive(false); + // disable all cameras when the script is disabled + for (int i = 0; i < MAX_CAMERAS; i++) + { + cameras[i]?.gameObject.SetActive(false); + } } mainCamera.cullingMask = mainCamCullingMask; From 5dabe9472101cfb6cb02d64df1415cc3c66a8052 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 11 Mar 2026 15:14:02 +0100 Subject: [PATCH 094/149] only hardcode 16 views for view generation --- G3DCamera.cs | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 4674c69..84107a0 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -254,6 +254,11 @@ private float scaledHalfCameraWidthAtStart #region Initialization + void Awake() + { + InitMainCamera(); + } + void Start() { previousValues.init(); @@ -267,7 +272,6 @@ void Start() calibrationPath = calibrationPathOverwrite; } - InitMainCamera(); oldRenderResolutionScale = renderResolutionScale; setupCameras(); @@ -585,9 +589,12 @@ void OnValidate() CalibrationProvider calibration = CalibrationProvider.getFromString( calibrationFile.text ); - // internalCameraCount = getCameraCountFromCalibrationFile(calibration); - // TODO DO NOT HARD CODE THIS VALUE! - internalCameraCount = 16; + internalCameraCount = getCameraCountFromCalibrationFile(calibration); + if (generateViews) + { + // TODO DO NOT HARD CODE THIS VALUE! + internalCameraCount = 16; + } loadMultiviewViewSeparationFromCalibration(calibration); } else @@ -750,8 +757,14 @@ public void setupCameras(bool calledFromValidate = false) { loadMultiviewViewSeparationFromCalibration(calibration); // TODO DO NOT HARD CODE THIS VALUE! - internalCameraCount = 16; // default value for multiview mode - // internalCameraCount = NativeViewcount; + if (generateViews) + { + internalCameraCount = 16; // default value for multiview mode + } + else + { + internalCameraCount = NativeViewcount; + } } else { @@ -1352,9 +1365,14 @@ private bool updateCameraCountBasedOnMode() } else if (mode == G3DCameraMode.MULTIVIEW) { - // internalCameraCount = getCameraCountFromCalibrationFile(); - // TODO DO NOT HARD CODE THIS VALUE! - internalCameraCount = 16; + if (generateViews) + { + // TODO DO NOT HARD CODE THIS VALUE! + internalCameraCount = 16; + } + { + internalCameraCount = getCameraCountFromCalibrationFile(); + } if (internalCameraCount > MAX_CAMERAS) { internalCameraCount = MAX_CAMERAS; @@ -1962,4 +1980,9 @@ public G3DShaderParameters GetShaderParameters() return shaderParameters; } } + + public void setOriginalFOV(float fov) + { + originalMainFOV = fov; + } } From a9cbf56861743a6f0604eb91eaca8ea97e99f489 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Fri, 13 Mar 2026 12:37:27 +0100 Subject: [PATCH 095/149] added taa handling (wip) --- G3DCamera.cs | 27 +++++++++++++++++++ .../HDRP/G3DHDRPViewGenerationPass.cs | 9 +++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 84107a0..3d88499 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -895,6 +895,10 @@ private AntialiasingMode getCameraAAMode() { aaMode = AntialiasingMode.FXAA; } + else if (antialiasingMode == HDAdditionalCameraData.AntialiasingMode.TemporalAntialiasing) + { + aaMode = AntialiasingMode.TAA; + } return aaMode; } #endif @@ -1303,7 +1307,30 @@ void updateCameras() HDAdditionalCameraData hdAdditionalCameraData = camera.gameObject.GetComponent(); if (hdAdditionalCameraData != null) + { + HDAdditionalCameraData mainData = + mainCamera.GetComponent(); hdAdditionalCameraData.antialiasing = antialiasingMode; + if ( + antialiasingMode + == HDAdditionalCameraData.AntialiasingMode.TemporalAntialiasing + ) + { + hdAdditionalCameraData.taaAntiFlicker = mainData.taaAntiFlicker; + hdAdditionalCameraData.taaAntiHistoryRinging = + mainData.taaAntiHistoryRinging; + hdAdditionalCameraData.taaBaseBlendFactor = mainData.taaBaseBlendFactor; + hdAdditionalCameraData.taaHistorySharpening = mainData.taaHistorySharpening; + hdAdditionalCameraData.taaJitterScale = mainData.taaJitterScale; + hdAdditionalCameraData.taaMotionVectorRejection = + mainData.taaMotionVectorRejection; + hdAdditionalCameraData.taaRingingReduction = mainData.taaRingingReduction; + hdAdditionalCameraData.taaSharpenMode = mainData.taaSharpenMode; + hdAdditionalCameraData.taaSharpenStrength = mainData.taaSharpenStrength; + hdAdditionalCameraData.TAAQuality = mainData.TAAQuality; + } + } + #endif } diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs index 4989000..1f7f122 100644 --- a/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs +++ b/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs @@ -8,7 +8,8 @@ internal enum AntialiasingMode { None, FXAA, - SMAA + SMAA, + TAA } internal class G3DHDRPViewGenerationPass : FullScreenCustomPass @@ -162,7 +163,11 @@ public void setAntiAliasingMode(AntialiasingMode mode) } antialiasingMode = mode; - if (mode == AntialiasingMode.None || mode == AntialiasingMode.FXAA) + if ( + mode == AntialiasingMode.None + || mode == AntialiasingMode.FXAA + || mode == AntialiasingMode.TAA + ) { releaseSMAATextures(); } From e6e77d776d0e177d62edc5e14f18d8b6de6058d9 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 17 Mar 2026 12:46:28 +0100 Subject: [PATCH 096/149] renamed custom pass files --- .../HDRP/{G3DHDRPCustomPass.cs => HDRPCustomPass.cs} | 0 .../HDRP/{G3DHDRPCustomPass.cs.meta => HDRPCustomPass.cs.meta} | 0 .../HDRP/{G3DHDRPDepthMapPrePass.cs => HDRPDepthMapPrePass.cs} | 0 ...G3DHDRPDepthMapPrePass.cs.meta => HDRPDepthMapPrePass.cs.meta} | 0 .../{G3DHDRPViewGenerationPass.cs => HDRPViewGenerationPass.cs} | 0 ...PViewGenerationPass.cs.meta => HDRPViewGenerationPass.cs.meta} | 0 .../{G3DUrpScriptableRenderPass.cs => UrpScriptableRenderPass.cs} | 0 ...riptableRenderPass.cs.meta => UrpScriptableRenderPass.cs.meta} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename Scripts/RenderPipelines/HDRP/{G3DHDRPCustomPass.cs => HDRPCustomPass.cs} (100%) rename Scripts/RenderPipelines/HDRP/{G3DHDRPCustomPass.cs.meta => HDRPCustomPass.cs.meta} (100%) rename Scripts/RenderPipelines/HDRP/{G3DHDRPDepthMapPrePass.cs => HDRPDepthMapPrePass.cs} (100%) rename Scripts/RenderPipelines/HDRP/{G3DHDRPDepthMapPrePass.cs.meta => HDRPDepthMapPrePass.cs.meta} (100%) rename Scripts/RenderPipelines/HDRP/{G3DHDRPViewGenerationPass.cs => HDRPViewGenerationPass.cs} (100%) rename Scripts/RenderPipelines/HDRP/{G3DHDRPViewGenerationPass.cs.meta => HDRPViewGenerationPass.cs.meta} (100%) rename Scripts/RenderPipelines/URP/{G3DUrpScriptableRenderPass.cs => UrpScriptableRenderPass.cs} (100%) rename Scripts/RenderPipelines/URP/{G3DUrpScriptableRenderPass.cs.meta => UrpScriptableRenderPass.cs.meta} (100%) diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs b/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs similarity index 100% rename from Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs rename to Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs.meta b/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs.meta similarity index 100% rename from Scripts/RenderPipelines/HDRP/G3DHDRPCustomPass.cs.meta rename to Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs.meta diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs b/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs similarity index 100% rename from Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs rename to Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs.meta b/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs.meta similarity index 100% rename from Scripts/RenderPipelines/HDRP/G3DHDRPDepthMapPrePass.cs.meta rename to Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs.meta diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs similarity index 100% rename from Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs rename to Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs diff --git a/Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs.meta b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs.meta similarity index 100% rename from Scripts/RenderPipelines/HDRP/G3DHDRPViewGenerationPass.cs.meta rename to Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs.meta diff --git a/Scripts/RenderPipelines/URP/G3DUrpScriptableRenderPass.cs b/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs similarity index 100% rename from Scripts/RenderPipelines/URP/G3DUrpScriptableRenderPass.cs rename to Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs diff --git a/Scripts/RenderPipelines/URP/G3DUrpScriptableRenderPass.cs.meta b/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs.meta similarity index 100% rename from Scripts/RenderPipelines/URP/G3DUrpScriptableRenderPass.cs.meta rename to Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs.meta From 9c2c2c70ad0c618bdc924e90415a69ab867bb38d Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 17 Mar 2026 12:58:50 +0100 Subject: [PATCH 097/149] renamed custom passes --- G3DCamera.cs | 33 ++++++++----------- G3DCameraMosaicMultiview.cs | 12 +++---- .../RenderPipelines/HDRP/HDRPCustomPass.cs | 2 +- .../HDRP/HDRPDepthMapPrePass.cs | 6 ++-- .../HDRP/HDRPViewGenerationPass.cs | 8 ++--- .../URP/UrpScriptableRenderPass.cs | 4 +-- 6 files changed, 30 insertions(+), 35 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 791010b..9ca7fd0 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -3,10 +3,6 @@ using System.IO; using UnityEngine; using UnityEngine.Rendering; -#if UNITY_EDITOR - -#endif - #if G3D_HDRP using UnityEngine.Rendering.HighDefinition; #endif @@ -179,7 +175,7 @@ public class G3DCamera .None; #endif #if G3D_URP - private G3DUrpScriptableRenderPass customPass; + private UrpScriptableRenderPass customPass; private AntialiasingMode antialiasingMode = AntialiasingMode.None; #endif @@ -235,8 +231,8 @@ private float scaledHalfCameraWidthAtStart private Material viewGenerationMaterial; #if G3D_HDRP - private G3DHDRPViewGenerationPass viewGenerationPass; - private G3DHDRPDepthMapPrePass depthMosaicPass; + private HDRPViewGenerationPass viewGenerationPass; + private HDRPDepthMapPrePass depthMosaicPass; /// /// Used for view generation mosaic rendering. @@ -283,7 +279,7 @@ void Start() #endif #if G3D_URP - customPass = new G3DUrpScriptableRenderPass(material); + customPass = new UrpScriptableRenderPass(material); antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing; mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None; #endif @@ -413,16 +409,15 @@ private void initCustomPass() { // add depth mosaic generation pass depthMosaicPass = - customPassVolume.AddPassOfType(typeof(G3DHDRPDepthMapPrePass)) - as G3DHDRPDepthMapPrePass; + customPassVolume.AddPassOfType(typeof(HDRPDepthMapPrePass)) as HDRPDepthMapPrePass; depthMosaicPass.cullingMask = mainCamCullingMask; depthMosaicPass.cameras = cameras; depthMosaicPass.internalCameraCount = internalCameraCount; // add multiview generation pass viewGenerationPass = - customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationPass)) - as G3DHDRPViewGenerationPass; + customPassVolume.AddPassOfType(typeof(HDRPViewGenerationPass)) + as HDRPViewGenerationPass; viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; @@ -455,8 +450,8 @@ private void initCustomPass() } else { - G3DHDRPCustomPass customPass = - customPassVolume.AddPassOfType(typeof(G3DHDRPCustomPass)) as G3DHDRPCustomPass; + HDRPCustomPass customPass = + customPassVolume.AddPassOfType(typeof(HDRPCustomPass)) as HDRPCustomPass; customPass.fullscreenPassMaterial = material; customPass.materialPassName = "G3DFullScreen3D"; } @@ -494,9 +489,9 @@ private void recreateMosaicTexture() if (rtHandleMosaic != null) { - G3DHDRPCustomPass.GetRTHandleSystem().Release(rtHandleMosaic); + HDRPCustomPass.GetRTHandleSystem().Release(rtHandleMosaic); } - rtHandleMosaic = G3DHDRPCustomPass.GetRTHandleSystem().Alloc(mosaicTexture); + rtHandleMosaic = HDRPCustomPass.GetRTHandleSystem().Alloc(mosaicTexture); material.SetTexture("_colorMosaic", rtHandleMosaic); viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } @@ -1082,7 +1077,7 @@ void Update() { recreatedRenderTextures = true; #if G3D_HDRP - G3DHDRPCustomPass.GetRTHandleSystem().ResetReferenceSize(Screen.width, Screen.height); + HDRPCustomPass.GetRTHandleSystem().ResetReferenceSize(Screen.width, Screen.height); // TODO this is only needed to reset the size of the cameras internal render targets // Find a way to avoid this line... RTHandles.ResetReferenceSize(Screen.width, Screen.height); @@ -1560,8 +1555,8 @@ Matrix4x4 mainCamProjectionMatrix // This function only does something when you use the SRP render pipeline. // when using either URP or HRDP image combination is handled in the respective renderpasses. - // URP -> G3DUrpScriptableRenderPass.cs - // HDRP -> G3DHDRPCustomPass.cs + // URP -> UrpScriptableRenderPass.cs + // HDRP -> HDRPCustomPass.cs void OnRenderImage(RenderTexture source, RenderTexture destination) { // This is where the material and shader are applied to the camera image. diff --git a/G3DCameraMosaicMultiview.cs b/G3DCameraMosaicMultiview.cs index 5577436..8401bbb 100644 --- a/G3DCameraMosaicMultiview.cs +++ b/G3DCameraMosaicMultiview.cs @@ -92,10 +92,10 @@ public class G3DCameraMosaicMultiview : MonoBehaviour private Camera mainCamera; private Material material; #if G3D_HDRP - private G3DHDRPCustomPass customPass; + private HDRPCustomPass customPass; #endif #if G3D_URP - private G3DUrpScriptableRenderPass customPass; + private UrpScriptableRenderPass customPass; #endif private ShaderHandles shaderHandles; @@ -146,13 +146,13 @@ void Start() customPassVolume.isGlobal = true; // Make the volume invisible in the inspector customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; - customPass = customPassVolume.AddPassOfType(typeof(G3DHDRPCustomPass)) as G3DHDRPCustomPass; + customPass = customPassVolume.AddPassOfType(typeof(HDRPCustomPass)) as HDRPCustomPass; customPass.fullscreenPassMaterial = material; customPass.materialPassName = "G3DFullScreen3D"; #endif #if G3D_URP - customPass = new G3DUrpScriptableRenderPass(material); + customPass = new UrpScriptableRenderPass(material); #endif // Do this last to ensure custom passes are already set up @@ -490,8 +490,8 @@ private bool windowMoved() // This function only does something when you use the SRP render pipeline. // when using either URP or HRDP image combination is handled in the respective renderpasses. - // URP -> G3DUrpScriptableRenderPass.cs - // HDRP -> G3DHDRPCustomPass.cs + // URP -> UrpScriptableRenderPass.cs + // HDRP -> HDRPCustomPass.cs void OnRenderImage(RenderTexture source, RenderTexture destination) { // This is where the material and shader are applied to the camera image. diff --git a/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs b/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs index 26018db..ef26adc 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs @@ -3,7 +3,7 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -internal class G3DHDRPCustomPass : FullScreenCustomPass +internal class HDRPCustomPass : FullScreenCustomPass { private static RTHandleSystem m_RTHandleSystem; diff --git a/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs b/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs index dbc8cca..e84d599 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs @@ -4,7 +4,7 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -internal class G3DHDRPDepthMapPrePass : FullScreenCustomPass +internal class HDRPDepthMapPrePass : FullScreenCustomPass { public List cameras; public int internalCameraCount = 16; @@ -80,7 +80,7 @@ private void cleanupDepthTextures() { continue; } - G3DHDRPCustomPass.GetRTHandleSystem().Release(indivDepthTextures[i]); + HDRPCustomPass.GetRTHandleSystem().Release(indivDepthTextures[i]); } } @@ -104,7 +104,7 @@ public void recreateDepthTextures(float renderResolutionScale = 1.0f) RenderTextureFormat.Depth ); depthTexture.Create(); - RTHandle handle = G3DHDRPCustomPass.GetRTHandleSystem().Alloc(depthTexture); + RTHandle handle = HDRPCustomPass.GetRTHandleSystem().Alloc(depthTexture); indivDepthTextures[i] = handle; } } diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs index 1f7f122..b75403d 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs @@ -12,7 +12,7 @@ internal enum AntialiasingMode TAA } -internal class G3DHDRPViewGenerationPass : FullScreenCustomPass +internal class HDRPViewGenerationPass : FullScreenCustomPass { public RTHandle leftColorMapHandle; public RTHandle rightColorMapHandle; @@ -111,7 +111,7 @@ private void CreateComputeShaderResultTexture() enableRandomWrite = true }; computeShaderResultTexture.Create(); - computeShaderResultTextureHandle = G3DHDRPCustomPass + computeShaderResultTextureHandle = HDRPCustomPass .GetRTHandleSystem() .Alloc(computeShaderResultTexture); @@ -137,7 +137,7 @@ private void CreateSMAATextures(int width, int height) enableRandomWrite = true }; smaaEdgesTex.Create(); - smaaEdgesTexHandle = G3DHDRPCustomPass.GetRTHandleSystem().Alloc(smaaEdgesTex); + smaaEdgesTexHandle = HDRPCustomPass.GetRTHandleSystem().Alloc(smaaEdgesTex); smaaBlendTex = new RenderTexture( width, @@ -151,7 +151,7 @@ private void CreateSMAATextures(int width, int height) enableRandomWrite = true }; smaaBlendTex.Create(); - smaaBlendTexHandle = G3DHDRPCustomPass.GetRTHandleSystem().Alloc(smaaBlendTex); + smaaBlendTexHandle = HDRPCustomPass.GetRTHandleSystem().Alloc(smaaBlendTex); } public void setAntiAliasingMode(AntialiasingMode mode) diff --git a/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs b/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs index 5c3b4bd..bc1080d 100644 --- a/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs +++ b/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs @@ -5,7 +5,7 @@ using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.Universal; -internal class G3DUrpScriptableRenderPass : ScriptableRenderPass +internal class UrpScriptableRenderPass : ScriptableRenderPass { Material m_Material; @@ -16,7 +16,7 @@ private class PassData internal Material blitMaterial; } - public G3DUrpScriptableRenderPass(Material material) + public UrpScriptableRenderPass(Material material) { m_Material = material; renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing; From f47775069d8c780c8372de39d2c1d8adb7251b6b Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 17 Mar 2026 13:09:32 +0100 Subject: [PATCH 098/149] moved custom passed to own namespace --- G3DCamera.cs | 49 +- G3DCameraMosaicMultiview.cs | 13 +- .../RenderPipelines/HDRP/HDRPCustomPass.cs | 96 +- .../HDRP/HDRPDepthMapPrePass.cs | 216 ++--- .../HDRP/HDRPViewGenerationPass.cs | 893 +++++++++--------- .../URP/UrpScriptableRenderPass.cs | 253 ++--- 6 files changed, 775 insertions(+), 745 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 9ca7fd0..0ef6128 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -175,7 +175,7 @@ public class G3DCamera .None; #endif #if G3D_URP - private UrpScriptableRenderPass customPass; + private g3d.UrpScriptableRenderPass customPass; private AntialiasingMode antialiasingMode = AntialiasingMode.None; #endif @@ -231,8 +231,8 @@ private float scaledHalfCameraWidthAtStart private Material viewGenerationMaterial; #if G3D_HDRP - private HDRPViewGenerationPass viewGenerationPass; - private HDRPDepthMapPrePass depthMosaicPass; + private g3d.HDRPViewGenerationPass viewGenerationPass; + private g3d.HDRPDepthMapPrePass depthMosaicPass; /// /// Used for view generation mosaic rendering. @@ -279,7 +279,7 @@ void Start() #endif #if G3D_URP - customPass = new UrpScriptableRenderPass(material); + customPass = new g3d.UrpScriptableRenderPass(material); antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing; mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None; #endif @@ -409,15 +409,16 @@ private void initCustomPass() { // add depth mosaic generation pass depthMosaicPass = - customPassVolume.AddPassOfType(typeof(HDRPDepthMapPrePass)) as HDRPDepthMapPrePass; + customPassVolume.AddPassOfType(typeof(g3d.HDRPDepthMapPrePass)) + as g3d.HDRPDepthMapPrePass; depthMosaicPass.cullingMask = mainCamCullingMask; depthMosaicPass.cameras = cameras; depthMosaicPass.internalCameraCount = internalCameraCount; // add multiview generation pass viewGenerationPass = - customPassVolume.AddPassOfType(typeof(HDRPViewGenerationPass)) - as HDRPViewGenerationPass; + customPassVolume.AddPassOfType(typeof(g3d.HDRPViewGenerationPass)) + as g3d.HDRPViewGenerationPass; viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; @@ -430,7 +431,7 @@ private void initCustomPass() viewGenerationPass.debugRendering = debugRendering; viewGenerationPass.fillHoles = isFillingHoles; viewGenerationPass.holeFillingRadius = holeFillingRadius; - AntialiasingMode aaMode = getCameraAAMode(); + g3d.AntialiasingMode aaMode = getCameraAAMode(); viewGenerationPass.init( new Vector2Int(mainCamera.pixelWidth, mainCamera.pixelHeight), aaMode @@ -441,17 +442,17 @@ private void initCustomPass() if (debugRendering == false) { - G3DHDRPViewGenerationMosaicPass finalAutostereoGeneration = - customPassVolume.AddPassOfType(typeof(G3DHDRPViewGenerationMosaicPass)) - as G3DHDRPViewGenerationMosaicPass; + g3d.HDRPViewGenerationMosaicPass finalAutostereoGeneration = + customPassVolume.AddPassOfType(typeof(g3d.HDRPViewGenerationMosaicPass)) + as g3d.HDRPViewGenerationMosaicPass; finalAutostereoGeneration.fullscreenPassMaterial = material; finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; } } else { - HDRPCustomPass customPass = - customPassVolume.AddPassOfType(typeof(HDRPCustomPass)) as HDRPCustomPass; + g3d.HDRPCustomPass customPass = + customPassVolume.AddPassOfType(typeof(g3d.HDRPCustomPass)) as g3d.HDRPCustomPass; customPass.fullscreenPassMaterial = material; customPass.materialPassName = "G3DFullScreen3D"; } @@ -489,9 +490,9 @@ private void recreateMosaicTexture() if (rtHandleMosaic != null) { - HDRPCustomPass.GetRTHandleSystem().Release(rtHandleMosaic); + g3d.HDRPCustomPass.GetRTHandleSystem().Release(rtHandleMosaic); } - rtHandleMosaic = HDRPCustomPass.GetRTHandleSystem().Alloc(mosaicTexture); + rtHandleMosaic = g3d.HDRPCustomPass.GetRTHandleSystem().Alloc(mosaicTexture); material.SetTexture("_colorMosaic", rtHandleMosaic); viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } @@ -875,25 +876,25 @@ private Vector2Int getDisplayResolutionFromCalibrationFile() } #if G3D_HDRP - private AntialiasingMode getCameraAAMode() + private g3d.AntialiasingMode getCameraAAMode() { - AntialiasingMode aaMode = AntialiasingMode.None; + g3d.AntialiasingMode aaMode = g3d.AntialiasingMode.None; if ( antialiasingMode == HDAdditionalCameraData.AntialiasingMode.SubpixelMorphologicalAntiAliasing ) { - aaMode = AntialiasingMode.SMAA; + aaMode = g3d.AntialiasingMode.SMAA; } else if ( antialiasingMode == HDAdditionalCameraData.AntialiasingMode.FastApproximateAntialiasing ) { - aaMode = AntialiasingMode.FXAA; + aaMode = g3d.AntialiasingMode.FXAA; } else if (antialiasingMode == HDAdditionalCameraData.AntialiasingMode.TemporalAntialiasing) { - aaMode = AntialiasingMode.TAA; + aaMode = g3d.AntialiasingMode.TAA; } return aaMode; } @@ -1077,7 +1078,7 @@ void Update() { recreatedRenderTextures = true; #if G3D_HDRP - HDRPCustomPass.GetRTHandleSystem().ResetReferenceSize(Screen.width, Screen.height); + g3d.HDRPCustomPass.GetRTHandleSystem().ResetReferenceSize(Screen.width, Screen.height); // TODO this is only needed to reset the size of the cameras internal render targets // Find a way to avoid this line... RTHandles.ResetReferenceSize(Screen.width, Screen.height); @@ -1101,7 +1102,7 @@ void Update() viewGenerationPass.holeFillingRadius = holeFillingRadius; viewGenerationPass.fillHoles = isFillingHoles; - AntialiasingMode aaMode = getCameraAAMode(); + g3d.AntialiasingMode aaMode = getCameraAAMode(); viewGenerationPass.setAntiAliasingMode(aaMode); } #endif @@ -1555,8 +1556,8 @@ Matrix4x4 mainCamProjectionMatrix // This function only does something when you use the SRP render pipeline. // when using either URP or HRDP image combination is handled in the respective renderpasses. - // URP -> UrpScriptableRenderPass.cs - // HDRP -> HDRPCustomPass.cs + // URP -> g3d.UrpScriptableRenderPass.cs + // HDRP -> g3d.HDRPCustomPass.cs void OnRenderImage(RenderTexture source, RenderTexture destination) { // This is where the material and shader are applied to the camera image. diff --git a/G3DCameraMosaicMultiview.cs b/G3DCameraMosaicMultiview.cs index 8401bbb..09d17a6 100644 --- a/G3DCameraMosaicMultiview.cs +++ b/G3DCameraMosaicMultiview.cs @@ -92,10 +92,10 @@ public class G3DCameraMosaicMultiview : MonoBehaviour private Camera mainCamera; private Material material; #if G3D_HDRP - private HDRPCustomPass customPass; + private g3d.HDRPCustomPass customPass; #endif #if G3D_URP - private UrpScriptableRenderPass customPass; + private g3d.UrpScriptableRenderPass customPass; #endif private ShaderHandles shaderHandles; @@ -146,13 +146,14 @@ void Start() customPassVolume.isGlobal = true; // Make the volume invisible in the inspector customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; - customPass = customPassVolume.AddPassOfType(typeof(HDRPCustomPass)) as HDRPCustomPass; + customPass = + customPassVolume.AddPassOfType(typeof(g3d.HDRPCustomPass)) as g3d.HDRPCustomPass; customPass.fullscreenPassMaterial = material; customPass.materialPassName = "G3DFullScreen3D"; #endif #if G3D_URP - customPass = new UrpScriptableRenderPass(material); + customPass = new g3d.UrpScriptableRenderPass(material); #endif // Do this last to ensure custom passes are already set up @@ -490,8 +491,8 @@ private bool windowMoved() // This function only does something when you use the SRP render pipeline. // when using either URP or HRDP image combination is handled in the respective renderpasses. - // URP -> UrpScriptableRenderPass.cs - // HDRP -> HDRPCustomPass.cs + // URP -> g3d.UrpScriptableRenderPass.cs + // HDRP -> g3d.HDRPCustomPass.cs void OnRenderImage(RenderTexture source, RenderTexture destination) { // This is where the material and shader are applied to the camera image. diff --git a/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs b/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs index ef26adc..1c99c8c 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs @@ -3,67 +3,69 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -internal class HDRPCustomPass : FullScreenCustomPass +namespace g3d { - private static RTHandleSystem m_RTHandleSystem; - - public static RTHandleSystem GetRTHandleSystem() + internal class HDRPCustomPass : FullScreenCustomPass { - if (m_RTHandleSystem == null) + private static RTHandleSystem m_RTHandleSystem; + + public static RTHandleSystem GetRTHandleSystem() { - m_RTHandleSystem = new RTHandleSystem(); - m_RTHandleSystem.Initialize(Screen.width, Screen.height); + if (m_RTHandleSystem == null) + { + m_RTHandleSystem = new RTHandleSystem(); + m_RTHandleSystem.Initialize(Screen.width, Screen.height); + } + return m_RTHandleSystem; } - return m_RTHandleSystem; - } - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } - protected override void Execute(CustomPassContext ctx) - { - var camera = ctx.hdCamera.camera; - if (shouldPerformBlit(camera)) + protected override void Execute(CustomPassContext ctx) { - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - CoreUtils.DrawFullScreen( - ctx.cmd, - fullscreenPassMaterial, - ctx.propertyBlock, - shaderPassId: 0 - ); + var camera = ctx.hdCamera.camera; + if (shouldPerformBlit(camera)) + { + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + CoreUtils.DrawFullScreen( + ctx.cmd, + fullscreenPassMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); + } } - } - /// - /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. - /// If so returns true, otherwise returns false. - /// - /// - /// - static bool shouldPerformBlit(Camera camera) - { - if (camera.cameraType != CameraType.Game) - return false; - bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; // only do something if our component is enabled + /// + /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. + /// If so returns true, otherwise returns false. + /// + /// + /// + static bool shouldPerformBlit(Camera camera) + { + if (camera.cameraType != CameraType.Game) + return false; + bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); + bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; // only do something if our component is enabled - bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent( - out var mosaicCamera - ); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; // same check if it is a mosaic camera + bool isMosaicMultiviewCamera = + camera.gameObject.TryGetComponent(out var mosaicCamera); + bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; // same check if it is a mosaic camera - if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) - return false; + if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) + return false; - // skip all cameras created by the G3D Camera script - if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) - { - return false; + // skip all cameras created by the G3D Camera script + if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) + { + return false; + } + + return true; } - return true; + protected override void Cleanup() { } } - - protected override void Cleanup() { } } #endif diff --git a/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs b/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs index e84d599..73c2507 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs @@ -4,141 +4,143 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -internal class HDRPDepthMapPrePass : FullScreenCustomPass +namespace g3d { - public List cameras; - public int internalCameraCount = 16; + internal class HDRPDepthMapPrePass : FullScreenCustomPass + { + public List cameras; + public int internalCameraCount = 16; - public RTHandle[] indivDepthTextures; + public RTHandle[] indivDepthTextures; - public bool excludeLayer = false; - public int layerToExclude = 3; + public bool excludeLayer = false; + public int layerToExclude = 3; - public int cullingMask = -1; + public int cullingMask = -1; - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } - protected override void Cleanup() { } + protected override void Cleanup() { } - protected override void Execute(CustomPassContext ctx) - { - var camera = ctx.hdCamera.camera; - if (isMainG3DCamera(camera) == false) + protected override void Execute(CustomPassContext ctx) { - return; - } - - LayerMask depthLayerMask = (LayerMask)cullingMask; - - // render depth maps - for (int i = 0; i < internalCameraCount; i++) - { - Camera bakingCamera = cameras[i]; - - // We need to be careful about the aspect ratio of render textures when doing the culling, otherwise it could result in objects poping: - bakingCamera.aspect = Mathf.Max( - bakingCamera.aspect, - indivDepthTextures[i].referenceSize.x / (float)indivDepthTextures[i].referenceSize.y - ); - bakingCamera.TryGetCullingParameters(out var cullingParams); - cullingParams.cullingOptions = CullingOptions.None; - camera.cullingMask &= ~(1 << bakingCamera.gameObject.layer); - - if (excludeLayer) + var camera = ctx.hdCamera.camera; + if (isMainG3DCamera(camera) == false) { - int layerMask = 1 << layerToExclude; - cullingParams.cullingMask = ~(uint)layerMask; + return; } - // Assign the custom culling result to the context - // so it'll be used for the following operations - ctx.cullingResults = ctx.renderContext.Cull(ref cullingParams); - var overrideDepthTest = new RenderStateBlock(RenderStateMask.Depth) + LayerMask depthLayerMask = (LayerMask)cullingMask; + + // render depth maps + for (int i = 0; i < internalCameraCount; i++) { - depthState = new DepthState(true, CompareFunction.LessEqual) - }; - CustomPassUtils.RenderDepthFromCamera( - ctx, - bakingCamera, - indivDepthTextures[i], - ClearFlag.Depth, - depthLayerMask - // overrideRenderState: overrideDepthTest - ); + Camera bakingCamera = cameras[i]; + + // We need to be careful about the aspect ratio of render textures when doing the culling, otherwise it could result in objects poping: + bakingCamera.aspect = Mathf.Max( + bakingCamera.aspect, + indivDepthTextures[i].referenceSize.x + / (float)indivDepthTextures[i].referenceSize.y + ); + bakingCamera.TryGetCullingParameters(out var cullingParams); + cullingParams.cullingOptions = CullingOptions.None; + camera.cullingMask &= ~(1 << bakingCamera.gameObject.layer); + + if (excludeLayer) + { + int layerMask = 1 << layerToExclude; + cullingParams.cullingMask = ~(uint)layerMask; + } + + // Assign the custom culling result to the context + // so it'll be used for the following operations + ctx.cullingResults = ctx.renderContext.Cull(ref cullingParams); + var overrideDepthTest = new RenderStateBlock(RenderStateMask.Depth) + { + depthState = new DepthState(true, CompareFunction.LessEqual) + }; + CustomPassUtils.RenderDepthFromCamera( + ctx, + bakingCamera, + indivDepthTextures[i], + ClearFlag.Depth, + depthLayerMask + // overrideRenderState: overrideDepthTest + ); + } } - } - private void cleanupDepthTextures() - { - if (indivDepthTextures == null) + private void cleanupDepthTextures() { - return; + if (indivDepthTextures == null) + { + return; + } + for (int i = 0; i < indivDepthTextures.Length; i++) + { + if (indivDepthTextures[i] == null) + { + continue; + } + HDRPCustomPass.GetRTHandleSystem().Release(indivDepthTextures[i]); + } } - for (int i = 0; i < indivDepthTextures.Length; i++) + + public void recreateDepthTextures(float renderResolutionScale = 1.0f) { - if (indivDepthTextures[i] == null) + cleanupDepthTextures(); + + indivDepthTextures = new RTHandle[internalCameraCount]; + for (int i = 0; i < internalCameraCount; i++) { - continue; + float width = Screen.width; + float height = Screen.height; + + width = width * (renderResolutionScale / 100f); + height = height * (renderResolutionScale / 100f); + + RenderTexture depthTexture = new RenderTexture( + (int)width, + (int)height, + 16, + RenderTextureFormat.Depth + ); + depthTexture.Create(); + RTHandle handle = HDRPCustomPass.GetRTHandleSystem().Alloc(depthTexture); + indivDepthTextures[i] = handle; } - HDRPCustomPass.GetRTHandleSystem().Release(indivDepthTextures[i]); } - } - public void recreateDepthTextures(float renderResolutionScale = 1.0f) - { - cleanupDepthTextures(); - - indivDepthTextures = new RTHandle[internalCameraCount]; - for (int i = 0; i < internalCameraCount; i++) + /// + /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. + /// If so returns true, otherwise returns false. + /// + /// + /// + static bool isMainG3DCamera(Camera camera) { - float width = Screen.width; - float height = Screen.height; - - width = width * (renderResolutionScale / 100f); - height = height * (renderResolutionScale / 100f); - - RenderTexture depthTexture = new RenderTexture( - (int)width, - (int)height, - 16, - RenderTextureFormat.Depth - ); - depthTexture.Create(); - RTHandle handle = HDRPCustomPass.GetRTHandleSystem().Alloc(depthTexture); - indivDepthTextures[i] = handle; - } - } + if (camera.cameraType != CameraType.Game) + return false; + bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); + bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; - /// - /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. - /// If so returns true, otherwise returns false. - /// - /// - /// - static bool isMainG3DCamera(Camera camera) - { - if (camera.cameraType != CameraType.Game) - return false; - bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; + bool isMosaicMultiviewCamera = + camera.gameObject.TryGetComponent(out var mosaicCamera); + bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; - bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent( - out var mosaicCamera - ); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; + if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) + return false; - if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) - return false; + // skip all cameras created by the G3D Camera script + if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) + { + return false; + } - // skip all cameras created by the G3D Camera script - if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) - { - return false; + return true; } - - return true; } } - #endif diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs index b75403d..7b4c8ee 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs @@ -4,548 +4,565 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -internal enum AntialiasingMode +namespace g3d { - None, - FXAA, - SMAA, - TAA -} + internal enum AntialiasingMode + { + None, + FXAA, + SMAA, + TAA + } -internal class HDRPViewGenerationPass : FullScreenCustomPass -{ - public RTHandle leftColorMapHandle; - public RTHandle rightColorMapHandle; + internal class HDRPViewGenerationPass : FullScreenCustomPass + { + public RTHandle leftColorMapHandle; + public RTHandle rightColorMapHandle; - public List cameras; - public int internalCameraCount = 16; + public List cameras; + public int internalCameraCount = 16; - public RTHandle mosaicImageHandle; + public RTHandle mosaicImageHandle; - private ComputeShader holeFillingCompShader; - private int holeFillingKernel; + private ComputeShader holeFillingCompShader; + private int holeFillingKernel; - public RenderTexture computeShaderResultTexture; - public RTHandle computeShaderResultTextureHandle; + public RenderTexture computeShaderResultTexture; + public RTHandle computeShaderResultTextureHandle; - private RenderTexture smaaEdgesTex; - private RTHandle smaaEdgesTexHandle; - private RenderTexture smaaBlendTex; - private RTHandle smaaBlendTexHandle; + private RenderTexture smaaEdgesTex; + private RTHandle smaaEdgesTexHandle; + private RenderTexture smaaBlendTex; + private RTHandle smaaBlendTexHandle; - public int holeFillingRadius; + public int holeFillingRadius; - public bool fillHoles; + public bool fillHoles; - public bool debugRendering; + public bool debugRendering; - private Material blitMaterial; + private Material blitMaterial; - public RTHandle[] indivDepthMaps; + public RTHandle[] indivDepthMaps; - private ComputeShader fxaaCompShader; - private int fxaaKernel; + private ComputeShader fxaaCompShader; + private int fxaaKernel; - private Material smaaMaterial; - private AntialiasingMode antialiasingMode = AntialiasingMode.None; + private Material smaaMaterial; + private AntialiasingMode antialiasingMode = AntialiasingMode.None; - public Vector2Int renderResolution = new Vector2Int(1920, 1080); + public Vector2Int renderResolution = new Vector2Int(1920, 1080); - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) - { - if (fillHoles) + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { - holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); - holeFillingKernel = holeFillingCompShader.FindKernel("main"); - } - - fxaaCompShader = Resources.Load("G3DFXAA"); - fxaaKernel = fxaaCompShader.FindKernel("FXAA"); + if (fillHoles) + { + holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); + holeFillingKernel = holeFillingCompShader.FindKernel("main"); + } - smaaMaterial = new Material(Shader.Find("G3D/SMAA")); - smaaMaterial.SetTexture("_AreaTex", Resources.Load("SMAA/AreaTex")); - // Import search tex as PNG because I can't get Unity to work with an R8 DDS file properly. - smaaMaterial.SetTexture("_SearchTex", Resources.Load("SMAA/SearchTex")); + fxaaCompShader = Resources.Load("G3DFXAA"); + fxaaKernel = fxaaCompShader.FindKernel("FXAA"); - blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); - blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); - } + smaaMaterial = new Material(Shader.Find("G3D/SMAA")); + smaaMaterial.SetTexture("_AreaTex", Resources.Load("SMAA/AreaTex")); + // Import search tex as PNG because I can't get Unity to work with an R8 DDS file properly. + smaaMaterial.SetTexture("_SearchTex", Resources.Load("SMAA/SearchTex")); - public void init(Vector2Int resolution, AntialiasingMode mode) - { - renderResolution = resolution; - CreateComputeShaderResultTexture(); - setAntiAliasingMode(mode); - } + blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); + blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); + } - public void updateRenderResolution(Vector2Int resolution) - { - if (renderResolution.x == resolution.x && renderResolution.y == resolution.y) + public void init(Vector2Int resolution, AntialiasingMode mode) { - return; + renderResolution = resolution; + CreateComputeShaderResultTexture(); + setAntiAliasingMode(mode); } - renderResolution = resolution; - CreateComputeShaderResultTexture(); - - if (antialiasingMode == AntialiasingMode.SMAA) + public void updateRenderResolution(Vector2Int resolution) { - CreateSMAATextures(renderResolution.x, renderResolution.y); + if (renderResolution.x == resolution.x && renderResolution.y == resolution.y) + { + return; + } + + renderResolution = resolution; + CreateComputeShaderResultTexture(); + + if (antialiasingMode == AntialiasingMode.SMAA) + { + CreateSMAATextures(renderResolution.x, renderResolution.y); + } } - } - private void CreateComputeShaderResultTexture() - { - // release old texture if it exists - computeShaderResultTexture?.Release(); - computeShaderResultTextureHandle?.Release(); - - computeShaderResultTexture = new RenderTexture( - renderResolution.x, - renderResolution.y, - 0, - RenderTextureFormat.ARGB32, - RenderTextureReadWrite.Linear - ) + private void CreateComputeShaderResultTexture() { - enableRandomWrite = true - }; - computeShaderResultTexture.Create(); - computeShaderResultTextureHandle = HDRPCustomPass - .GetRTHandleSystem() - .Alloc(computeShaderResultTexture); - - if (blitMaterial != null) - { - blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); + // release old texture if it exists + computeShaderResultTexture?.Release(); + computeShaderResultTextureHandle?.Release(); + + computeShaderResultTexture = new RenderTexture( + renderResolution.x, + renderResolution.y, + 0, + RenderTextureFormat.ARGB32, + RenderTextureReadWrite.Linear + ) + { + enableRandomWrite = true + }; + computeShaderResultTexture.Create(); + computeShaderResultTextureHandle = HDRPCustomPass + .GetRTHandleSystem() + .Alloc(computeShaderResultTexture); + + if (blitMaterial != null) + { + blitMaterial.SetTexture( + Shader.PropertyToID("_mainTex"), + computeShaderResultTexture + ); + } } - } - private void CreateSMAATextures(int width, int height) - { - releaseSMAATextures(); - - smaaEdgesTex = new RenderTexture( - width, - height, - 0, - RenderTextureFormat.ARGB32, - RenderTextureReadWrite.Linear - ) - { - name = "SMAAEdgesTex", - enableRandomWrite = true - }; - smaaEdgesTex.Create(); - smaaEdgesTexHandle = HDRPCustomPass.GetRTHandleSystem().Alloc(smaaEdgesTex); - - smaaBlendTex = new RenderTexture( - width, - height, - 0, - RenderTextureFormat.ARGB32, - RenderTextureReadWrite.Linear - ) + private void CreateSMAATextures(int width, int height) { - name = "SMAABlendTex", - enableRandomWrite = true - }; - smaaBlendTex.Create(); - smaaBlendTexHandle = HDRPCustomPass.GetRTHandleSystem().Alloc(smaaBlendTex); - } + releaseSMAATextures(); - public void setAntiAliasingMode(AntialiasingMode mode) - { - AntialiasingMode oldMode = antialiasingMode; - if (oldMode == mode) - { - return; + smaaEdgesTex = new RenderTexture( + width, + height, + 0, + RenderTextureFormat.ARGB32, + RenderTextureReadWrite.Linear + ) + { + name = "SMAAEdgesTex", + enableRandomWrite = true + }; + smaaEdgesTex.Create(); + smaaEdgesTexHandle = HDRPCustomPass.GetRTHandleSystem().Alloc(smaaEdgesTex); + + smaaBlendTex = new RenderTexture( + width, + height, + 0, + RenderTextureFormat.ARGB32, + RenderTextureReadWrite.Linear + ) + { + name = "SMAABlendTex", + enableRandomWrite = true + }; + smaaBlendTex.Create(); + smaaBlendTexHandle = HDRPCustomPass.GetRTHandleSystem().Alloc(smaaBlendTex); } - antialiasingMode = mode; - if ( - mode == AntialiasingMode.None - || mode == AntialiasingMode.FXAA - || mode == AntialiasingMode.TAA - ) + public void setAntiAliasingMode(AntialiasingMode mode) { - releaseSMAATextures(); + AntialiasingMode oldMode = antialiasingMode; + if (oldMode == mode) + { + return; + } + + antialiasingMode = mode; + if ( + mode == AntialiasingMode.None + || mode == AntialiasingMode.FXAA + || mode == AntialiasingMode.TAA + ) + { + releaseSMAATextures(); + } + else if (mode == AntialiasingMode.SMAA) + { + CreateSMAATextures(renderResolution.x, renderResolution.y); + } } - else if (mode == AntialiasingMode.SMAA) + + private void releaseSMAATextures() { - CreateSMAATextures(renderResolution.x, renderResolution.y); + smaaEdgesTex?.Release(); + smaaBlendTex?.Release(); + smaaBlendTexHandle?.Release(); + smaaEdgesTexHandle?.Release(); } - } - private void releaseSMAATextures() - { - smaaEdgesTex?.Release(); - smaaBlendTex?.Release(); - smaaBlendTexHandle?.Release(); - smaaEdgesTexHandle?.Release(); - } - - private void setMatrix(CustomPassContext ctx, Matrix4x4 matrix, string name) - { - ctx.propertyBlock.SetMatrix(Shader.PropertyToID(name), matrix); - if (fillHoles) + private void setMatrix(CustomPassContext ctx, Matrix4x4 matrix, string name) { - ctx.cmd.SetComputeMatrixParam(holeFillingCompShader, name, matrix); + ctx.propertyBlock.SetMatrix(Shader.PropertyToID(name), matrix); + if (fillHoles) + { + ctx.cmd.SetComputeMatrixParam(holeFillingCompShader, name, matrix); + } } - } - private void addViewProjectionMatrix(CustomPassContext ctx, Camera camera, string name) - { - Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false); - Matrix4x4 viewMatrix = camera.worldToCameraMatrix; - Matrix4x4 viewProjectionMatrix = projectionMatrix * viewMatrix; - setMatrix(ctx, viewProjectionMatrix, name); - } + private void addViewProjectionMatrix(CustomPassContext ctx, Camera camera, string name) + { + Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false); + Matrix4x4 viewMatrix = camera.worldToCameraMatrix; + Matrix4x4 viewProjectionMatrix = projectionMatrix * viewMatrix; + setMatrix(ctx, viewProjectionMatrix, name); + } - protected override void Execute(CustomPassContext ctx) - { - var camera = ctx.hdCamera.camera; - HDAdditionalCameraData hdCamera = camera.GetComponent(); - if (isMainG3DCamera(camera)) + protected override void Execute(CustomPassContext ctx) { - runReprojection(ctx); - // color image now in mosaicImageHandle + var camera = ctx.hdCamera.camera; + HDAdditionalCameraData hdCamera = camera.GetComponent(); + if (isMainG3DCamera(camera)) + { + runReprojection(ctx); + // color image now in mosaicImageHandle - runHoleFilling(ctx); - runFXAA(ctx); - runSMAA(ctx, hdCamera); + runHoleFilling(ctx); + runFXAA(ctx); + runSMAA(ctx, hdCamera); - if (debugRendering) - { - if (fillHoles == false && antialiasingMode == AntialiasingMode.None) + if (debugRendering) { - blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), mosaicImageHandle); + if (fillHoles == false && antialiasingMode == AntialiasingMode.None) + { + blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), mosaicImageHandle); + } + else + { + blitMaterial.SetTexture( + Shader.PropertyToID("_mainTex"), + computeShaderResultTexture + ); + } + + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + CoreUtils.DrawFullScreen( + ctx.cmd, + blitMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); } else { + if (fillHoles == false && antialiasingMode == AntialiasingMode.None) + { + return; + } + blitMaterial.SetTexture( Shader.PropertyToID("_mainTex"), computeShaderResultTexture ); - } - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); - } - else - { - if (fillHoles == false && antialiasingMode == AntialiasingMode.None) - { - return; + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + CoreUtils.DrawFullScreen( + ctx.cmd, + blitMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); } - - blitMaterial.SetTexture( - Shader.PropertyToID("_mainTex"), - computeShaderResultTexture - ); - - CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); - CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); } } - } - - private void runReprojection(CustomPassContext ctx) - { - // upload all inv view projection matrices - Matrix4x4[] viewMatrices = new Matrix4x4[16]; - Matrix4x4[] invProjMatrices = new Matrix4x4[16]; - for (int i = 0; i < internalCameraCount; i++) + private void runReprojection(CustomPassContext ctx) { - Matrix4x4 projectionMatrixInner = GL.GetGPUProjectionMatrix( - cameras[i].projectionMatrix, - false - ); - Matrix4x4 viewMatrixInner = cameras[i].worldToCameraMatrix; + // upload all inv view projection matrices + Matrix4x4[] viewMatrices = new Matrix4x4[16]; + Matrix4x4[] invProjMatrices = new Matrix4x4[16]; - Matrix4x4 viewProjectionMatrixInner = projectionMatrixInner * viewMatrixInner; - Matrix4x4 invGPUProjMatrix = viewProjectionMatrixInner.inverse; + for (int i = 0; i < internalCameraCount; i++) + { + Matrix4x4 projectionMatrixInner = GL.GetGPUProjectionMatrix( + cameras[i].projectionMatrix, + false + ); + Matrix4x4 viewMatrixInner = cameras[i].worldToCameraMatrix; - setMatrix(ctx, invGPUProjMatrix, "inverseProjMatrix" + i); - setMatrix(ctx, cameras[i].worldToCameraMatrix, "viewMatrix" + i); + Matrix4x4 viewProjectionMatrixInner = projectionMatrixInner * viewMatrixInner; + Matrix4x4 invGPUProjMatrix = viewProjectionMatrixInner.inverse; - viewMatrices[i] = cameras[i].worldToCameraMatrix; - invProjMatrices[i] = invGPUProjMatrix; - } + setMatrix(ctx, invGPUProjMatrix, "inverseProjMatrix" + i); + setMatrix(ctx, cameras[i].worldToCameraMatrix, "viewMatrix" + i); - if (fillHoles) - { - ctx.cmd.SetComputeMatrixArrayParam(holeFillingCompShader, "viewMatrices", viewMatrices); - ctx.cmd.SetComputeMatrixArrayParam( - holeFillingCompShader, - "invProjMatrices", - invProjMatrices - ); - } + viewMatrices[i] = cameras[i].worldToCameraMatrix; + invProjMatrices[i] = invGPUProjMatrix; + } - addViewProjectionMatrix(ctx, cameras[0], "leftViewProjMatrix"); - addViewProjectionMatrix(ctx, cameras[internalCameraCount / 2], "middleViewProjMatrix"); - addViewProjectionMatrix(ctx, cameras[internalCameraCount - 1], "rightViewProjMatrix"); + if (fillHoles) + { + ctx.cmd.SetComputeMatrixArrayParam( + holeFillingCompShader, + "viewMatrices", + viewMatrices + ); + ctx.cmd.SetComputeMatrixArrayParam( + holeFillingCompShader, + "invProjMatrices", + invProjMatrices + ); + } - // always render to mosaic image handle - CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + addViewProjectionMatrix(ctx, cameras[0], "leftViewProjMatrix"); + addViewProjectionMatrix(ctx, cameras[internalCameraCount / 2], "middleViewProjMatrix"); + addViewProjectionMatrix(ctx, cameras[internalCameraCount - 1], "rightViewProjMatrix"); - // reprojection pass - CoreUtils.DrawFullScreen( - ctx.cmd, - fullscreenPassMaterial, - ctx.propertyBlock, - shaderPassId: 0 - ); - } + // always render to mosaic image handle + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); - private void runHoleFilling(CustomPassContext ctx) - { - if (fillHoles == false) - { - return; + // reprojection pass + CoreUtils.DrawFullScreen( + ctx.cmd, + fullscreenPassMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); } - // fill holes in the mosaic image - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - holeFillingKernel, - "Result", - computeShaderResultTexture - ); - for (int i = 0; i < internalCameraCount; i++) + private void runHoleFilling(CustomPassContext ctx) { + if (fillHoles == false) + { + return; + } + + // fill holes in the mosaic image ctx.cmd.SetComputeTextureParam( holeFillingCompShader, holeFillingKernel, - "_depthMap" + i, - indivDepthMaps[i] + "Result", + computeShaderResultTexture + ); + for (int i = 0; i < internalCameraCount; i++) + { + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + holeFillingKernel, + "_depthMap" + i, + indivDepthMaps[i] + ); + } + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + holeFillingKernel, + "_colorMosaic", + mosaicImageHandle + ); + ctx.cmd.SetComputeIntParams(holeFillingCompShader, "gridSize", new int[] { 4, 4 }); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); + ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); + ctx.cmd.SetComputeIntParams( + holeFillingCompShader, + "imageSize", + new int[] { mosaicImageHandle.rt.width, mosaicImageHandle.rt.height } ); - } - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - holeFillingKernel, - "_colorMosaic", - mosaicImageHandle - ); - ctx.cmd.SetComputeIntParams(holeFillingCompShader, "gridSize", new int[] { 4, 4 }); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); - ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); - ctx.cmd.SetComputeIntParams( - holeFillingCompShader, - "imageSize", - new int[] { mosaicImageHandle.rt.width, mosaicImageHandle.rt.height } - ); - - ctx.cmd.DispatchCompute( - holeFillingCompShader, - holeFillingKernel, - mosaicImageHandle.rt.width, - mosaicImageHandle.rt.height, - 1 - ); - - // Blit the result to the mosaic image - CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); - CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); - } - private void runFXAA(CustomPassContext ctx) - { - if (antialiasingMode != AntialiasingMode.FXAA) - { - return; - } + ctx.cmd.DispatchCompute( + holeFillingCompShader, + holeFillingKernel, + mosaicImageHandle.rt.width, + mosaicImageHandle.rt.height, + 1 + ); - Tonemapping tonemapping = ctx.hdCamera.volumeStack.GetComponent(); - float paperWhite = tonemapping.paperWhite.value; - Vector4 hdroutParameters = new Vector4(0, 1000, paperWhite, 1f / paperWhite); - - ctx.cmd.SetComputeTextureParam( - fxaaCompShader, - fxaaKernel, - "_colorMosaic", - mosaicImageHandle - ); - ctx.cmd.SetComputeTextureParam( - fxaaCompShader, - fxaaKernel, - "_OutputTexture", - computeShaderResultTexture - ); - ctx.cmd.SetComputeVectorParam(fxaaCompShader, "_HDROutputParams", hdroutParameters); - ctx.cmd.DispatchCompute( - fxaaCompShader, - fxaaKernel, - mosaicImageHandle.rt.width, - mosaicImageHandle.rt.height, - 1 - ); - } + // Blit the result to the mosaic image + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); + } - private void runSMAA(CustomPassContext ctx, HDAdditionalCameraData hdCamera) - { - if (antialiasingMode != AntialiasingMode.SMAA) + private void runFXAA(CustomPassContext ctx) { - return; - } + if (antialiasingMode != AntialiasingMode.FXAA) + { + return; + } + + Tonemapping tonemapping = ctx.hdCamera.volumeStack.GetComponent(); + float paperWhite = tonemapping.paperWhite.value; + Vector4 hdroutParameters = new Vector4(0, 1000, paperWhite, 1f / paperWhite); - smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); - smaaMaterial.SetVector( - Shader.PropertyToID("_SMAARTMetrics"), - new Vector4( - 1.0f / mosaicImageHandle.rt.width, - 1.0f / mosaicImageHandle.rt.height, + ctx.cmd.SetComputeTextureParam( + fxaaCompShader, + fxaaKernel, + "_colorMosaic", + mosaicImageHandle + ); + ctx.cmd.SetComputeTextureParam( + fxaaCompShader, + fxaaKernel, + "_OutputTexture", + computeShaderResultTexture + ); + ctx.cmd.SetComputeVectorParam(fxaaCompShader, "_HDROutputParams", hdroutParameters); + ctx.cmd.DispatchCompute( + fxaaCompShader, + fxaaKernel, mosaicImageHandle.rt.width, - mosaicImageHandle.rt.height - ) - ); - smaaMaterial.SetInt(Shader.PropertyToID("_StencilRef"), (int)(1 << 2)); - smaaMaterial.SetInt(Shader.PropertyToID("_StencilCmp"), (int)(1 << 2)); + mosaicImageHandle.rt.height, + 1 + ); + } - switch (hdCamera.SMAAQuality) + private void runSMAA(CustomPassContext ctx, HDAdditionalCameraData hdCamera) { - case HDAdditionalCameraData.SMAAQualityLevel.Low: - smaaMaterial.EnableKeyword("SMAA_PRESET_LOW"); - break; - case HDAdditionalCameraData.SMAAQualityLevel.Medium: - smaaMaterial.EnableKeyword("SMAA_PRESET_MEDIUM"); - break; - case HDAdditionalCameraData.SMAAQualityLevel.High: - smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); - break; - default: - smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); - break; - } + if (antialiasingMode != AntialiasingMode.SMAA) + { + return; + } - // ----------------------------------------------------------------------------- - // EdgeDetection stage - ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); - CoreUtils.SetRenderTarget(ctx.cmd, smaaEdgesTex, ClearFlag.Color); - CoreUtils.DrawFullScreen( - ctx.cmd, - smaaMaterial, - ctx.propertyBlock, - shaderPassId: smaaMaterial.FindPass("EdgeDetection") - ); - - // ----------------------------------------------------------------------------- - // BlendWeights stage - ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), smaaEdgesTex); - CoreUtils.SetRenderTarget(ctx.cmd, smaaBlendTex, ClearFlag.Color); - CoreUtils.DrawFullScreen( - ctx.cmd, - smaaMaterial, - ctx.propertyBlock, - shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation") - ); - - // ----------------------------------------------------------------------------- - // NeighborhoodBlending stage - ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); - ctx.propertyBlock.SetTexture(Shader.PropertyToID("_BlendTex"), smaaBlendTex); - CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTextureHandle, ClearFlag.None); - CoreUtils.DrawFullScreen( - ctx.cmd, - smaaMaterial, - ctx.propertyBlock, - shaderPassId: smaaMaterial.FindPass("NeighborhoodBlending") - ); - } + smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); + smaaMaterial.SetVector( + Shader.PropertyToID("_SMAARTMetrics"), + new Vector4( + 1.0f / mosaicImageHandle.rt.width, + 1.0f / mosaicImageHandle.rt.height, + mosaicImageHandle.rt.width, + mosaicImageHandle.rt.height + ) + ); + smaaMaterial.SetInt(Shader.PropertyToID("_StencilRef"), (int)(1 << 2)); + smaaMaterial.SetInt(Shader.PropertyToID("_StencilCmp"), (int)(1 << 2)); - /// - /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. - /// If so returns true, otherwise returns false. - /// - /// - /// - static bool isMainG3DCamera(Camera camera) - { - if (camera.cameraType != CameraType.Game) - return false; - bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; + switch (hdCamera.SMAAQuality) + { + case HDAdditionalCameraData.SMAAQualityLevel.Low: + smaaMaterial.EnableKeyword("SMAA_PRESET_LOW"); + break; + case HDAdditionalCameraData.SMAAQualityLevel.Medium: + smaaMaterial.EnableKeyword("SMAA_PRESET_MEDIUM"); + break; + case HDAdditionalCameraData.SMAAQualityLevel.High: + smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); + break; + default: + smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); + break; + } - bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent( - out var mosaicCamera - ); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; + // ----------------------------------------------------------------------------- + // EdgeDetection stage + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); + CoreUtils.SetRenderTarget(ctx.cmd, smaaEdgesTex, ClearFlag.Color); + CoreUtils.DrawFullScreen( + ctx.cmd, + smaaMaterial, + ctx.propertyBlock, + shaderPassId: smaaMaterial.FindPass("EdgeDetection") + ); - if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) - return false; + // ----------------------------------------------------------------------------- + // BlendWeights stage + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), smaaEdgesTex); + CoreUtils.SetRenderTarget(ctx.cmd, smaaBlendTex, ClearFlag.Color); + CoreUtils.DrawFullScreen( + ctx.cmd, + smaaMaterial, + ctx.propertyBlock, + shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation") + ); - // skip all cameras created by the G3D Camera script - if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) - { - return false; + // ----------------------------------------------------------------------------- + // NeighborhoodBlending stage + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_BlendTex"), smaaBlendTex); + CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTextureHandle, ClearFlag.None); + CoreUtils.DrawFullScreen( + ctx.cmd, + smaaMaterial, + ctx.propertyBlock, + shaderPassId: smaaMaterial.FindPass("NeighborhoodBlending") + ); } - return true; - } + /// + /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. + /// If so returns true, otherwise returns false. + /// + /// + /// + static bool isMainG3DCamera(Camera camera) + { + if (camera.cameraType != CameraType.Game) + return false; + bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); + bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; - protected override void Cleanup() - { - releaseSMAATextures(); - computeShaderResultTextureHandle?.Release(); - computeShaderResultTexture?.Release(); - } -} + bool isMosaicMultiviewCamera = + camera.gameObject.TryGetComponent(out var mosaicCamera); + bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; -internal class G3DHDRPViewGenerationMosaicPass : FullScreenCustomPass -{ - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } + if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) + return false; - protected override void Execute(CustomPassContext ctx) - { - var camera = ctx.hdCamera.camera; - if (isMainG3DCamera(camera)) + // skip all cameras created by the G3D Camera script + if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) + { + return false; + } + + return true; + } + + protected override void Cleanup() { - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_rows"), 4); - ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_columns"), 4); - CoreUtils.DrawFullScreen( - ctx.cmd, - fullscreenPassMaterial, - ctx.propertyBlock, - shaderPassId: 0 - ); + releaseSMAATextures(); + computeShaderResultTextureHandle?.Release(); + computeShaderResultTexture?.Release(); } } - /// - /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. - /// If so returns true, otherwise returns false. - /// - /// - /// - static bool isMainG3DCamera(Camera camera) + internal class HDRPViewGenerationMosaicPass : FullScreenCustomPass { - if (camera.cameraType != CameraType.Game) - return false; - bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } - bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent( - out var mosaicCamera - ); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; - - if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) - return false; + protected override void Execute(CustomPassContext ctx) + { + var camera = ctx.hdCamera.camera; + if (isMainG3DCamera(camera)) + { + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_rows"), 4); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_columns"), 4); + CoreUtils.DrawFullScreen( + ctx.cmd, + fullscreenPassMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); + } + } - // skip all cameras created by the G3D Camera script - if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) + /// + /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. + /// If so returns true, otherwise returns false. + /// + /// + /// + static bool isMainG3DCamera(Camera camera) { - return false; + if (camera.cameraType != CameraType.Game) + return false; + bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); + bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; + + bool isMosaicMultiviewCamera = + camera.gameObject.TryGetComponent(out var mosaicCamera); + bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; + + if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) + return false; + + // skip all cameras created by the G3D Camera script + if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) + { + return false; + } + + return true; } - return true; + protected override void Cleanup() { } } - - protected override void Cleanup() { } } - #endif diff --git a/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs b/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs index bc1080d..1cc9481 100644 --- a/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs +++ b/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs @@ -5,153 +5,160 @@ using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.Universal; -internal class UrpScriptableRenderPass : ScriptableRenderPass +namespace g3d { - Material m_Material; - - private class PassData + internal class UrpScriptableRenderPass : ScriptableRenderPass { - internal TextureHandle src; - internal Camera camera; - internal Material blitMaterial; - } + Material m_Material; - public UrpScriptableRenderPass(Material material) - { - m_Material = material; - renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing; - } + private class PassData + { + internal TextureHandle src; + internal Camera camera; + internal Material blitMaterial; + } - [System.Obsolete] - public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) - { - ConfigureTarget(renderingData.cameraData.renderer.cameraColorTargetHandle); - } + public UrpScriptableRenderPass(Material material) + { + m_Material = material; + renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing; + } - public void updateMaterial(Material material) - { - m_Material = material; - } + [System.Obsolete] + public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) + { + ConfigureTarget(renderingData.cameraData.renderer.cameraColorTargetHandle); + } - [System.Obsolete] - public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) - { - var camera = renderingData.cameraData.camera; + public void updateMaterial(Material material) + { + m_Material = material; + } - if (performBlit(camera, m_Material)) + [System.Obsolete] + public override void Execute( + ScriptableRenderContext context, + ref RenderingData renderingData + ) { - CommandBuffer cmd = CommandBufferPool.Get(); - Blitter.BlitCameraTexture( - cmd, - renderingData.cameraData.renderer.cameraColorTargetHandle, - renderingData.cameraData.renderer.cameraColorTargetHandle, - m_Material, - 0 - ); - context.ExecuteCommandBuffer(cmd); - cmd.Clear(); - - CommandBufferPool.Release(cmd); + var camera = renderingData.cameraData.camera; + + if (performBlit(camera, m_Material)) + { + CommandBuffer cmd = CommandBufferPool.Get(); + Blitter.BlitCameraTexture( + cmd, + renderingData.cameraData.renderer.cameraColorTargetHandle, + renderingData.cameraData.renderer.cameraColorTargetHandle, + m_Material, + 0 + ); + context.ExecuteCommandBuffer(cmd); + cmd.Clear(); + + CommandBufferPool.Release(cmd); + } } - } - private void InitPassData( - RenderGraph renderGraph, - ContextContainer frameData, - ref PassData passData - ) - { - // Fill up the passData with the data needed by the passes + private void InitPassData( + RenderGraph renderGraph, + ContextContainer frameData, + ref PassData passData + ) + { + // Fill up the passData with the data needed by the passes - // UniversalResourceData contains all the texture handles used by the renderer, including the active color and depth textures - // The active color and depth textures are the main color and depth buffers that the camera renders into - UniversalResourceData resourceData = frameData.Get(); + // UniversalResourceData contains all the texture handles used by the renderer, including the active color and depth textures + // The active color and depth textures are the main color and depth buffers that the camera renders into + UniversalResourceData resourceData = frameData.Get(); - UniversalCameraData cameraData = frameData.Get(); + UniversalCameraData cameraData = frameData.Get(); - passData.src = resourceData.activeColorTexture; - passData.camera = cameraData.camera; - passData.blitMaterial = m_Material; - } + passData.src = resourceData.activeColorTexture; + passData.camera = cameraData.camera; + passData.blitMaterial = m_Material; + } - /// - /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. - /// If so returns true, otherwise returns false. - /// - /// - /// - static bool performBlit(Camera camera, Material blitMaterial) - { - if (camera.cameraType != CameraType.Game) - return false; - bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; + /// + /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. + /// If so returns true, otherwise returns false. + /// + /// + /// + static bool performBlit(Camera camera, Material blitMaterial) + { + if (camera.cameraType != CameraType.Game) + return false; + bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); + bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; - bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent( - out var mosaicCamera - ); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; + bool isMosaicMultiviewCamera = + camera.gameObject.TryGetComponent(out var mosaicCamera); + bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; - if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) - return false; + if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) + return false; - if (blitMaterial == null) - return false; + if (blitMaterial == null) + return false; - // skip all cameras created by the G3D Camera script - if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) - { - return false; - } + // skip all cameras created by the G3D Camera script + if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) + { + return false; + } - return true; - } + return true; + } - static void ExecutePass(PassData data, RasterGraphContext context) - { - var camera = data.camera; - if (performBlit(camera, data.blitMaterial)) + static void ExecutePass(PassData data, RasterGraphContext context) { - // CommandBuffer cmd = CommandBufferPool.Get(); - // Blitter.BlitCameraTexture(cmd, data.src, data.src, data.blitMaterial, 0); - // Blitter. - // context.ExecuteCommandBuffer(cmd); - // cmd.Clear(); - // CommandBufferPool.Release(cmd); - - Blitter.BlitTexture( - context.cmd, - data.src, - new Vector4(1, 1, 0, 0), - data.blitMaterial, - 0 - ); + var camera = data.camera; + if (performBlit(camera, data.blitMaterial)) + { + // CommandBuffer cmd = CommandBufferPool.Get(); + // Blitter.BlitCameraTexture(cmd, data.src, data.src, data.blitMaterial, 0); + // Blitter. + // context.ExecuteCommandBuffer(cmd); + // cmd.Clear(); + // CommandBufferPool.Release(cmd); + + Blitter.BlitTexture( + context.cmd, + data.src, + new Vector4(1, 1, 0, 0), + data.blitMaterial, + 0 + ); + } } - } - - // This is where the renderGraph handle can be accessed. - // Each ScriptableRenderPass can use the RenderGraph handle to add multiple render passes to the render graph - public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) - { - string passName = "Blit With Material"; - - // This simple pass copies the active color texture to a new texture using a custom material. This sample is for API demonstrative purposes, - // so the new texture is not used anywhere else in the frame, you can use the frame debugger to verify its contents. - // add a raster render pass to the render graph, specifying the name and the data type that will be passed to the ExecutePass function - using (var builder = renderGraph.AddRasterRenderPass(passName, out var passData)) + // This is where the renderGraph handle can be accessed. + // Each ScriptableRenderPass can use the RenderGraph handle to add multiple render passes to the render graph + public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { - // Initialize the pass data - InitPassData(renderGraph, frameData, ref passData); - - // // We disable culling for this pass for the demonstrative purpose of this sampe, as normally this pass would be culled, - // // since the destination texture is not used anywhere else - builder.AllowPassCulling(false); - - // Assign the ExecutePass function to the render pass delegate, which will be called by the render graph when executing the pass - builder.SetRenderFunc( - (PassData data, RasterGraphContext context) => ExecutePass(data, context) - ); + string passName = "Blit With Material"; + + // This simple pass copies the active color texture to a new texture using a custom material. This sample is for API demonstrative purposes, + // so the new texture is not used anywhere else in the frame, you can use the frame debugger to verify its contents. + + // add a raster render pass to the render graph, specifying the name and the data type that will be passed to the ExecutePass function + using ( + var builder = renderGraph.AddRasterRenderPass(passName, out var passData) + ) + { + // Initialize the pass data + InitPassData(renderGraph, frameData, ref passData); + + // // We disable culling for this pass for the demonstrative purpose of this sampe, as normally this pass would be culled, + // // since the destination texture is not used anywhere else + builder.AllowPassCulling(false); + + // Assign the ExecutePass function to the render pass delegate, which will be called by the render graph when executing the pass + builder.SetRenderFunc( + (PassData data, RasterGraphContext context) => ExecutePass(data, context) + ); + } } } } From 8783f922ef3eaf258e362e86a7144f6f7021f052 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 17 Mar 2026 14:14:40 +0100 Subject: [PATCH 099/149] moved mosaic render pass to own file --- .../HDRP/HDRPViewGenerationMosaicPass.cs | 71 +++++++++++++++++++ .../HDRP/HDRPViewGenerationPass.cs | 53 -------------- 2 files changed, 71 insertions(+), 53 deletions(-) create mode 100644 Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs new file mode 100644 index 0000000..7fad05f --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs @@ -0,0 +1,71 @@ +#if G3D_HDRP +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.HighDefinition; + +namespace g3d +{ + internal enum AntialiasingMode + { + None, + FXAA, + SMAA, + TAA + } + + internal class HDRPViewGenerationMosaicPass : FullScreenCustomPass + { + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } + + protected override void Execute(CustomPassContext ctx) + { + var camera = ctx.hdCamera.camera; + if (isMainG3DCamera(camera)) + { + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_rows"), 4); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_columns"), 4); + CoreUtils.DrawFullScreen( + ctx.cmd, + fullscreenPassMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); + } + } + + /// + /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. + /// If so returns true, otherwise returns false. + /// + /// + /// + static bool isMainG3DCamera(Camera camera) + { + if (camera.cameraType != CameraType.Game) + return false; + bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); + bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; + + bool isMosaicMultiviewCamera = + camera.gameObject.TryGetComponent(out var mosaicCamera); + bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; + + if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) + return false; + + // skip all cameras created by the G3D Camera script + if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) + { + return false; + } + + return true; + } + + protected override void Cleanup() { } + } +} + +#endif diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs index 7b4c8ee..72250b8 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs @@ -510,59 +510,6 @@ protected override void Cleanup() computeShaderResultTexture?.Release(); } } - - internal class HDRPViewGenerationMosaicPass : FullScreenCustomPass - { - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } - - protected override void Execute(CustomPassContext ctx) - { - var camera = ctx.hdCamera.camera; - if (isMainG3DCamera(camera)) - { - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_rows"), 4); - ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_columns"), 4); - CoreUtils.DrawFullScreen( - ctx.cmd, - fullscreenPassMaterial, - ctx.propertyBlock, - shaderPassId: 0 - ); - } - } - - /// - /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. - /// If so returns true, otherwise returns false. - /// - /// - /// - static bool isMainG3DCamera(Camera camera) - { - if (camera.cameraType != CameraType.Game) - return false; - bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; - - bool isMosaicMultiviewCamera = - camera.gameObject.TryGetComponent(out var mosaicCamera); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; - - if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) - return false; - - // skip all cameras created by the G3D Camera script - if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) - { - return false; - } - - return true; - } - - protected override void Cleanup() { } - } } #endif From 10bd329e7971cbd4fa4ad80ab729cc1969c67d16 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 17 Mar 2026 14:27:00 +0100 Subject: [PATCH 100/149] renamed namespace --- G3DCamera.cs | 53 ++++++++++--------- G3DCameraMosaicMultiview.cs | 13 ++--- Scripts/InspectorG3DMosaicCamera.cs | 2 +- .../RenderPipelines/HDRP/HDRPCustomPass.cs | 2 +- .../HDRP/HDRPDepthMapPrePass.cs | 2 +- .../HDRP/HDRPViewGenerationMosaicPass.cs | 10 +--- .../HDRP/HDRPViewGenerationMosaicPass.cs.meta | 2 + .../HDRP/HDRPViewGenerationPass.cs | 2 +- .../URP/UrpScriptableRenderPass.cs | 2 +- 9 files changed, 43 insertions(+), 45 deletions(-) create mode 100644 Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index 0ef6128..e1f5da0 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -175,7 +175,7 @@ public class G3DCamera .None; #endif #if G3D_URP - private g3d.UrpScriptableRenderPass customPass; + private G3D.RenderPass.UrpScriptableRenderPass customPass; private AntialiasingMode antialiasingMode = AntialiasingMode.None; #endif @@ -231,8 +231,8 @@ private float scaledHalfCameraWidthAtStart private Material viewGenerationMaterial; #if G3D_HDRP - private g3d.HDRPViewGenerationPass viewGenerationPass; - private g3d.HDRPDepthMapPrePass depthMosaicPass; + private G3D.RenderPass.HDRPViewGenerationPass viewGenerationPass; + private G3D.RenderPass.HDRPDepthMapPrePass depthMosaicPass; /// /// Used for view generation mosaic rendering. @@ -279,7 +279,7 @@ void Start() #endif #if G3D_URP - customPass = new g3d.UrpScriptableRenderPass(material); + customPass = new G3D.RenderPass.UrpScriptableRenderPass(material); antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing; mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None; #endif @@ -409,16 +409,16 @@ private void initCustomPass() { // add depth mosaic generation pass depthMosaicPass = - customPassVolume.AddPassOfType(typeof(g3d.HDRPDepthMapPrePass)) - as g3d.HDRPDepthMapPrePass; + customPassVolume.AddPassOfType(typeof(G3D.RenderPass.HDRPDepthMapPrePass)) + as G3D.RenderPass.HDRPDepthMapPrePass; depthMosaicPass.cullingMask = mainCamCullingMask; depthMosaicPass.cameras = cameras; depthMosaicPass.internalCameraCount = internalCameraCount; // add multiview generation pass viewGenerationPass = - customPassVolume.AddPassOfType(typeof(g3d.HDRPViewGenerationPass)) - as g3d.HDRPViewGenerationPass; + customPassVolume.AddPassOfType(typeof(G3D.RenderPass.HDRPViewGenerationPass)) + as G3D.RenderPass.HDRPViewGenerationPass; viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; @@ -431,7 +431,7 @@ private void initCustomPass() viewGenerationPass.debugRendering = debugRendering; viewGenerationPass.fillHoles = isFillingHoles; viewGenerationPass.holeFillingRadius = holeFillingRadius; - g3d.AntialiasingMode aaMode = getCameraAAMode(); + G3D.RenderPass.AntialiasingMode aaMode = getCameraAAMode(); viewGenerationPass.init( new Vector2Int(mainCamera.pixelWidth, mainCamera.pixelHeight), aaMode @@ -442,17 +442,19 @@ private void initCustomPass() if (debugRendering == false) { - g3d.HDRPViewGenerationMosaicPass finalAutostereoGeneration = - customPassVolume.AddPassOfType(typeof(g3d.HDRPViewGenerationMosaicPass)) - as g3d.HDRPViewGenerationMosaicPass; + G3D.RenderPass.HDRPViewGenerationMosaicPass finalAutostereoGeneration = + customPassVolume.AddPassOfType( + typeof(G3D.RenderPass.HDRPViewGenerationMosaicPass) + ) as G3D.RenderPass.HDRPViewGenerationMosaicPass; finalAutostereoGeneration.fullscreenPassMaterial = material; finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; } } else { - g3d.HDRPCustomPass customPass = - customPassVolume.AddPassOfType(typeof(g3d.HDRPCustomPass)) as g3d.HDRPCustomPass; + G3D.RenderPass.HDRPCustomPass customPass = + customPassVolume.AddPassOfType(typeof(G3D.RenderPass.HDRPCustomPass)) + as G3D.RenderPass.HDRPCustomPass; customPass.fullscreenPassMaterial = material; customPass.materialPassName = "G3DFullScreen3D"; } @@ -490,9 +492,9 @@ private void recreateMosaicTexture() if (rtHandleMosaic != null) { - g3d.HDRPCustomPass.GetRTHandleSystem().Release(rtHandleMosaic); + G3D.RenderPass.HDRPCustomPass.GetRTHandleSystem().Release(rtHandleMosaic); } - rtHandleMosaic = g3d.HDRPCustomPass.GetRTHandleSystem().Alloc(mosaicTexture); + rtHandleMosaic = G3D.RenderPass.HDRPCustomPass.GetRTHandleSystem().Alloc(mosaicTexture); material.SetTexture("_colorMosaic", rtHandleMosaic); viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } @@ -876,25 +878,25 @@ private Vector2Int getDisplayResolutionFromCalibrationFile() } #if G3D_HDRP - private g3d.AntialiasingMode getCameraAAMode() + private G3D.RenderPass.AntialiasingMode getCameraAAMode() { - g3d.AntialiasingMode aaMode = g3d.AntialiasingMode.None; + G3D.RenderPass.AntialiasingMode aaMode = G3D.RenderPass.AntialiasingMode.None; if ( antialiasingMode == HDAdditionalCameraData.AntialiasingMode.SubpixelMorphologicalAntiAliasing ) { - aaMode = g3d.AntialiasingMode.SMAA; + aaMode = G3D.RenderPass.AntialiasingMode.SMAA; } else if ( antialiasingMode == HDAdditionalCameraData.AntialiasingMode.FastApproximateAntialiasing ) { - aaMode = g3d.AntialiasingMode.FXAA; + aaMode = G3D.RenderPass.AntialiasingMode.FXAA; } else if (antialiasingMode == HDAdditionalCameraData.AntialiasingMode.TemporalAntialiasing) { - aaMode = g3d.AntialiasingMode.TAA; + aaMode = G3D.RenderPass.AntialiasingMode.TAA; } return aaMode; } @@ -1078,7 +1080,8 @@ void Update() { recreatedRenderTextures = true; #if G3D_HDRP - g3d.HDRPCustomPass.GetRTHandleSystem().ResetReferenceSize(Screen.width, Screen.height); + G3D.RenderPass.HDRPCustomPass.GetRTHandleSystem() + .ResetReferenceSize(Screen.width, Screen.height); // TODO this is only needed to reset the size of the cameras internal render targets // Find a way to avoid this line... RTHandles.ResetReferenceSize(Screen.width, Screen.height); @@ -1102,7 +1105,7 @@ void Update() viewGenerationPass.holeFillingRadius = holeFillingRadius; viewGenerationPass.fillHoles = isFillingHoles; - g3d.AntialiasingMode aaMode = getCameraAAMode(); + G3D.RenderPass.AntialiasingMode aaMode = getCameraAAMode(); viewGenerationPass.setAntiAliasingMode(aaMode); } #endif @@ -1556,8 +1559,8 @@ Matrix4x4 mainCamProjectionMatrix // This function only does something when you use the SRP render pipeline. // when using either URP or HRDP image combination is handled in the respective renderpasses. - // URP -> g3d.UrpScriptableRenderPass.cs - // HDRP -> g3d.HDRPCustomPass.cs + // URP -> G3D.RenderPass.UrpScriptableRenderPass.cs + // HDRP -> G3D.RenderPass.HDRPCustomPass.cs void OnRenderImage(RenderTexture source, RenderTexture destination) { // This is where the material and shader are applied to the camera image. diff --git a/G3DCameraMosaicMultiview.cs b/G3DCameraMosaicMultiview.cs index 09d17a6..fad2742 100644 --- a/G3DCameraMosaicMultiview.cs +++ b/G3DCameraMosaicMultiview.cs @@ -92,10 +92,10 @@ public class G3DCameraMosaicMultiview : MonoBehaviour private Camera mainCamera; private Material material; #if G3D_HDRP - private g3d.HDRPCustomPass customPass; + private G3D.RenderPass.HDRPCustomPass customPass; #endif #if G3D_URP - private g3d.UrpScriptableRenderPass customPass; + private G3D.RenderPass.UrpScriptableRenderPass customPass; #endif private ShaderHandles shaderHandles; @@ -147,13 +147,14 @@ void Start() // Make the volume invisible in the inspector customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; customPass = - customPassVolume.AddPassOfType(typeof(g3d.HDRPCustomPass)) as g3d.HDRPCustomPass; + customPassVolume.AddPassOfType(typeof(G3D.RenderPass.HDRPCustomPass)) + as G3D.RenderPass.HDRPCustomPass; customPass.fullscreenPassMaterial = material; customPass.materialPassName = "G3DFullScreen3D"; #endif #if G3D_URP - customPass = new g3d.UrpScriptableRenderPass(material); + customPass = new G3D.RenderPass.UrpScriptableRenderPass(material); #endif // Do this last to ensure custom passes are already set up @@ -491,8 +492,8 @@ private bool windowMoved() // This function only does something when you use the SRP render pipeline. // when using either URP or HRDP image combination is handled in the respective renderpasses. - // URP -> g3d.UrpScriptableRenderPass.cs - // HDRP -> g3d.HDRPCustomPass.cs + // URP -> G3D.RenderPass.UrpScriptableRenderPass.cs + // HDRP -> G3D.RenderPass.HDRPCustomPass.cs void OnRenderImage(RenderTexture source, RenderTexture destination) { // This is where the material and shader are applied to the camera image. diff --git a/Scripts/InspectorG3DMosaicCamera.cs b/Scripts/InspectorG3DMosaicCamera.cs index ca78449..1e241af 100644 --- a/Scripts/InspectorG3DMosaicCamera.cs +++ b/Scripts/InspectorG3DMosaicCamera.cs @@ -5,7 +5,7 @@ using UnityEngine.UIElements; [CustomEditor(typeof(G3DCameraMosaicMultiview))] -public class InspectorG3DMosaicCamera1 : Editor +public class InspectorG3DMosaicCamera : Editor { public VisualTreeAsset inspectorXML; diff --git a/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs b/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs index 1c99c8c..3204bdb 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs @@ -3,7 +3,7 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -namespace g3d +namespace G3D.RenderPass { internal class HDRPCustomPass : FullScreenCustomPass { diff --git a/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs b/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs index 73c2507..83c7f66 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs @@ -4,7 +4,7 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -namespace g3d +namespace G3D.RenderPass { internal class HDRPDepthMapPrePass : FullScreenCustomPass { diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs index 7fad05f..fe9d8cb 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs @@ -4,16 +4,8 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -namespace g3d +namespace G3D.RenderPass { - internal enum AntialiasingMode - { - None, - FXAA, - SMAA, - TAA - } - internal class HDRPViewGenerationMosaicPass : FullScreenCustomPass { protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs.meta b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs.meta new file mode 100644 index 0000000..755c969 --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e0f1de26b474ed24583a4c4541f5980a \ No newline at end of file diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs index 72250b8..f53da5e 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs +++ b/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs @@ -4,7 +4,7 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -namespace g3d +namespace G3D.RenderPass { internal enum AntialiasingMode { diff --git a/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs b/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs index 1cc9481..5b17b5e 100644 --- a/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs +++ b/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs @@ -5,7 +5,7 @@ using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.Universal; -namespace g3d +namespace G3D.RenderPass { internal class UrpScriptableRenderPass : ScriptableRenderPass { From bb46d50e4d28ad825d7877c992a41c52a2851242 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Tue, 17 Mar 2026 15:13:43 +0100 Subject: [PATCH 101/149] renamed files/ pass names; minor tweaks performBlit and is mainCamera are now oen function and were moved to the same file --- G3DCamera.cs | 54 +++++++-------- G3DCameraMosaicMultiview.cs | 14 ++-- .../HDRP/{HDRPCustomPass.cs => CustomPass.cs} | 4 +- ...PCustomPass.cs.meta => CustomPass.cs.meta} | 0 ...PDepthMapPrePass.cs => DepthMapPrePass.cs} | 39 ++--------- ...rePass.cs.meta => DepthMapPrePass.cs.meta} | 0 .../HDRP/ViewGenerationMosaicPass.cs | 34 ++++++++++ ....meta => ViewGenerationMosaicPass.cs.meta} | 0 ...enerationPass.cs => ViewGenerationPass.cs} | 65 ++++++------------- ...ass.cs.meta => ViewGenerationPass.cs.meta} | 0 ...rationMosaicPass.cs => HelperFunctions.cs} | 39 ++++------- .../RenderPipelines/HelperFunctions.cs.meta | 2 + ...criptableRenderPass.cs => ScriptableRP.cs} | 55 ++-------------- .../RenderPipelines/URP/ScriptableRP.cs.meta | 2 + .../URP/UrpScriptableRenderPass.cs.meta | 11 ---- 15 files changed, 119 insertions(+), 200 deletions(-) rename Scripts/RenderPipelines/HDRP/{HDRPCustomPass.cs => CustomPass.cs} (96%) rename Scripts/RenderPipelines/HDRP/{HDRPCustomPass.cs.meta => CustomPass.cs.meta} (100%) rename Scripts/RenderPipelines/HDRP/{HDRPDepthMapPrePass.cs => DepthMapPrePass.cs} (71%) rename Scripts/RenderPipelines/HDRP/{HDRPDepthMapPrePass.cs.meta => DepthMapPrePass.cs.meta} (100%) create mode 100644 Scripts/RenderPipelines/HDRP/ViewGenerationMosaicPass.cs rename Scripts/RenderPipelines/HDRP/{HDRPViewGenerationMosaicPass.cs.meta => ViewGenerationMosaicPass.cs.meta} (100%) rename Scripts/RenderPipelines/HDRP/{HDRPViewGenerationPass.cs => ViewGenerationPass.cs} (90%) rename Scripts/RenderPipelines/HDRP/{HDRPViewGenerationPass.cs.meta => ViewGenerationPass.cs.meta} (100%) rename Scripts/RenderPipelines/{HDRP/HDRPViewGenerationMosaicPass.cs => HelperFunctions.cs} (54%) create mode 100644 Scripts/RenderPipelines/HelperFunctions.cs.meta rename Scripts/RenderPipelines/URP/{UrpScriptableRenderPass.cs => ScriptableRP.cs} (63%) create mode 100644 Scripts/RenderPipelines/URP/ScriptableRP.cs.meta delete mode 100644 Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index e1f5da0..2d6d925 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -175,7 +175,7 @@ public class G3DCamera .None; #endif #if G3D_URP - private G3D.RenderPass.UrpScriptableRenderPass customPass; + private G3D.RenderPipeline.URP.ScriptableRP customPass; private AntialiasingMode antialiasingMode = AntialiasingMode.None; #endif @@ -231,8 +231,8 @@ private float scaledHalfCameraWidthAtStart private Material viewGenerationMaterial; #if G3D_HDRP - private G3D.RenderPass.HDRPViewGenerationPass viewGenerationPass; - private G3D.RenderPass.HDRPDepthMapPrePass depthMosaicPass; + private G3D.RenderPipeline.HDRP.ViewGeneration viewGenerationPass; + private G3D.RenderPipeline.HDRP.DepthMaps depthMosaicPass; /// /// Used for view generation mosaic rendering. @@ -279,7 +279,7 @@ void Start() #endif #if G3D_URP - customPass = new G3D.RenderPass.UrpScriptableRenderPass(material); + customPass = new G3D.RenderPipeline.URP.ScriptableRP(material); antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing; mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None; #endif @@ -409,16 +409,16 @@ private void initCustomPass() { // add depth mosaic generation pass depthMosaicPass = - customPassVolume.AddPassOfType(typeof(G3D.RenderPass.HDRPDepthMapPrePass)) - as G3D.RenderPass.HDRPDepthMapPrePass; + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.DepthMaps)) + as G3D.RenderPipeline.HDRP.DepthMaps; depthMosaicPass.cullingMask = mainCamCullingMask; depthMosaicPass.cameras = cameras; depthMosaicPass.internalCameraCount = internalCameraCount; // add multiview generation pass viewGenerationPass = - customPassVolume.AddPassOfType(typeof(G3D.RenderPass.HDRPViewGenerationPass)) - as G3D.RenderPass.HDRPViewGenerationPass; + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.ViewGeneration)) + as G3D.RenderPipeline.HDRP.ViewGeneration; viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; @@ -431,7 +431,7 @@ private void initCustomPass() viewGenerationPass.debugRendering = debugRendering; viewGenerationPass.fillHoles = isFillingHoles; viewGenerationPass.holeFillingRadius = holeFillingRadius; - G3D.RenderPass.AntialiasingMode aaMode = getCameraAAMode(); + G3D.RenderPipeline.AntialiasingMode aaMode = getCameraAAMode(); viewGenerationPass.init( new Vector2Int(mainCamera.pixelWidth, mainCamera.pixelHeight), aaMode @@ -442,19 +442,19 @@ private void initCustomPass() if (debugRendering == false) { - G3D.RenderPass.HDRPViewGenerationMosaicPass finalAutostereoGeneration = + G3D.RenderPipeline.HDRP.ViewGenerationMosaic finalAutostereoGeneration = customPassVolume.AddPassOfType( - typeof(G3D.RenderPass.HDRPViewGenerationMosaicPass) - ) as G3D.RenderPass.HDRPViewGenerationMosaicPass; + typeof(G3D.RenderPipeline.HDRP.ViewGenerationMosaic) + ) as G3D.RenderPipeline.HDRP.ViewGenerationMosaic; finalAutostereoGeneration.fullscreenPassMaterial = material; finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; } } else { - G3D.RenderPass.HDRPCustomPass customPass = - customPassVolume.AddPassOfType(typeof(G3D.RenderPass.HDRPCustomPass)) - as G3D.RenderPass.HDRPCustomPass; + G3D.RenderPipeline.HDRP.CustomPass customPass = + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.CustomPass)) + as G3D.RenderPipeline.HDRP.CustomPass; customPass.fullscreenPassMaterial = material; customPass.materialPassName = "G3DFullScreen3D"; } @@ -492,9 +492,11 @@ private void recreateMosaicTexture() if (rtHandleMosaic != null) { - G3D.RenderPass.HDRPCustomPass.GetRTHandleSystem().Release(rtHandleMosaic); + G3D.RenderPipeline.HDRP.CustomPass.GetRTHandleSystem().Release(rtHandleMosaic); } - rtHandleMosaic = G3D.RenderPass.HDRPCustomPass.GetRTHandleSystem().Alloc(mosaicTexture); + rtHandleMosaic = G3D + .RenderPipeline.HDRP.CustomPass.GetRTHandleSystem() + .Alloc(mosaicTexture); material.SetTexture("_colorMosaic", rtHandleMosaic); viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } @@ -878,25 +880,25 @@ private Vector2Int getDisplayResolutionFromCalibrationFile() } #if G3D_HDRP - private G3D.RenderPass.AntialiasingMode getCameraAAMode() + private G3D.RenderPipeline.AntialiasingMode getCameraAAMode() { - G3D.RenderPass.AntialiasingMode aaMode = G3D.RenderPass.AntialiasingMode.None; + G3D.RenderPipeline.AntialiasingMode aaMode = G3D.RenderPipeline.AntialiasingMode.None; if ( antialiasingMode == HDAdditionalCameraData.AntialiasingMode.SubpixelMorphologicalAntiAliasing ) { - aaMode = G3D.RenderPass.AntialiasingMode.SMAA; + aaMode = G3D.RenderPipeline.AntialiasingMode.SMAA; } else if ( antialiasingMode == HDAdditionalCameraData.AntialiasingMode.FastApproximateAntialiasing ) { - aaMode = G3D.RenderPass.AntialiasingMode.FXAA; + aaMode = G3D.RenderPipeline.AntialiasingMode.FXAA; } else if (antialiasingMode == HDAdditionalCameraData.AntialiasingMode.TemporalAntialiasing) { - aaMode = G3D.RenderPass.AntialiasingMode.TAA; + aaMode = G3D.RenderPipeline.AntialiasingMode.TAA; } return aaMode; } @@ -1080,7 +1082,7 @@ void Update() { recreatedRenderTextures = true; #if G3D_HDRP - G3D.RenderPass.HDRPCustomPass.GetRTHandleSystem() + G3D.RenderPipeline.HDRP.CustomPass.GetRTHandleSystem() .ResetReferenceSize(Screen.width, Screen.height); // TODO this is only needed to reset the size of the cameras internal render targets // Find a way to avoid this line... @@ -1105,7 +1107,7 @@ void Update() viewGenerationPass.holeFillingRadius = holeFillingRadius; viewGenerationPass.fillHoles = isFillingHoles; - G3D.RenderPass.AntialiasingMode aaMode = getCameraAAMode(); + G3D.RenderPipeline.AntialiasingMode aaMode = getCameraAAMode(); viewGenerationPass.setAntiAliasingMode(aaMode); } #endif @@ -1559,8 +1561,8 @@ Matrix4x4 mainCamProjectionMatrix // This function only does something when you use the SRP render pipeline. // when using either URP or HRDP image combination is handled in the respective renderpasses. - // URP -> G3D.RenderPass.UrpScriptableRenderPass.cs - // HDRP -> G3D.RenderPass.HDRPCustomPass.cs + // URP -> G3D.RenderPipeline.URP.ScriptableRP.cs + // HDRP -> G3D.RenderPipeline.HDRP.CustomPass.cs void OnRenderImage(RenderTexture source, RenderTexture destination) { // This is where the material and shader are applied to the camera image. diff --git a/G3DCameraMosaicMultiview.cs b/G3DCameraMosaicMultiview.cs index fad2742..f1044dc 100644 --- a/G3DCameraMosaicMultiview.cs +++ b/G3DCameraMosaicMultiview.cs @@ -92,10 +92,10 @@ public class G3DCameraMosaicMultiview : MonoBehaviour private Camera mainCamera; private Material material; #if G3D_HDRP - private G3D.RenderPass.HDRPCustomPass customPass; + private G3D.RenderPipeline.HDRP.CustomPass customPass; #endif #if G3D_URP - private G3D.RenderPass.UrpScriptableRenderPass customPass; + private G3D.RenderPipeline.URP.ScriptableRP customPass; #endif private ShaderHandles shaderHandles; @@ -147,14 +147,14 @@ void Start() // Make the volume invisible in the inspector customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; customPass = - customPassVolume.AddPassOfType(typeof(G3D.RenderPass.HDRPCustomPass)) - as G3D.RenderPass.HDRPCustomPass; + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.CustomPass)) + as G3D.RenderPipeline.HDRP.CustomPass; customPass.fullscreenPassMaterial = material; customPass.materialPassName = "G3DFullScreen3D"; #endif #if G3D_URP - customPass = new G3D.RenderPass.UrpScriptableRenderPass(material); + customPass = new G3D.RenderPipeline.URP.ScriptableRP(material); #endif // Do this last to ensure custom passes are already set up @@ -492,8 +492,8 @@ private bool windowMoved() // This function only does something when you use the SRP render pipeline. // when using either URP or HRDP image combination is handled in the respective renderpasses. - // URP -> G3D.RenderPass.UrpScriptableRenderPass.cs - // HDRP -> G3D.RenderPass.HDRPCustomPass.cs + // URP -> G3D.RenderPipeline.URP.ScriptableRP.cs + // HDRP -> G3D.RenderPipeline.HDRP.CustomPass.cs void OnRenderImage(RenderTexture source, RenderTexture destination) { // This is where the material and shader are applied to the camera image. diff --git a/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs b/Scripts/RenderPipelines/HDRP/CustomPass.cs similarity index 96% rename from Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs rename to Scripts/RenderPipelines/HDRP/CustomPass.cs index 3204bdb..f44b19d 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs +++ b/Scripts/RenderPipelines/HDRP/CustomPass.cs @@ -3,9 +3,9 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -namespace G3D.RenderPass +namespace G3D.RenderPipeline.HDRP { - internal class HDRPCustomPass : FullScreenCustomPass + internal class CustomPass : FullScreenCustomPass { private static RTHandleSystem m_RTHandleSystem; diff --git a/Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs.meta b/Scripts/RenderPipelines/HDRP/CustomPass.cs.meta similarity index 100% rename from Scripts/RenderPipelines/HDRP/HDRPCustomPass.cs.meta rename to Scripts/RenderPipelines/HDRP/CustomPass.cs.meta diff --git a/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs b/Scripts/RenderPipelines/HDRP/DepthMapPrePass.cs similarity index 71% rename from Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs rename to Scripts/RenderPipelines/HDRP/DepthMapPrePass.cs index 83c7f66..db038da 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs +++ b/Scripts/RenderPipelines/HDRP/DepthMapPrePass.cs @@ -4,9 +4,9 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -namespace G3D.RenderPass +namespace G3D.RenderPipeline.HDRP { - internal class HDRPDepthMapPrePass : FullScreenCustomPass + internal class DepthMaps : FullScreenCustomPass { public List cameras; public int internalCameraCount = 16; @@ -25,7 +25,7 @@ protected override void Cleanup() { } protected override void Execute(CustomPassContext ctx) { var camera = ctx.hdCamera.camera; - if (isMainG3DCamera(camera) == false) + if (Helpers.isMainG3DCamera(camera) == false) { return; } @@ -83,7 +83,7 @@ private void cleanupDepthTextures() { continue; } - HDRPCustomPass.GetRTHandleSystem().Release(indivDepthTextures[i]); + CustomPass.GetRTHandleSystem().Release(indivDepthTextures[i]); } } @@ -107,39 +107,10 @@ public void recreateDepthTextures(float renderResolutionScale = 1.0f) RenderTextureFormat.Depth ); depthTexture.Create(); - RTHandle handle = HDRPCustomPass.GetRTHandleSystem().Alloc(depthTexture); + RTHandle handle = CustomPass.GetRTHandleSystem().Alloc(depthTexture); indivDepthTextures[i] = handle; } } - - /// - /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. - /// If so returns true, otherwise returns false. - /// - /// - /// - static bool isMainG3DCamera(Camera camera) - { - if (camera.cameraType != CameraType.Game) - return false; - bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; - - bool isMosaicMultiviewCamera = - camera.gameObject.TryGetComponent(out var mosaicCamera); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; - - if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) - return false; - - // skip all cameras created by the G3D Camera script - if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) - { - return false; - } - - return true; - } } } diff --git a/Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs.meta b/Scripts/RenderPipelines/HDRP/DepthMapPrePass.cs.meta similarity index 100% rename from Scripts/RenderPipelines/HDRP/HDRPDepthMapPrePass.cs.meta rename to Scripts/RenderPipelines/HDRP/DepthMapPrePass.cs.meta diff --git a/Scripts/RenderPipelines/HDRP/ViewGenerationMosaicPass.cs b/Scripts/RenderPipelines/HDRP/ViewGenerationMosaicPass.cs new file mode 100644 index 0000000..59bd55c --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/ViewGenerationMosaicPass.cs @@ -0,0 +1,34 @@ +#if G3D_HDRP +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.HighDefinition; + +namespace G3D.RenderPipeline.HDRP +{ + internal class ViewGenerationMosaic : FullScreenCustomPass + { + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } + + protected override void Execute(CustomPassContext ctx) + { + var camera = ctx.hdCamera.camera; + if (Helpers.isMainG3DCamera(camera)) + { + CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_rows"), 4); + ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_columns"), 4); + CoreUtils.DrawFullScreen( + ctx.cmd, + fullscreenPassMaterial, + ctx.propertyBlock, + shaderPassId: 0 + ); + } + } + + protected override void Cleanup() { } + } +} + +#endif diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs.meta b/Scripts/RenderPipelines/HDRP/ViewGenerationMosaicPass.cs.meta similarity index 100% rename from Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs.meta rename to Scripts/RenderPipelines/HDRP/ViewGenerationMosaicPass.cs.meta diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/ViewGenerationPass.cs similarity index 90% rename from Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs rename to Scripts/RenderPipelines/HDRP/ViewGenerationPass.cs index f53da5e..9f14e6e 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs +++ b/Scripts/RenderPipelines/HDRP/ViewGenerationPass.cs @@ -4,17 +4,9 @@ using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -namespace G3D.RenderPass +namespace G3D.RenderPipeline.HDRP { - internal enum AntialiasingMode - { - None, - FXAA, - SMAA, - TAA - } - - internal class HDRPViewGenerationPass : FullScreenCustomPass + internal class ViewGeneration : FullScreenCustomPass { public RTHandle leftColorMapHandle; public RTHandle rightColorMapHandle; @@ -113,7 +105,7 @@ private void CreateComputeShaderResultTexture() enableRandomWrite = true }; computeShaderResultTexture.Create(); - computeShaderResultTextureHandle = HDRPCustomPass + computeShaderResultTextureHandle = CustomPass .GetRTHandleSystem() .Alloc(computeShaderResultTexture); @@ -142,7 +134,7 @@ private void CreateSMAATextures(int width, int height) enableRandomWrite = true }; smaaEdgesTex.Create(); - smaaEdgesTexHandle = HDRPCustomPass.GetRTHandleSystem().Alloc(smaaEdgesTex); + smaaEdgesTexHandle = CustomPass.GetRTHandleSystem().Alloc(smaaEdgesTex); smaaBlendTex = new RenderTexture( width, @@ -156,7 +148,7 @@ private void CreateSMAATextures(int width, int height) enableRandomWrite = true }; smaaBlendTex.Create(); - smaaBlendTexHandle = HDRPCustomPass.GetRTHandleSystem().Alloc(smaaBlendTex); + smaaBlendTexHandle = CustomPass.GetRTHandleSystem().Alloc(smaaBlendTex); } public void setAntiAliasingMode(AntialiasingMode mode) @@ -211,7 +203,7 @@ protected override void Execute(CustomPassContext ctx) { var camera = ctx.hdCamera.camera; HDAdditionalCameraData hdCamera = camera.GetComponent(); - if (isMainG3DCamera(camera)) + if (Helpers.isMainG3DCamera(camera)) { runReprojection(ctx); // color image now in mosaicImageHandle @@ -222,7 +214,13 @@ protected override void Execute(CustomPassContext ctx) if (debugRendering) { - if (fillHoles == false && antialiasingMode == AntialiasingMode.None) + if ( + fillHoles == false + && ( + antialiasingMode == AntialiasingMode.None + || antialiasingMode == AntialiasingMode.TAA + ) + ) { blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), mosaicImageHandle); } @@ -244,7 +242,13 @@ protected override void Execute(CustomPassContext ctx) } else { - if (fillHoles == false && antialiasingMode == AntialiasingMode.None) + if ( + fillHoles == false + && ( + antialiasingMode == AntialiasingMode.None + || antialiasingMode == AntialiasingMode.TAA + ) + ) { return; } @@ -474,35 +478,6 @@ private void runSMAA(CustomPassContext ctx, HDAdditionalCameraData hdCamera) ); } - /// - /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. - /// If so returns true, otherwise returns false. - /// - /// - /// - static bool isMainG3DCamera(Camera camera) - { - if (camera.cameraType != CameraType.Game) - return false; - bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; - - bool isMosaicMultiviewCamera = - camera.gameObject.TryGetComponent(out var mosaicCamera); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; - - if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) - return false; - - // skip all cameras created by the G3D Camera script - if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) - { - return false; - } - - return true; - } - protected override void Cleanup() { releaseSMAATextures(); diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs.meta b/Scripts/RenderPipelines/HDRP/ViewGenerationPass.cs.meta similarity index 100% rename from Scripts/RenderPipelines/HDRP/HDRPViewGenerationPass.cs.meta rename to Scripts/RenderPipelines/HDRP/ViewGenerationPass.cs.meta diff --git a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs b/Scripts/RenderPipelines/HelperFunctions.cs similarity index 54% rename from Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs rename to Scripts/RenderPipelines/HelperFunctions.cs index fe9d8cb..d680a92 100644 --- a/Scripts/RenderPipelines/HDRP/HDRPViewGenerationMosaicPass.cs +++ b/Scripts/RenderPipelines/HelperFunctions.cs @@ -1,48 +1,36 @@ #if G3D_HDRP -using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; -namespace G3D.RenderPass +namespace G3D.RenderPipeline { - internal class HDRPViewGenerationMosaicPass : FullScreenCustomPass + internal enum AntialiasingMode { - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } - - protected override void Execute(CustomPassContext ctx) - { - var camera = ctx.hdCamera.camera; - if (isMainG3DCamera(camera)) - { - CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); - ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_rows"), 4); - ctx.propertyBlock.SetFloat(Shader.PropertyToID("mosaic_columns"), 4); - CoreUtils.DrawFullScreen( - ctx.cmd, - fullscreenPassMaterial, - ctx.propertyBlock, - shaderPassId: 0 - ); - } - } + None, + FXAA, + SMAA, + TAA + } + internal class Helpers + { /// /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. /// If so returns true, otherwise returns false. /// /// /// - static bool isMainG3DCamera(Camera camera) + internal static bool isMainG3DCamera(Camera camera) { if (camera.cameraType != CameraType.Game) return false; bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; + bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; // only do something if our component is enabled bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent(out var mosaicCamera); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; + bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; // same check if it is a mosaic camera if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) return false; @@ -55,9 +43,6 @@ static bool isMainG3DCamera(Camera camera) return true; } - - protected override void Cleanup() { } } } - #endif diff --git a/Scripts/RenderPipelines/HelperFunctions.cs.meta b/Scripts/RenderPipelines/HelperFunctions.cs.meta new file mode 100644 index 0000000..8f29b7e --- /dev/null +++ b/Scripts/RenderPipelines/HelperFunctions.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 799f380d4db3cab488be49ae4a8ffd51 \ No newline at end of file diff --git a/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs b/Scripts/RenderPipelines/URP/ScriptableRP.cs similarity index 63% rename from Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs rename to Scripts/RenderPipelines/URP/ScriptableRP.cs index 5b17b5e..9af5bd7 100644 --- a/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs +++ b/Scripts/RenderPipelines/URP/ScriptableRP.cs @@ -5,9 +5,9 @@ using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.Universal; -namespace G3D.RenderPass +namespace G3D.RenderPipeline.URP { - internal class UrpScriptableRenderPass : ScriptableRenderPass + internal class ScriptableRP : ScriptableRenderPass { Material m_Material; @@ -18,7 +18,7 @@ private class PassData internal Material blitMaterial; } - public UrpScriptableRenderPass(Material material) + public ScriptableRP(Material material) { m_Material = material; renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing; @@ -43,7 +43,7 @@ ref RenderingData renderingData { var camera = renderingData.cameraData.camera; - if (performBlit(camera, m_Material)) + if (Helpers.isMainG3DCamera(camera, m_Material)) { CommandBuffer cmd = CommandBufferPool.Get(); Blitter.BlitCameraTexture( @@ -79,50 +79,11 @@ ref PassData passData passData.blitMaterial = m_Material; } - /// - /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. - /// If so returns true, otherwise returns false. - /// - /// - /// - static bool performBlit(Camera camera, Material blitMaterial) - { - if (camera.cameraType != CameraType.Game) - return false; - bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; - - bool isMosaicMultiviewCamera = - camera.gameObject.TryGetComponent(out var mosaicCamera); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; - - if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) - return false; - - if (blitMaterial == null) - return false; - - // skip all cameras created by the G3D Camera script - if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) - { - return false; - } - - return true; - } - static void ExecutePass(PassData data, RasterGraphContext context) { var camera = data.camera; - if (performBlit(camera, data.blitMaterial)) + if (Helpers.isMainG3DCamera(camera, data.blitMaterial)) { - // CommandBuffer cmd = CommandBufferPool.Get(); - // Blitter.BlitCameraTexture(cmd, data.src, data.src, data.blitMaterial, 0); - // Blitter. - // context.ExecuteCommandBuffer(cmd); - // cmd.Clear(); - // CommandBufferPool.Release(cmd); - Blitter.BlitTexture( context.cmd, data.src, @@ -139,10 +100,6 @@ public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer { string passName = "Blit With Material"; - // This simple pass copies the active color texture to a new texture using a custom material. This sample is for API demonstrative purposes, - // so the new texture is not used anywhere else in the frame, you can use the frame debugger to verify its contents. - - // add a raster render pass to the render graph, specifying the name and the data type that will be passed to the ExecutePass function using ( var builder = renderGraph.AddRasterRenderPass(passName, out var passData) ) @@ -150,6 +107,8 @@ public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer // Initialize the pass data InitPassData(renderGraph, frameData, ref passData); + // TODO check if these steps are needed + // // We disable culling for this pass for the demonstrative purpose of this sampe, as normally this pass would be culled, // // since the destination texture is not used anywhere else builder.AllowPassCulling(false); diff --git a/Scripts/RenderPipelines/URP/ScriptableRP.cs.meta b/Scripts/RenderPipelines/URP/ScriptableRP.cs.meta new file mode 100644 index 0000000..6532923 --- /dev/null +++ b/Scripts/RenderPipelines/URP/ScriptableRP.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: bb8964ec0b71d084e907dd3219da9dab \ No newline at end of file diff --git a/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs.meta b/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs.meta deleted file mode 100644 index 1bb5ebb..0000000 --- a/Scripts/RenderPipelines/URP/UrpScriptableRenderPass.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 993cb6f20dc048149b29bd7aba5d03e9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From f4e9b5d0f462fbb063b6c7f2ef866dc770aeba78 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 18 Mar 2026 11:53:25 +0100 Subject: [PATCH 102/149] moved view generation, smaa, fxaa and hole filling into their own render passes --- G3DCamera.cs | 19 - Scripts/RenderPipelines/HDRP/FXAA.cs | 104 +++++ Scripts/RenderPipelines/HDRP/FXAA.cs.meta | 2 + .../RenderPipelines/HDRP/HoleFillingPass.cs | 134 ++++++ .../HDRP/HoleFillingPass.cs.meta | 2 + Scripts/RenderPipelines/HDRP/SMAA.cs | 173 ++++++++ Scripts/RenderPipelines/HDRP/SMAA.cs.meta | 2 + .../HDRP/ViewGenerationPass.cs | 384 +----------------- 8 files changed, 418 insertions(+), 402 deletions(-) create mode 100644 Scripts/RenderPipelines/HDRP/FXAA.cs create mode 100644 Scripts/RenderPipelines/HDRP/FXAA.cs.meta create mode 100644 Scripts/RenderPipelines/HDRP/HoleFillingPass.cs create mode 100644 Scripts/RenderPipelines/HDRP/HoleFillingPass.cs.meta create mode 100644 Scripts/RenderPipelines/HDRP/SMAA.cs create mode 100644 Scripts/RenderPipelines/HDRP/SMAA.cs.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index 2d6d925..911e5c7 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -429,13 +429,6 @@ private void initCustomPass() recreateDepthTextures(); viewGenerationPass.indivDepthMaps = depthMosaicPass.indivDepthTextures; viewGenerationPass.debugRendering = debugRendering; - viewGenerationPass.fillHoles = isFillingHoles; - viewGenerationPass.holeFillingRadius = holeFillingRadius; - G3D.RenderPipeline.AntialiasingMode aaMode = getCameraAAMode(); - viewGenerationPass.init( - new Vector2Int(mainCamera.pixelWidth, mainCamera.pixelHeight), - aaMode - ); // add autostereo mosaic generation pass recreateMosaicTexture(); @@ -1100,17 +1093,6 @@ void Update() { updateShaderRenderTextures(); } - -#if G3D_HDRP - if (generateViews) - { - viewGenerationPass.holeFillingRadius = holeFillingRadius; - viewGenerationPass.fillHoles = isFillingHoles; - - G3D.RenderPipeline.AntialiasingMode aaMode = getCameraAAMode(); - viewGenerationPass.setAntiAliasingMode(aaMode); - } -#endif } private void updateScreenViewportProperties() @@ -1495,7 +1477,6 @@ public void updateShaderRenderTextures() #if G3D_HDRP recreateDepthTextures(); recreateMosaicTexture(); - viewGenerationPass.updateRenderResolution(new Vector2Int(Screen.width, Screen.height)); #endif } else diff --git a/Scripts/RenderPipelines/HDRP/FXAA.cs b/Scripts/RenderPipelines/HDRP/FXAA.cs new file mode 100644 index 0000000..737bfe8 --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/FXAA.cs @@ -0,0 +1,104 @@ +#if G3D_HDRP +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.HighDefinition; + +namespace G3D.RenderPipeline.HDRP +{ + internal class FXAA : FullScreenCustomPass + { + public RTHandle mosaicImageHandle; + + public RTHandle computeShaderResultTextureHandle; + + private Material blitMaterial; + + private ComputeShader fxaaCompShader; + private int fxaaKernel; + + public Vector2Int renderResolution = new Vector2Int(1920, 1080); + + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) + { + fxaaCompShader = Resources.Load("G3DFXAA"); + fxaaKernel = fxaaCompShader.FindKernel("FXAA"); + + blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); + blitMaterial.SetTexture( + Shader.PropertyToID("_mainTex"), + computeShaderResultTextureHandle + ); + } + + public void updateRenderResolution(Vector2Int resolution) + { + if (renderResolution.x == resolution.x && renderResolution.y == resolution.y) + { + return; + } + + renderResolution = resolution; + CreateComputeShaderResultTexture(); + } + + private void CreateComputeShaderResultTexture() + { + // release old texture if it exists + computeShaderResultTextureHandle?.Release(); + + computeShaderResultTextureHandle = CustomPass + .GetRTHandleSystem() + .Alloc( + renderResolution.x, + renderResolution.y, + colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_UNorm, + enableRandomWrite: true + ); + } + + protected override void Execute(CustomPassContext ctx) + { + var camera = ctx.hdCamera.camera; + if (Helpers.isMainG3DCamera(camera)) + { + runFXAA(ctx); + } + } + + private void runFXAA(CustomPassContext ctx) + { + Tonemapping tonemapping = ctx.hdCamera.volumeStack.GetComponent(); + float paperWhite = tonemapping.paperWhite.value; + Vector4 hdroutParameters = new Vector4(0, 1000, paperWhite, 1f / paperWhite); + + ctx.cmd.SetComputeTextureParam( + fxaaCompShader, + fxaaKernel, + "_colorMosaic", + mosaicImageHandle + ); + ctx.cmd.SetComputeTextureParam( + fxaaCompShader, + fxaaKernel, + "_OutputTexture", + computeShaderResultTextureHandle + ); + ctx.cmd.SetComputeVectorParam(fxaaCompShader, "_HDROutputParams", hdroutParameters); + ctx.cmd.DispatchCompute( + fxaaCompShader, + fxaaKernel, + mosaicImageHandle.rt.width, + mosaicImageHandle.rt.height, + 1 + ); + } + + protected override void Cleanup() + { + computeShaderResultTextureHandle?.Release(); + } + } +} + +#endif diff --git a/Scripts/RenderPipelines/HDRP/FXAA.cs.meta b/Scripts/RenderPipelines/HDRP/FXAA.cs.meta new file mode 100644 index 0000000..ab9edea --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/FXAA.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 64003de110fe15c4897ba3fc986f874c \ No newline at end of file diff --git a/Scripts/RenderPipelines/HDRP/HoleFillingPass.cs b/Scripts/RenderPipelines/HDRP/HoleFillingPass.cs new file mode 100644 index 0000000..3ce2b5b --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/HoleFillingPass.cs @@ -0,0 +1,134 @@ +#if G3D_HDRP +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.HighDefinition; + +namespace G3D.RenderPipeline.HDRP +{ + internal class HoleFillingPass : FullScreenCustomPass + { + public RTHandle mosaicImageHandle; + + private ComputeShader holeFillingCompShader; + private int holeFillingKernel; + + public RTHandle computeShaderResultTextureHandle; + + public int holeFillingRadius; + + private Material blitMaterial; + + public RTHandle[] indivDepthMaps; + public int internalCameraCount = 16; + + public Vector2Int renderResolution = new Vector2Int(1920, 1080); + + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) + { + holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); + holeFillingKernel = holeFillingCompShader.FindKernel("main"); + + blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); + blitMaterial.SetTexture( + Shader.PropertyToID("_mainTex"), + computeShaderResultTextureHandle + ); + } + + public void init(Vector2Int resolution, AntialiasingMode mode) + { + renderResolution = resolution; + CreateComputeShaderResultTexture(); + } + + public void updateRenderResolution(Vector2Int resolution) + { + if (renderResolution.x == resolution.x && renderResolution.y == resolution.y) + { + return; + } + + renderResolution = resolution; + CreateComputeShaderResultTexture(); + } + + private void CreateComputeShaderResultTexture() + { + // release old texture if it exists + computeShaderResultTextureHandle?.Release(); + + computeShaderResultTextureHandle = CustomPass + .GetRTHandleSystem() + .Alloc( + renderResolution.x, + renderResolution.y, + colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_UNorm, + enableRandomWrite: true + ); + } + + protected override void Execute(CustomPassContext ctx) + { + var camera = ctx.hdCamera.camera; + HDAdditionalCameraData hdCamera = camera.GetComponent(); + if (Helpers.isMainG3DCamera(camera)) + { + runHoleFilling(ctx); + } + } + + private void runHoleFilling(CustomPassContext ctx) + { + // fill holes in the mosaic image + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + holeFillingKernel, + "Result", + computeShaderResultTextureHandle + ); + for (int i = 0; i < internalCameraCount; i++) + { + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + holeFillingKernel, + "_depthMap" + i, + indivDepthMaps[i] + ); + } + ctx.cmd.SetComputeTextureParam( + holeFillingCompShader, + holeFillingKernel, + "_colorMosaic", + mosaicImageHandle + ); + ctx.cmd.SetComputeIntParams(holeFillingCompShader, "gridSize", new int[] { 4, 4 }); + ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); + ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); + ctx.cmd.SetComputeIntParams( + holeFillingCompShader, + "imageSize", + new int[] { mosaicImageHandle.rt.width, mosaicImageHandle.rt.height } + ); + + ctx.cmd.DispatchCompute( + holeFillingCompShader, + holeFillingKernel, + mosaicImageHandle.rt.width, + mosaicImageHandle.rt.height, + 1 + ); + + // Blit the result to the mosaic image + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); + } + + protected override void Cleanup() + { + computeShaderResultTextureHandle?.Release(); + } + } +} + +#endif diff --git a/Scripts/RenderPipelines/HDRP/HoleFillingPass.cs.meta b/Scripts/RenderPipelines/HDRP/HoleFillingPass.cs.meta new file mode 100644 index 0000000..a19045b --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/HoleFillingPass.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 79f28898e83f7814b8b12c56c453ffcd \ No newline at end of file diff --git a/Scripts/RenderPipelines/HDRP/SMAA.cs b/Scripts/RenderPipelines/HDRP/SMAA.cs new file mode 100644 index 0000000..10e1284 --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/SMAA.cs @@ -0,0 +1,173 @@ +#if G3D_HDRP +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.HighDefinition; + +namespace G3D.RenderPipeline.HDRP +{ + internal class SMAA : FullScreenCustomPass + { + public RTHandle mosaicImageHandle; + + public RTHandle computeShaderResultTextureHandle; + + private RTHandle smaaEdgesTexHandle; + private RTHandle smaaBlendTexHandle; + + private Material blitMaterial; + + private Material smaaMaterial; + + public Vector2Int renderResolution = new Vector2Int(1920, 1080); + + protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) + { + smaaMaterial = new Material(Shader.Find("G3D/SMAA")); + smaaMaterial.SetTexture("_AreaTex", Resources.Load("SMAA/AreaTex")); + // Import search tex as PNG because I can't get Unity to work with an R8 DDS file properly. + smaaMaterial.SetTexture("_SearchTex", Resources.Load("SMAA/SearchTex")); + + blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); + blitMaterial.SetTexture( + Shader.PropertyToID("_mainTex"), + computeShaderResultTextureHandle + ); + } + + public void updateRenderResolution(Vector2Int resolution) + { + if (renderResolution.x == resolution.x && renderResolution.y == resolution.y) + { + return; + } + + renderResolution = resolution; + CreateSMAATextures(renderResolution.x, renderResolution.y); + } + + private void CreateSMAATextures(int width, int height) + { + releaseSMAATextures(); + + smaaEdgesTexHandle = CustomPass + .GetRTHandleSystem() + .Alloc( + width, + height, + colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_UNorm, + name: "SMAAEdgesTex", + enableRandomWrite: true + ); + + smaaBlendTexHandle = CustomPass + .GetRTHandleSystem() + .Alloc( + width, + height, + colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_UNorm, + name: "SMAABlendTex", + enableRandomWrite: true + ); + + computeShaderResultTextureHandle = CustomPass + .GetRTHandleSystem() + .Alloc( + width, + height, + colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_UNorm, + enableRandomWrite: true + ); + } + + private void releaseSMAATextures() + { + smaaBlendTexHandle?.Release(); + smaaEdgesTexHandle?.Release(); + computeShaderResultTextureHandle?.Release(); + } + + protected override void Execute(CustomPassContext ctx) + { + var camera = ctx.hdCamera.camera; + HDAdditionalCameraData hdCamera = camera.GetComponent(); + if (Helpers.isMainG3DCamera(camera)) + { + runSMAA(ctx, hdCamera); + } + } + + private void runSMAA(CustomPassContext ctx, HDAdditionalCameraData hdCamera) + { + smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); + smaaMaterial.SetVector( + Shader.PropertyToID("_SMAARTMetrics"), + new Vector4( + 1.0f / mosaicImageHandle.rt.width, + 1.0f / mosaicImageHandle.rt.height, + mosaicImageHandle.rt.width, + mosaicImageHandle.rt.height + ) + ); + smaaMaterial.SetInt(Shader.PropertyToID("_StencilRef"), (int)(1 << 2)); + smaaMaterial.SetInt(Shader.PropertyToID("_StencilCmp"), (int)(1 << 2)); + + switch (hdCamera.SMAAQuality) + { + case HDAdditionalCameraData.SMAAQualityLevel.Low: + smaaMaterial.EnableKeyword("SMAA_PRESET_LOW"); + break; + case HDAdditionalCameraData.SMAAQualityLevel.Medium: + smaaMaterial.EnableKeyword("SMAA_PRESET_MEDIUM"); + break; + case HDAdditionalCameraData.SMAAQualityLevel.High: + smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); + break; + default: + smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); + break; + } + + // ----------------------------------------------------------------------------- + // EdgeDetection stage + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); + CoreUtils.SetRenderTarget(ctx.cmd, smaaEdgesTexHandle, ClearFlag.Color); + CoreUtils.DrawFullScreen( + ctx.cmd, + smaaMaterial, + ctx.propertyBlock, + shaderPassId: smaaMaterial.FindPass("EdgeDetection") + ); + + // ----------------------------------------------------------------------------- + // BlendWeights stage + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), smaaEdgesTexHandle); + CoreUtils.SetRenderTarget(ctx.cmd, smaaBlendTexHandle, ClearFlag.Color); + CoreUtils.DrawFullScreen( + ctx.cmd, + smaaMaterial, + ctx.propertyBlock, + shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation") + ); + + // ----------------------------------------------------------------------------- + // NeighborhoodBlending stage + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); + ctx.propertyBlock.SetTexture(Shader.PropertyToID("_BlendTex"), smaaBlendTexHandle); + CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTextureHandle, ClearFlag.None); + CoreUtils.DrawFullScreen( + ctx.cmd, + smaaMaterial, + ctx.propertyBlock, + shaderPassId: smaaMaterial.FindPass("NeighborhoodBlending") + ); + } + + protected override void Cleanup() + { + releaseSMAATextures(); + } + } +} + +#endif diff --git a/Scripts/RenderPipelines/HDRP/SMAA.cs.meta b/Scripts/RenderPipelines/HDRP/SMAA.cs.meta new file mode 100644 index 0000000..677ae8b --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/SMAA.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3c9c50e8d556c1046a613aacedcf2946 \ No newline at end of file diff --git a/Scripts/RenderPipelines/HDRP/ViewGenerationPass.cs b/Scripts/RenderPipelines/HDRP/ViewGenerationPass.cs index 9f14e6e..175f670 100644 --- a/Scripts/RenderPipelines/HDRP/ViewGenerationPass.cs +++ b/Scripts/RenderPipelines/HDRP/ViewGenerationPass.cs @@ -15,180 +15,20 @@ internal class ViewGeneration : FullScreenCustomPass public int internalCameraCount = 16; public RTHandle mosaicImageHandle; - - private ComputeShader holeFillingCompShader; - private int holeFillingKernel; - - public RenderTexture computeShaderResultTexture; - public RTHandle computeShaderResultTextureHandle; - - private RenderTexture smaaEdgesTex; - private RTHandle smaaEdgesTexHandle; - private RenderTexture smaaBlendTex; - private RTHandle smaaBlendTexHandle; - - public int holeFillingRadius; - - public bool fillHoles; - public bool debugRendering; private Material blitMaterial; public RTHandle[] indivDepthMaps; - private ComputeShader fxaaCompShader; - private int fxaaKernel; - - private Material smaaMaterial; - private AntialiasingMode antialiasingMode = AntialiasingMode.None; - - public Vector2Int renderResolution = new Vector2Int(1920, 1080); - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { - if (fillHoles) - { - holeFillingCompShader = Resources.Load("G3DViewGenHoleFilling"); - holeFillingKernel = holeFillingCompShader.FindKernel("main"); - } - - fxaaCompShader = Resources.Load("G3DFXAA"); - fxaaKernel = fxaaCompShader.FindKernel("FXAA"); - - smaaMaterial = new Material(Shader.Find("G3D/SMAA")); - smaaMaterial.SetTexture("_AreaTex", Resources.Load("SMAA/AreaTex")); - // Import search tex as PNG because I can't get Unity to work with an R8 DDS file properly. - smaaMaterial.SetTexture("_SearchTex", Resources.Load("SMAA/SearchTex")); - blitMaterial = new Material(Shader.Find("G3D/G3DBlit")); - blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), computeShaderResultTexture); - } - - public void init(Vector2Int resolution, AntialiasingMode mode) - { - renderResolution = resolution; - CreateComputeShaderResultTexture(); - setAntiAliasingMode(mode); - } - - public void updateRenderResolution(Vector2Int resolution) - { - if (renderResolution.x == resolution.x && renderResolution.y == resolution.y) - { - return; - } - - renderResolution = resolution; - CreateComputeShaderResultTexture(); - - if (antialiasingMode == AntialiasingMode.SMAA) - { - CreateSMAATextures(renderResolution.x, renderResolution.y); - } - } - - private void CreateComputeShaderResultTexture() - { - // release old texture if it exists - computeShaderResultTexture?.Release(); - computeShaderResultTextureHandle?.Release(); - - computeShaderResultTexture = new RenderTexture( - renderResolution.x, - renderResolution.y, - 0, - RenderTextureFormat.ARGB32, - RenderTextureReadWrite.Linear - ) - { - enableRandomWrite = true - }; - computeShaderResultTexture.Create(); - computeShaderResultTextureHandle = CustomPass - .GetRTHandleSystem() - .Alloc(computeShaderResultTexture); - - if (blitMaterial != null) - { - blitMaterial.SetTexture( - Shader.PropertyToID("_mainTex"), - computeShaderResultTexture - ); - } - } - - private void CreateSMAATextures(int width, int height) - { - releaseSMAATextures(); - - smaaEdgesTex = new RenderTexture( - width, - height, - 0, - RenderTextureFormat.ARGB32, - RenderTextureReadWrite.Linear - ) - { - name = "SMAAEdgesTex", - enableRandomWrite = true - }; - smaaEdgesTex.Create(); - smaaEdgesTexHandle = CustomPass.GetRTHandleSystem().Alloc(smaaEdgesTex); - - smaaBlendTex = new RenderTexture( - width, - height, - 0, - RenderTextureFormat.ARGB32, - RenderTextureReadWrite.Linear - ) - { - name = "SMAABlendTex", - enableRandomWrite = true - }; - smaaBlendTex.Create(); - smaaBlendTexHandle = CustomPass.GetRTHandleSystem().Alloc(smaaBlendTex); - } - - public void setAntiAliasingMode(AntialiasingMode mode) - { - AntialiasingMode oldMode = antialiasingMode; - if (oldMode == mode) - { - return; - } - - antialiasingMode = mode; - if ( - mode == AntialiasingMode.None - || mode == AntialiasingMode.FXAA - || mode == AntialiasingMode.TAA - ) - { - releaseSMAATextures(); - } - else if (mode == AntialiasingMode.SMAA) - { - CreateSMAATextures(renderResolution.x, renderResolution.y); - } - } - - private void releaseSMAATextures() - { - smaaEdgesTex?.Release(); - smaaBlendTex?.Release(); - smaaBlendTexHandle?.Release(); - smaaEdgesTexHandle?.Release(); } private void setMatrix(CustomPassContext ctx, Matrix4x4 matrix, string name) { ctx.propertyBlock.SetMatrix(Shader.PropertyToID(name), matrix); - if (fillHoles) - { - ctx.cmd.SetComputeMatrixParam(holeFillingCompShader, name, matrix); - } } private void addViewProjectionMatrix(CustomPassContext ctx, Camera camera, string name) @@ -208,29 +48,9 @@ protected override void Execute(CustomPassContext ctx) runReprojection(ctx); // color image now in mosaicImageHandle - runHoleFilling(ctx); - runFXAA(ctx); - runSMAA(ctx, hdCamera); - if (debugRendering) { - if ( - fillHoles == false - && ( - antialiasingMode == AntialiasingMode.None - || antialiasingMode == AntialiasingMode.TAA - ) - ) - { - blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), mosaicImageHandle); - } - else - { - blitMaterial.SetTexture( - Shader.PropertyToID("_mainTex"), - computeShaderResultTexture - ); - } + blitMaterial.SetTexture(Shader.PropertyToID("_mainTex"), mosaicImageHandle); CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); CoreUtils.DrawFullScreen( @@ -240,32 +60,6 @@ protected override void Execute(CustomPassContext ctx) shaderPassId: 0 ); } - else - { - if ( - fillHoles == false - && ( - antialiasingMode == AntialiasingMode.None - || antialiasingMode == AntialiasingMode.TAA - ) - ) - { - return; - } - - blitMaterial.SetTexture( - Shader.PropertyToID("_mainTex"), - computeShaderResultTexture - ); - - CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); - CoreUtils.DrawFullScreen( - ctx.cmd, - blitMaterial, - ctx.propertyBlock, - shaderPassId: 0 - ); - } } } @@ -293,20 +87,6 @@ private void runReprojection(CustomPassContext ctx) invProjMatrices[i] = invGPUProjMatrix; } - if (fillHoles) - { - ctx.cmd.SetComputeMatrixArrayParam( - holeFillingCompShader, - "viewMatrices", - viewMatrices - ); - ctx.cmd.SetComputeMatrixArrayParam( - holeFillingCompShader, - "invProjMatrices", - invProjMatrices - ); - } - addViewProjectionMatrix(ctx, cameras[0], "leftViewProjMatrix"); addViewProjectionMatrix(ctx, cameras[internalCameraCount / 2], "middleViewProjMatrix"); addViewProjectionMatrix(ctx, cameras[internalCameraCount - 1], "rightViewProjMatrix"); @@ -322,168 +102,6 @@ private void runReprojection(CustomPassContext ctx) shaderPassId: 0 ); } - - private void runHoleFilling(CustomPassContext ctx) - { - if (fillHoles == false) - { - return; - } - - // fill holes in the mosaic image - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - holeFillingKernel, - "Result", - computeShaderResultTexture - ); - for (int i = 0; i < internalCameraCount; i++) - { - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - holeFillingKernel, - "_depthMap" + i, - indivDepthMaps[i] - ); - } - ctx.cmd.SetComputeTextureParam( - holeFillingCompShader, - holeFillingKernel, - "_colorMosaic", - mosaicImageHandle - ); - ctx.cmd.SetComputeIntParams(holeFillingCompShader, "gridSize", new int[] { 4, 4 }); - ctx.cmd.SetComputeIntParam(holeFillingCompShader, "radius", holeFillingRadius); - ctx.cmd.SetComputeFloatParam(holeFillingCompShader, "sigma", holeFillingRadius / 2.0f); - ctx.cmd.SetComputeIntParams( - holeFillingCompShader, - "imageSize", - new int[] { mosaicImageHandle.rt.width, mosaicImageHandle.rt.height } - ); - - ctx.cmd.DispatchCompute( - holeFillingCompShader, - holeFillingKernel, - mosaicImageHandle.rt.width, - mosaicImageHandle.rt.height, - 1 - ); - - // Blit the result to the mosaic image - CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); - CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, ctx.propertyBlock, shaderPassId: 0); - } - - private void runFXAA(CustomPassContext ctx) - { - if (antialiasingMode != AntialiasingMode.FXAA) - { - return; - } - - Tonemapping tonemapping = ctx.hdCamera.volumeStack.GetComponent(); - float paperWhite = tonemapping.paperWhite.value; - Vector4 hdroutParameters = new Vector4(0, 1000, paperWhite, 1f / paperWhite); - - ctx.cmd.SetComputeTextureParam( - fxaaCompShader, - fxaaKernel, - "_colorMosaic", - mosaicImageHandle - ); - ctx.cmd.SetComputeTextureParam( - fxaaCompShader, - fxaaKernel, - "_OutputTexture", - computeShaderResultTexture - ); - ctx.cmd.SetComputeVectorParam(fxaaCompShader, "_HDROutputParams", hdroutParameters); - ctx.cmd.DispatchCompute( - fxaaCompShader, - fxaaKernel, - mosaicImageHandle.rt.width, - mosaicImageHandle.rt.height, - 1 - ); - } - - private void runSMAA(CustomPassContext ctx, HDAdditionalCameraData hdCamera) - { - if (antialiasingMode != AntialiasingMode.SMAA) - { - return; - } - - smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); - smaaMaterial.SetVector( - Shader.PropertyToID("_SMAARTMetrics"), - new Vector4( - 1.0f / mosaicImageHandle.rt.width, - 1.0f / mosaicImageHandle.rt.height, - mosaicImageHandle.rt.width, - mosaicImageHandle.rt.height - ) - ); - smaaMaterial.SetInt(Shader.PropertyToID("_StencilRef"), (int)(1 << 2)); - smaaMaterial.SetInt(Shader.PropertyToID("_StencilCmp"), (int)(1 << 2)); - - switch (hdCamera.SMAAQuality) - { - case HDAdditionalCameraData.SMAAQualityLevel.Low: - smaaMaterial.EnableKeyword("SMAA_PRESET_LOW"); - break; - case HDAdditionalCameraData.SMAAQualityLevel.Medium: - smaaMaterial.EnableKeyword("SMAA_PRESET_MEDIUM"); - break; - case HDAdditionalCameraData.SMAAQualityLevel.High: - smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); - break; - default: - smaaMaterial.EnableKeyword("SMAA_PRESET_HIGH"); - break; - } - - // ----------------------------------------------------------------------------- - // EdgeDetection stage - ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); - CoreUtils.SetRenderTarget(ctx.cmd, smaaEdgesTex, ClearFlag.Color); - CoreUtils.DrawFullScreen( - ctx.cmd, - smaaMaterial, - ctx.propertyBlock, - shaderPassId: smaaMaterial.FindPass("EdgeDetection") - ); - - // ----------------------------------------------------------------------------- - // BlendWeights stage - ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), smaaEdgesTex); - CoreUtils.SetRenderTarget(ctx.cmd, smaaBlendTex, ClearFlag.Color); - CoreUtils.DrawFullScreen( - ctx.cmd, - smaaMaterial, - ctx.propertyBlock, - shaderPassId: smaaMaterial.FindPass("BlendingWeightCalculation") - ); - - // ----------------------------------------------------------------------------- - // NeighborhoodBlending stage - ctx.propertyBlock.SetTexture(Shader.PropertyToID("_InputTexture"), mosaicImageHandle); - ctx.propertyBlock.SetTexture(Shader.PropertyToID("_BlendTex"), smaaBlendTex); - CoreUtils.SetRenderTarget(ctx.cmd, computeShaderResultTextureHandle, ClearFlag.None); - CoreUtils.DrawFullScreen( - ctx.cmd, - smaaMaterial, - ctx.propertyBlock, - shaderPassId: smaaMaterial.FindPass("NeighborhoodBlending") - ); - } - - protected override void Cleanup() - { - releaseSMAATextures(); - computeShaderResultTextureHandle?.Release(); - computeShaderResultTexture?.Release(); - } } } From 193159fa34376a90e26a413244487a42f43614cf Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 18 Mar 2026 12:29:32 +0100 Subject: [PATCH 103/149] renamed helpers file --- Scripts/RenderPipelines/{HelperFunctions.cs => Helpers.cs} | 0 .../RenderPipelines/{HelperFunctions.cs.meta => Helpers.cs.meta} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Scripts/RenderPipelines/{HelperFunctions.cs => Helpers.cs} (100%) rename Scripts/RenderPipelines/{HelperFunctions.cs.meta => Helpers.cs.meta} (100%) diff --git a/Scripts/RenderPipelines/HelperFunctions.cs b/Scripts/RenderPipelines/Helpers.cs similarity index 100% rename from Scripts/RenderPipelines/HelperFunctions.cs rename to Scripts/RenderPipelines/Helpers.cs diff --git a/Scripts/RenderPipelines/HelperFunctions.cs.meta b/Scripts/RenderPipelines/Helpers.cs.meta similarity index 100% rename from Scripts/RenderPipelines/HelperFunctions.cs.meta rename to Scripts/RenderPipelines/Helpers.cs.meta From becfd48a22dbc27e0a1500a96e50ca973be32cbc Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 18 Mar 2026 14:31:59 +0100 Subject: [PATCH 104/149] moved custom pass handling to own class; removed hole filling no fxaa, smaa, hole filling --- G3DCamera.cs | 184 ++----------- Resources/G3DCameraInspector.uxml | 2 - Scripts/RenderPipelines/HDRP/CustomPass.cs | 12 - .../HDRP/CustomPassController.cs | 250 ++++++++++++++++++ .../HDRP/CustomPassController.cs.meta | 2 + .../RenderPipelines/HDRP/DepthMapPrePass.cs | 4 +- Scripts/RenderPipelines/HDRP/FXAA.cs | 2 +- .../RenderPipelines/HDRP/HoleFillingPass.cs | 2 +- Scripts/RenderPipelines/HDRP/SMAA.cs | 6 +- Scripts/RenderPipelines/Helpers.cs | 12 + 10 files changed, 292 insertions(+), 184 deletions(-) create mode 100644 Scripts/RenderPipelines/HDRP/CustomPassController.cs create mode 100644 Scripts/RenderPipelines/HDRP/CustomPassController.cs.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index 911e5c7..cf356a5 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -56,13 +56,6 @@ public class G3DCamera [Tooltip("Only actually renders three views. The rest are generated. IF turned on anti ali")] public bool generateViews = true; - [Tooltip( - "Fill small holes in generated views. (Only takes effect if view generation is turned on.)" - )] - public bool isFillingHoles; - - [Range(0, 64)] - public int holeFillingRadius = 8; private bool useVectorMapViewGeneration = false; #endregion @@ -230,22 +223,16 @@ private float scaledHalfCameraWidthAtStart #endregion private Material viewGenerationMaterial; -#if G3D_HDRP - private G3D.RenderPipeline.HDRP.ViewGeneration viewGenerationPass; - private G3D.RenderPipeline.HDRP.DepthMaps depthMosaicPass; - - /// - /// Used for view generation mosaic rendering. - /// - private RenderTexture mosaicTexture; - private RTHandle rtHandleMosaic; -#endif private RenderTexture[] colorRenderTextures = null; private int mainCamCullingMask = -1; private float originalMainFOV; private CameraClearFlags originalMainClearFlags; +#if G3D_HDRP + private G3D.RenderPipeline.HDRP.CustomPassController customPassController; +#endif + // TODO Handle viewport resizing/ moving #region Initialization @@ -272,10 +259,17 @@ void Start() setupCameras(); reinitializeShader(); -#if G3D_HDRP - initCustomPass(); - antialiasingMode = mainCamera.GetComponent().antialiasing; +#if G3D_HDRP + customPassController = + gameObject.AddComponent(); + viewGenerationMaterial = customPassController.init( + internalCameraCount, + cameras, + mainCamCullingMask, + ref material, + renderResolutionScale + ); #endif #if G3D_URP @@ -395,106 +389,6 @@ private void InitMainCamera() originalMainClearFlags = mainCamera.clearFlags; } -#if G3D_HDRP - private void initCustomPass() - { - // init fullscreen postprocessing for hd render pipeline - CustomPassVolume customPassVolume = gameObject.AddComponent(); - customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; - customPassVolume.isGlobal = true; - // Make the volume invisible in the inspector - customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; - - if (generateViews) - { - // add depth mosaic generation pass - depthMosaicPass = - customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.DepthMaps)) - as G3D.RenderPipeline.HDRP.DepthMaps; - depthMosaicPass.cullingMask = mainCamCullingMask; - depthMosaicPass.cameras = cameras; - depthMosaicPass.internalCameraCount = internalCameraCount; - - // add multiview generation pass - viewGenerationPass = - customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.ViewGeneration)) - as G3D.RenderPipeline.HDRP.ViewGeneration; - viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); - - viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; - viewGenerationPass.materialPassName = "G3DViewGeneration"; - viewGenerationPass.cameras = cameras; - viewGenerationPass.internalCameraCount = internalCameraCount; - - recreateDepthTextures(); - viewGenerationPass.indivDepthMaps = depthMosaicPass.indivDepthTextures; - viewGenerationPass.debugRendering = debugRendering; - - // add autostereo mosaic generation pass - recreateMosaicTexture(); - - if (debugRendering == false) - { - G3D.RenderPipeline.HDRP.ViewGenerationMosaic finalAutostereoGeneration = - customPassVolume.AddPassOfType( - typeof(G3D.RenderPipeline.HDRP.ViewGenerationMosaic) - ) as G3D.RenderPipeline.HDRP.ViewGenerationMosaic; - finalAutostereoGeneration.fullscreenPassMaterial = material; - finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; - } - } - else - { - G3D.RenderPipeline.HDRP.CustomPass customPass = - customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.CustomPass)) - as G3D.RenderPipeline.HDRP.CustomPass; - customPass.fullscreenPassMaterial = material; - customPass.materialPassName = "G3DFullScreen3D"; - } - } - - private void recreateDepthTextures() - { - depthMosaicPass.recreateDepthTextures(renderResolutionScale); - - for (int i = 0; i < internalCameraCount; i++) - { - viewGenerationPass.fullscreenPassMaterial.SetTexture( - "_depthMap" + i, - depthMosaicPass.indivDepthTextures[i], - RenderTextureSubElement.Depth - ); - } - } - - private void recreateMosaicTexture() - { - if (mosaicTexture != null) - { - mosaicTexture.Release(); - } - - mosaicTexture = new RenderTexture( - mainCamera.pixelWidth, - mainCamera.pixelHeight, - 0, - RenderTextureFormat.ARGB32, - RenderTextureReadWrite.sRGB - ); - mosaicTexture.enableRandomWrite = true; - - if (rtHandleMosaic != null) - { - G3D.RenderPipeline.HDRP.CustomPass.GetRTHandleSystem().Release(rtHandleMosaic); - } - rtHandleMosaic = G3D - .RenderPipeline.HDRP.CustomPass.GetRTHandleSystem() - .Alloc(mosaicTexture); - material.SetTexture("_colorMosaic", rtHandleMosaic); - viewGenerationPass.mosaicImageHandle = rtHandleMosaic; - } -#endif - void OnApplicationQuit() { deinitLibrary(); @@ -548,11 +442,6 @@ private void OnBeginCamera(ScriptableRenderContext context, Camera cam) } #endif - /// - /// this variable is onle here to track changes made to the public calibration file from the editor. - /// - - /// /// OnValidate gets called every time the script is changed in the editor. /// This is used to react to changes made to the parameters. @@ -872,31 +761,6 @@ private Vector2Int getDisplayResolutionFromCalibrationFile() return new Vector2Int(HorizontalResolution, VerticalResolution); } -#if G3D_HDRP - private G3D.RenderPipeline.AntialiasingMode getCameraAAMode() - { - G3D.RenderPipeline.AntialiasingMode aaMode = G3D.RenderPipeline.AntialiasingMode.None; - if ( - antialiasingMode - == HDAdditionalCameraData.AntialiasingMode.SubpixelMorphologicalAntiAliasing - ) - { - aaMode = G3D.RenderPipeline.AntialiasingMode.SMAA; - } - else if ( - antialiasingMode == HDAdditionalCameraData.AntialiasingMode.FastApproximateAntialiasing - ) - { - aaMode = G3D.RenderPipeline.AntialiasingMode.FXAA; - } - else if (antialiasingMode == HDAdditionalCameraData.AntialiasingMode.TemporalAntialiasing) - { - aaMode = G3D.RenderPipeline.AntialiasingMode.TAA; - } - return aaMode; - } -#endif - private void initLibrary() { string applicationName = Application.productName; @@ -1074,13 +938,6 @@ void Update() if (windowResizedLastFrame) { recreatedRenderTextures = true; -#if G3D_HDRP - G3D.RenderPipeline.HDRP.CustomPass.GetRTHandleSystem() - .ResetReferenceSize(Screen.width, Screen.height); - // TODO this is only needed to reset the size of the cameras internal render targets - // Find a way to avoid this line... - RTHandles.ResetReferenceSize(Screen.width, Screen.height); -#endif } if (cameraCountChanged || oldRenderResolutionScale != renderResolutionScale) @@ -1089,6 +946,11 @@ void Update() recreatedRenderTextures = true; } + customPassController.cameraCountChanged = cameraCountChanged; + customPassController.resolutionScaleChanged = + oldRenderResolutionScale != renderResolutionScale; + customPassController.debugRendering = debugRendering; + if (recreatedRenderTextures) { updateShaderRenderTextures(); @@ -1415,8 +1277,8 @@ private void addRenderTextureToCamera( int width = Screen.width; int height = Screen.height; - width = (int)(width * ((float)renderResolutionScale / 100f)); - height = (int)(height * ((float)renderResolutionScale / 100f)); + width = (int)(width * (renderResolutionScale / 100f)); + height = (int)(height * (renderResolutionScale / 100f)); renderTextures[renderTextureIndex] = new RenderTexture(width, height, 0) { @@ -1474,10 +1336,6 @@ public void updateShaderRenderTextures() internalCameraCount - 1, "_rightCamTex" ); // right camera -#if G3D_HDRP - recreateDepthTextures(); - recreateMosaicTexture(); -#endif } else { diff --git a/Resources/G3DCameraInspector.uxml b/Resources/G3DCameraInspector.uxml index 6ec33f1..7c896b8 100644 --- a/Resources/G3DCameraInspector.uxml +++ b/Resources/G3DCameraInspector.uxml @@ -25,8 +25,6 @@ - - diff --git a/Scripts/RenderPipelines/HDRP/CustomPass.cs b/Scripts/RenderPipelines/HDRP/CustomPass.cs index f44b19d..21e47e1 100644 --- a/Scripts/RenderPipelines/HDRP/CustomPass.cs +++ b/Scripts/RenderPipelines/HDRP/CustomPass.cs @@ -7,18 +7,6 @@ namespace G3D.RenderPipeline.HDRP { internal class CustomPass : FullScreenCustomPass { - private static RTHandleSystem m_RTHandleSystem; - - public static RTHandleSystem GetRTHandleSystem() - { - if (m_RTHandleSystem == null) - { - m_RTHandleSystem = new RTHandleSystem(); - m_RTHandleSystem.Initialize(Screen.width, Screen.height); - } - return m_RTHandleSystem; - } - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } protected override void Execute(CustomPassContext ctx) diff --git a/Scripts/RenderPipelines/HDRP/CustomPassController.cs b/Scripts/RenderPipelines/HDRP/CustomPassController.cs new file mode 100644 index 0000000..e42535a --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/CustomPassController.cs @@ -0,0 +1,250 @@ +#if G3D_HDRP +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.HighDefinition; + +namespace G3D.RenderPipeline.HDRP +{ + [HideInInspector] + [DisallowMultipleComponent] + [RequireComponent(typeof(G3DCamera))] + [RequireComponent(typeof(Camera))] + public class CustomPassController : MonoBehaviour + { + private G3DCamera g3dCamera; + private Camera mainCamera; + + private HDAdditionalCameraData.AntialiasingMode antialiasingMode = HDAdditionalCameraData + .AntialiasingMode + .None; + + private ViewGeneration viewGenerationPass; + private DepthMaps depthMosaicPass; + + /// + /// Used for view generation mosaic rendering. + /// + private RenderTexture mosaicTexture; + private RTHandle rtHandleMosaic; + + public bool cameraCountChanged = false; + public bool resolutionScaleChanged = false; + public bool debugRendering = false; + public bool generateViews = true; + private bool windowResizedLastFrame = false; + private bool mainCamInactiveLastFrame = false; + private int mainCamCullingMask = -1; + private int internalCameraCount = 16; + private int renderResolutionScale = 100; + private List cameras; + private Material viewGenerationMaterial; + private Material material; + + private Vector2Int cachedWindowSize; + + // Start is called before the first frame update + public Material init( + int internalCameraCount, + List cameras, + int mainCamCullingMask, + ref Material material, + int renderResolutionScale = 100 + ) + { + this.internalCameraCount = internalCameraCount; + this.mainCamCullingMask = mainCamCullingMask; + this.cameras = cameras; + this.material = material; + this.renderResolutionScale = renderResolutionScale; + + g3dCamera = GetComponent(); + mainCamera = GetComponent(); + + initCustomPass(); + antialiasingMode = mainCamera.GetComponent().antialiasing; + + return viewGenerationMaterial; + } + + // Update is called once per frame + void Update() + { + if (mainCamera.enabled == false) + { + mainCamInactiveLastFrame = true; + return; + } + + bool recreatedRenderTextures = false; + + if (mainCamInactiveLastFrame) + { + mainCamInactiveLastFrame = false; + // recreate shader render textures when main camera was inactive last frame + recreatedRenderTextures = true; + } + + windowResizedLastFrame = windowResized(); + + if (windowResizedLastFrame) + { + recreatedRenderTextures = true; + Helpers.GetRTHandleSystem().ResetReferenceSize(Screen.width, Screen.height); + // TODO this is only needed to reset the size of the cameras internal render targets + // Find a way to avoid this line... + RTHandles.ResetReferenceSize(Screen.width, Screen.height); + } + + if (cameraCountChanged || resolutionScaleChanged) + { + recreatedRenderTextures = true; + } + + if (recreatedRenderTextures) + { + recreateRenderTextures(); + } + } + + private void initCustomPass() + { + // init fullscreen postprocessing for hd render pipeline + CustomPassVolume customPassVolume = gameObject.AddComponent(); + customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; + customPassVolume.isGlobal = true; + // Make the volume invisible in the inspector + customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; + + if (generateViews) + { + // add depth mosaic generation pass + depthMosaicPass = + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.DepthMaps)) + as G3D.RenderPipeline.HDRP.DepthMaps; + depthMosaicPass.cullingMask = mainCamCullingMask; + depthMosaicPass.cameras = cameras; + depthMosaicPass.internalCameraCount = internalCameraCount; + + // add multiview generation pass + viewGenerationPass = + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.ViewGeneration)) + as G3D.RenderPipeline.HDRP.ViewGeneration; + viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); + + viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; + viewGenerationPass.materialPassName = "G3DViewGeneration"; + viewGenerationPass.cameras = cameras; + viewGenerationPass.internalCameraCount = internalCameraCount; + + recreateDepthTextures(); + viewGenerationPass.indivDepthMaps = depthMosaicPass.indivDepthTextures; + viewGenerationPass.debugRendering = debugRendering; + + // add autostereo mosaic generation pass + recreateMosaicTexture(); + + if (debugRendering == false) + { + G3D.RenderPipeline.HDRP.ViewGenerationMosaic finalAutostereoGeneration = + customPassVolume.AddPassOfType( + typeof(G3D.RenderPipeline.HDRP.ViewGenerationMosaic) + ) as G3D.RenderPipeline.HDRP.ViewGenerationMosaic; + finalAutostereoGeneration.fullscreenPassMaterial = material; + finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; + } + } + else + { + G3D.RenderPipeline.HDRP.CustomPass customPass = + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.CustomPass)) + as G3D.RenderPipeline.HDRP.CustomPass; + customPass.fullscreenPassMaterial = material; + customPass.materialPassName = "G3DFullScreen3D"; + } + } + + private void recreateDepthTextures() + { + depthMosaicPass.recreateDepthTextures(renderResolutionScale); + + for (int i = 0; i < internalCameraCount; i++) + { + viewGenerationPass.fullscreenPassMaterial.SetTexture( + "_depthMap" + i, + depthMosaicPass.indivDepthTextures[i], + RenderTextureSubElement.Depth + ); + } + } + + private void recreateMosaicTexture() + { + if (mosaicTexture != null) + { + mosaicTexture.Release(); + } + + mosaicTexture = new RenderTexture( + mainCamera.pixelWidth, + mainCamera.pixelHeight, + 0, + RenderTextureFormat.ARGB32, + RenderTextureReadWrite.sRGB + ); + mosaicTexture.enableRandomWrite = true; + + if (rtHandleMosaic != null) + { + Helpers.GetRTHandleSystem().Release(rtHandleMosaic); + } + rtHandleMosaic = Helpers.GetRTHandleSystem().Alloc(mosaicTexture); + material.SetTexture("_colorMosaic", rtHandleMosaic); + viewGenerationPass.mosaicImageHandle = rtHandleMosaic; + } + + private bool windowResized() + { + var window_dim = new Vector2Int(Screen.width, Screen.height); + if (cachedWindowSize != window_dim) + { + cachedWindowSize = window_dim; + return true; + } + return false; + } + + private void recreateRenderTextures() + { + recreateDepthTextures(); + recreateMosaicTexture(); + } + + private G3D.RenderPipeline.AntialiasingMode getCameraAAMode() + { + G3D.RenderPipeline.AntialiasingMode aaMode = G3D.RenderPipeline.AntialiasingMode.None; + if ( + antialiasingMode + == HDAdditionalCameraData.AntialiasingMode.SubpixelMorphologicalAntiAliasing + ) + { + aaMode = G3D.RenderPipeline.AntialiasingMode.SMAA; + } + else if ( + antialiasingMode + == HDAdditionalCameraData.AntialiasingMode.FastApproximateAntialiasing + ) + { + aaMode = G3D.RenderPipeline.AntialiasingMode.FXAA; + } + else if ( + antialiasingMode == HDAdditionalCameraData.AntialiasingMode.TemporalAntialiasing + ) + { + aaMode = G3D.RenderPipeline.AntialiasingMode.TAA; + } + return aaMode; + } + } +} +#endif diff --git a/Scripts/RenderPipelines/HDRP/CustomPassController.cs.meta b/Scripts/RenderPipelines/HDRP/CustomPassController.cs.meta new file mode 100644 index 0000000..9da58e3 --- /dev/null +++ b/Scripts/RenderPipelines/HDRP/CustomPassController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a427d9bac985f434aa2d672711da494f \ No newline at end of file diff --git a/Scripts/RenderPipelines/HDRP/DepthMapPrePass.cs b/Scripts/RenderPipelines/HDRP/DepthMapPrePass.cs index db038da..67bfc03 100644 --- a/Scripts/RenderPipelines/HDRP/DepthMapPrePass.cs +++ b/Scripts/RenderPipelines/HDRP/DepthMapPrePass.cs @@ -83,7 +83,7 @@ private void cleanupDepthTextures() { continue; } - CustomPass.GetRTHandleSystem().Release(indivDepthTextures[i]); + Helpers.GetRTHandleSystem().Release(indivDepthTextures[i]); } } @@ -107,7 +107,7 @@ public void recreateDepthTextures(float renderResolutionScale = 1.0f) RenderTextureFormat.Depth ); depthTexture.Create(); - RTHandle handle = CustomPass.GetRTHandleSystem().Alloc(depthTexture); + RTHandle handle = Helpers.GetRTHandleSystem().Alloc(depthTexture); indivDepthTextures[i] = handle; } } diff --git a/Scripts/RenderPipelines/HDRP/FXAA.cs b/Scripts/RenderPipelines/HDRP/FXAA.cs index 737bfe8..f6f9f3d 100644 --- a/Scripts/RenderPipelines/HDRP/FXAA.cs +++ b/Scripts/RenderPipelines/HDRP/FXAA.cs @@ -47,7 +47,7 @@ private void CreateComputeShaderResultTexture() // release old texture if it exists computeShaderResultTextureHandle?.Release(); - computeShaderResultTextureHandle = CustomPass + computeShaderResultTextureHandle = Helpers .GetRTHandleSystem() .Alloc( renderResolution.x, diff --git a/Scripts/RenderPipelines/HDRP/HoleFillingPass.cs b/Scripts/RenderPipelines/HDRP/HoleFillingPass.cs index 3ce2b5b..08575c8 100644 --- a/Scripts/RenderPipelines/HDRP/HoleFillingPass.cs +++ b/Scripts/RenderPipelines/HDRP/HoleFillingPass.cs @@ -58,7 +58,7 @@ private void CreateComputeShaderResultTexture() // release old texture if it exists computeShaderResultTextureHandle?.Release(); - computeShaderResultTextureHandle = CustomPass + computeShaderResultTextureHandle = Helpers .GetRTHandleSystem() .Alloc( renderResolution.x, diff --git a/Scripts/RenderPipelines/HDRP/SMAA.cs b/Scripts/RenderPipelines/HDRP/SMAA.cs index 10e1284..772ca53 100644 --- a/Scripts/RenderPipelines/HDRP/SMAA.cs +++ b/Scripts/RenderPipelines/HDRP/SMAA.cs @@ -50,7 +50,7 @@ private void CreateSMAATextures(int width, int height) { releaseSMAATextures(); - smaaEdgesTexHandle = CustomPass + smaaEdgesTexHandle = Helpers .GetRTHandleSystem() .Alloc( width, @@ -60,7 +60,7 @@ private void CreateSMAATextures(int width, int height) enableRandomWrite: true ); - smaaBlendTexHandle = CustomPass + smaaBlendTexHandle = Helpers .GetRTHandleSystem() .Alloc( width, @@ -70,7 +70,7 @@ private void CreateSMAATextures(int width, int height) enableRandomWrite: true ); - computeShaderResultTextureHandle = CustomPass + computeShaderResultTextureHandle = Helpers .GetRTHandleSystem() .Alloc( width, diff --git a/Scripts/RenderPipelines/Helpers.cs b/Scripts/RenderPipelines/Helpers.cs index d680a92..07b4822 100644 --- a/Scripts/RenderPipelines/Helpers.cs +++ b/Scripts/RenderPipelines/Helpers.cs @@ -15,6 +15,18 @@ internal enum AntialiasingMode internal class Helpers { + private static RTHandleSystem m_RTHandleSystem; + + public static RTHandleSystem GetRTHandleSystem() + { + if (m_RTHandleSystem == null) + { + m_RTHandleSystem = new RTHandleSystem(); + m_RTHandleSystem.Initialize(Screen.width, Screen.height); + } + return m_RTHandleSystem; + } + /// /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. /// If so returns true, otherwise returns false. From aaae4838aa7674e8e9942e5729cc07eb19340e7a Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 18 Mar 2026 15:48:08 +0100 Subject: [PATCH 105/149] fix --- Scripts/RenderPipelines/HDRP/CustomPass.cs | 31 +--------------------- 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/Scripts/RenderPipelines/HDRP/CustomPass.cs b/Scripts/RenderPipelines/HDRP/CustomPass.cs index 21e47e1..3aa5ba7 100644 --- a/Scripts/RenderPipelines/HDRP/CustomPass.cs +++ b/Scripts/RenderPipelines/HDRP/CustomPass.cs @@ -12,7 +12,7 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff protected override void Execute(CustomPassContext ctx) { var camera = ctx.hdCamera.camera; - if (shouldPerformBlit(camera)) + if (Helpers.isMainG3DCamera(camera)) { CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None); CoreUtils.DrawFullScreen( @@ -24,35 +24,6 @@ protected override void Execute(CustomPassContext ctx) } } - /// - /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. - /// If so returns true, otherwise returns false. - /// - /// - /// - static bool shouldPerformBlit(Camera camera) - { - if (camera.cameraType != CameraType.Game) - return false; - bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera); - bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; // only do something if our component is enabled - - bool isMosaicMultiviewCamera = - camera.gameObject.TryGetComponent(out var mosaicCamera); - bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; // same check if it is a mosaic camera - - if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled) - return false; - - // skip all cameras created by the G3D Camera script - if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX)) - { - return false; - } - - return true; - } - protected override void Cleanup() { } } } From f2b11e8c3dd99e66836014da2258509e33e3c0f7 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Wed, 18 Mar 2026 15:51:42 +0100 Subject: [PATCH 106/149] use RTHandles instead of rendertextures; code refactoring --- .../HDRP/CustomPassController.cs | 117 ++++++++++-------- .../HDRP/ViewGenerationMosaicPass.cs | 2 +- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/Scripts/RenderPipelines/HDRP/CustomPassController.cs b/Scripts/RenderPipelines/HDRP/CustomPassController.cs index e42535a..167098b 100644 --- a/Scripts/RenderPipelines/HDRP/CustomPassController.cs +++ b/Scripts/RenderPipelines/HDRP/CustomPassController.cs @@ -25,7 +25,6 @@ public class CustomPassController : MonoBehaviour /// /// Used for view generation mosaic rendering. /// - private RenderTexture mosaicTexture; private RTHandle rtHandleMosaic; public bool cameraCountChanged = false; @@ -118,41 +117,7 @@ private void initCustomPass() if (generateViews) { - // add depth mosaic generation pass - depthMosaicPass = - customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.DepthMaps)) - as G3D.RenderPipeline.HDRP.DepthMaps; - depthMosaicPass.cullingMask = mainCamCullingMask; - depthMosaicPass.cameras = cameras; - depthMosaicPass.internalCameraCount = internalCameraCount; - - // add multiview generation pass - viewGenerationPass = - customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.ViewGeneration)) - as G3D.RenderPipeline.HDRP.ViewGeneration; - viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); - - viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; - viewGenerationPass.materialPassName = "G3DViewGeneration"; - viewGenerationPass.cameras = cameras; - viewGenerationPass.internalCameraCount = internalCameraCount; - - recreateDepthTextures(); - viewGenerationPass.indivDepthMaps = depthMosaicPass.indivDepthTextures; - viewGenerationPass.debugRendering = debugRendering; - - // add autostereo mosaic generation pass - recreateMosaicTexture(); - - if (debugRendering == false) - { - G3D.RenderPipeline.HDRP.ViewGenerationMosaic finalAutostereoGeneration = - customPassVolume.AddPassOfType( - typeof(G3D.RenderPipeline.HDRP.ViewGenerationMosaic) - ) as G3D.RenderPipeline.HDRP.ViewGenerationMosaic; - finalAutostereoGeneration.fullscreenPassMaterial = material; - finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; - } + initViewGeneration(customPassVolume); } else { @@ -164,6 +129,59 @@ private void initCustomPass() } } + private void initViewGeneration(CustomPassVolume customPassVolume) + { + // add depth mosaic generation pass + depthMosaicPass = + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.DepthMaps)) + as G3D.RenderPipeline.HDRP.DepthMaps; + depthMosaicPass.cullingMask = mainCamCullingMask; + depthMosaicPass.cameras = cameras; + depthMosaicPass.internalCameraCount = internalCameraCount; + + // add multiview generation pass + viewGenerationPass = + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.ViewGeneration)) + as G3D.RenderPipeline.HDRP.ViewGeneration; + viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); + + viewGenerationPass.fullscreenPassMaterial = viewGenerationMaterial; + viewGenerationPass.materialPassName = "G3DViewGeneration"; + viewGenerationPass.cameras = cameras; + viewGenerationPass.internalCameraCount = internalCameraCount; + + recreateDepthTextures(); + viewGenerationPass.indivDepthMaps = depthMosaicPass.indivDepthTextures; + viewGenerationPass.debugRendering = debugRendering; + + G3D.RenderPipeline.AntialiasingMode aaMode = getCameraAAMode(); + if (aaMode == G3D.RenderPipeline.AntialiasingMode.SMAA) + { + // add SMAA pass + } + else if (aaMode == G3D.RenderPipeline.AntialiasingMode.FXAA) + { + // add FXAA pass + } + else if (aaMode == G3D.RenderPipeline.AntialiasingMode.TAA) + { + viewGenerationMaterial.EnableKeyword("TAA"); + } + + // add autostereo mosaic generation pass + recreateMosaicTexture(); + + if (debugRendering == false) + { + G3D.RenderPipeline.HDRP.ViewGenerationMosaicPass finalAutostereoGeneration = + customPassVolume.AddPassOfType( + typeof(G3D.RenderPipeline.HDRP.ViewGenerationMosaicPass) + ) as G3D.RenderPipeline.HDRP.ViewGenerationMosaicPass; + finalAutostereoGeneration.fullscreenPassMaterial = material; + finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; + } + } + private void recreateDepthTextures() { depthMosaicPass.recreateDepthTextures(renderResolutionScale); @@ -180,25 +198,16 @@ private void recreateDepthTextures() private void recreateMosaicTexture() { - if (mosaicTexture != null) - { - mosaicTexture.Release(); - } - - mosaicTexture = new RenderTexture( - mainCamera.pixelWidth, - mainCamera.pixelHeight, - 0, - RenderTextureFormat.ARGB32, - RenderTextureReadWrite.sRGB - ); - mosaicTexture.enableRandomWrite = true; + Helpers.GetRTHandleSystem().Release(rtHandleMosaic); + rtHandleMosaic = Helpers + .GetRTHandleSystem() + .Alloc( + mainCamera.pixelWidth, + mainCamera.pixelHeight, + colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_SRGB, + enableRandomWrite: true + ); - if (rtHandleMosaic != null) - { - Helpers.GetRTHandleSystem().Release(rtHandleMosaic); - } - rtHandleMosaic = Helpers.GetRTHandleSystem().Alloc(mosaicTexture); material.SetTexture("_colorMosaic", rtHandleMosaic); viewGenerationPass.mosaicImageHandle = rtHandleMosaic; } diff --git a/Scripts/RenderPipelines/HDRP/ViewGenerationMosaicPass.cs b/Scripts/RenderPipelines/HDRP/ViewGenerationMosaicPass.cs index 59bd55c..0815ced 100644 --- a/Scripts/RenderPipelines/HDRP/ViewGenerationMosaicPass.cs +++ b/Scripts/RenderPipelines/HDRP/ViewGenerationMosaicPass.cs @@ -6,7 +6,7 @@ namespace G3D.RenderPipeline.HDRP { - internal class ViewGenerationMosaic : FullScreenCustomPass + internal class ViewGenerationMosaicPass : FullScreenCustomPass { protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { } From 943decf52c413dec11b96a2bc12d508f9f1d4aa9 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 19 Mar 2026 09:50:40 +0100 Subject: [PATCH 107/149] call smaa and fxaa passes --- .../HDRP/CustomPassController.cs | 49 ++++++++++++++++--- Scripts/RenderPipelines/HDRP/FXAA.cs | 19 ++----- Scripts/RenderPipelines/HDRP/SMAA.cs | 15 +----- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/Scripts/RenderPipelines/HDRP/CustomPassController.cs b/Scripts/RenderPipelines/HDRP/CustomPassController.cs index 167098b..75d5c2c 100644 --- a/Scripts/RenderPipelines/HDRP/CustomPassController.cs +++ b/Scripts/RenderPipelines/HDRP/CustomPassController.cs @@ -19,8 +19,10 @@ public class CustomPassController : MonoBehaviour .AntialiasingMode .None; - private ViewGeneration viewGenerationPass; private DepthMaps depthMosaicPass; + private ViewGeneration viewGenerationPass; + private SMAA smaaPass; + private FXAA fxaaPass; /// /// Used for view generation mosaic rendering. @@ -131,6 +133,9 @@ private void initCustomPass() private void initViewGeneration(CustomPassVolume customPassVolume) { + recreateMosaicTexture(); + recreateDepthTextures(); + // add depth mosaic generation pass depthMosaicPass = customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.DepthMaps)) @@ -139,7 +144,7 @@ private void initViewGeneration(CustomPassVolume customPassVolume) depthMosaicPass.cameras = cameras; depthMosaicPass.internalCameraCount = internalCameraCount; - // add multiview generation pass + // add iview generation pass viewGenerationPass = customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.ViewGeneration)) as G3D.RenderPipeline.HDRP.ViewGeneration; @@ -150,27 +155,34 @@ private void initViewGeneration(CustomPassVolume customPassVolume) viewGenerationPass.cameras = cameras; viewGenerationPass.internalCameraCount = internalCameraCount; - recreateDepthTextures(); viewGenerationPass.indivDepthMaps = depthMosaicPass.indivDepthTextures; viewGenerationPass.debugRendering = debugRendering; + // add antialiasing pass if needed G3D.RenderPipeline.AntialiasingMode aaMode = getCameraAAMode(); if (aaMode == G3D.RenderPipeline.AntialiasingMode.SMAA) { // add SMAA pass + smaaPass = + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.SMAA)) + as G3D.RenderPipeline.HDRP.SMAA; + smaaPass.CreateSMAATextures(mainCamera.pixelWidth, mainCamera.pixelHeight); + smaaPass.mosaicImageHandle = rtHandleMosaic; } else if (aaMode == G3D.RenderPipeline.AntialiasingMode.FXAA) { // add FXAA pass + fxaaPass = + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.FXAA)) + as G3D.RenderPipeline.HDRP.FXAA; + fxaaPass.CreateFXAATextures(mainCamera.pixelWidth, mainCamera.pixelHeight); + fxaaPass.mosaicImageHandle = rtHandleMosaic; } else if (aaMode == G3D.RenderPipeline.AntialiasingMode.TAA) { viewGenerationMaterial.EnableKeyword("TAA"); } - // add autostereo mosaic generation pass - recreateMosaicTexture(); - if (debugRendering == false) { G3D.RenderPipeline.HDRP.ViewGenerationMosaicPass finalAutostereoGeneration = @@ -210,6 +222,15 @@ private void recreateMosaicTexture() material.SetTexture("_colorMosaic", rtHandleMosaic); viewGenerationPass.mosaicImageHandle = rtHandleMosaic; + + if (smaaPass != null) + { + smaaPass.mosaicImageHandle = rtHandleMosaic; + } + if (fxaaPass != null) + { + fxaaPass.mosaicImageHandle = rtHandleMosaic; + } } private bool windowResized() @@ -227,6 +248,22 @@ private void recreateRenderTextures() { recreateDepthTextures(); recreateMosaicTexture(); + + if ( + antialiasingMode + == HDAdditionalCameraData.AntialiasingMode.SubpixelMorphologicalAntiAliasing + ) + { + smaaPass.CreateSMAATextures(mainCamera.pixelWidth, mainCamera.pixelHeight); + } + else if ( + antialiasingMode + == HDAdditionalCameraData.AntialiasingMode.FastApproximateAntialiasing + ) + { + // recreate FXAA render textures + } + // TODO recreate FXAA/ SMAA render textures if needed } private G3D.RenderPipeline.AntialiasingMode getCameraAAMode() diff --git a/Scripts/RenderPipelines/HDRP/FXAA.cs b/Scripts/RenderPipelines/HDRP/FXAA.cs index f6f9f3d..846bcfb 100644 --- a/Scripts/RenderPipelines/HDRP/FXAA.cs +++ b/Scripts/RenderPipelines/HDRP/FXAA.cs @@ -17,8 +17,6 @@ internal class FXAA : FullScreenCustomPass private ComputeShader fxaaCompShader; private int fxaaKernel; - public Vector2Int renderResolution = new Vector2Int(1920, 1080); - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { fxaaCompShader = Resources.Load("G3DFXAA"); @@ -31,18 +29,7 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff ); } - public void updateRenderResolution(Vector2Int resolution) - { - if (renderResolution.x == resolution.x && renderResolution.y == resolution.y) - { - return; - } - - renderResolution = resolution; - CreateComputeShaderResultTexture(); - } - - private void CreateComputeShaderResultTexture() + public void CreateFXAATextures(int width, int height) { // release old texture if it exists computeShaderResultTextureHandle?.Release(); @@ -50,8 +37,8 @@ private void CreateComputeShaderResultTexture() computeShaderResultTextureHandle = Helpers .GetRTHandleSystem() .Alloc( - renderResolution.x, - renderResolution.y, + width, + height, colorFormat: UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_UNorm, enableRandomWrite: true ); diff --git a/Scripts/RenderPipelines/HDRP/SMAA.cs b/Scripts/RenderPipelines/HDRP/SMAA.cs index 772ca53..8b44797 100644 --- a/Scripts/RenderPipelines/HDRP/SMAA.cs +++ b/Scripts/RenderPipelines/HDRP/SMAA.cs @@ -19,8 +19,6 @@ internal class SMAA : FullScreenCustomPass private Material smaaMaterial; - public Vector2Int renderResolution = new Vector2Int(1920, 1080); - protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { smaaMaterial = new Material(Shader.Find("G3D/SMAA")); @@ -35,18 +33,7 @@ protected override void Setup(ScriptableRenderContext renderContext, CommandBuff ); } - public void updateRenderResolution(Vector2Int resolution) - { - if (renderResolution.x == resolution.x && renderResolution.y == resolution.y) - { - return; - } - - renderResolution = resolution; - CreateSMAATextures(renderResolution.x, renderResolution.y); - } - - private void CreateSMAATextures(int width, int height) + public void CreateSMAATextures(int width, int height) { releaseSMAATextures(); From 6b8777d42d383d56d57f0570a2ecc0c6a88f265d Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 19 Mar 2026 10:00:48 +0100 Subject: [PATCH 108/149] fixed smaa init --- .../HDRP/CustomPassController.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Scripts/RenderPipelines/HDRP/CustomPassController.cs b/Scripts/RenderPipelines/HDRP/CustomPassController.cs index 75d5c2c..df9859b 100644 --- a/Scripts/RenderPipelines/HDRP/CustomPassController.cs +++ b/Scripts/RenderPipelines/HDRP/CustomPassController.cs @@ -44,6 +44,8 @@ public class CustomPassController : MonoBehaviour private Vector2Int cachedWindowSize; + private bool initialized = false; + // Start is called before the first frame update public Material init( int internalCameraCount, @@ -58,12 +60,15 @@ public Material init( this.cameras = cameras; this.material = material; this.renderResolutionScale = renderResolutionScale; + viewGenerationMaterial = new Material(Shader.Find("G3D/ViewGeneration")); g3dCamera = GetComponent(); mainCamera = GetComponent(); - initCustomPass(); antialiasingMode = mainCamera.GetComponent().antialiasing; + initCustomPass(); + + initialized = true; return viewGenerationMaterial; } @@ -71,6 +76,10 @@ public Material init( // Update is called once per frame void Update() { + if (!initialized) + { + return; + } if (mainCamera.enabled == false) { mainCamInactiveLastFrame = true; @@ -133,9 +142,6 @@ private void initCustomPass() private void initViewGeneration(CustomPassVolume customPassVolume) { - recreateMosaicTexture(); - recreateDepthTextures(); - // add depth mosaic generation pass depthMosaicPass = customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.DepthMaps)) @@ -192,6 +198,10 @@ private void initViewGeneration(CustomPassVolume customPassVolume) finalAutostereoGeneration.fullscreenPassMaterial = material; finalAutostereoGeneration.materialPassName = "G3DFullScreen3D"; } + + // do these last so that the passes are already created + recreateMosaicTexture(); + recreateDepthTextures(); } private void recreateDepthTextures() From 77b1a7ad2b04d8d9f769d59b0509e6f2558a52f4 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 19 Mar 2026 10:37:23 +0100 Subject: [PATCH 109/149] blit results of smaa/ fxaa --- Scripts/RenderPipelines/HDRP/CustomPassController.cs | 5 ++--- Scripts/RenderPipelines/HDRP/FXAA.cs | 4 ++++ Scripts/RenderPipelines/HDRP/SMAA.cs | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Scripts/RenderPipelines/HDRP/CustomPassController.cs b/Scripts/RenderPipelines/HDRP/CustomPassController.cs index df9859b..cdf942a 100644 --- a/Scripts/RenderPipelines/HDRP/CustomPassController.cs +++ b/Scripts/RenderPipelines/HDRP/CustomPassController.cs @@ -264,16 +264,15 @@ private void recreateRenderTextures() == HDAdditionalCameraData.AntialiasingMode.SubpixelMorphologicalAntiAliasing ) { - smaaPass.CreateSMAATextures(mainCamera.pixelWidth, mainCamera.pixelHeight); + smaaPass?.CreateSMAATextures(mainCamera.pixelWidth, mainCamera.pixelHeight); } else if ( antialiasingMode == HDAdditionalCameraData.AntialiasingMode.FastApproximateAntialiasing ) { - // recreate FXAA render textures + fxaaPass?.CreateFXAATextures(mainCamera.pixelWidth, mainCamera.pixelHeight); } - // TODO recreate FXAA/ SMAA render textures if needed } private G3D.RenderPipeline.AntialiasingMode getCameraAAMode() diff --git a/Scripts/RenderPipelines/HDRP/FXAA.cs b/Scripts/RenderPipelines/HDRP/FXAA.cs index 846bcfb..dac7747 100644 --- a/Scripts/RenderPipelines/HDRP/FXAA.cs +++ b/Scripts/RenderPipelines/HDRP/FXAA.cs @@ -50,6 +50,10 @@ protected override void Execute(CustomPassContext ctx) if (Helpers.isMainG3DCamera(camera)) { runFXAA(ctx); + + // Blit the result of the compute shader to the camera's color buffer + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, shaderPassId: 0); } } diff --git a/Scripts/RenderPipelines/HDRP/SMAA.cs b/Scripts/RenderPipelines/HDRP/SMAA.cs index 8b44797..96dd3b7 100644 --- a/Scripts/RenderPipelines/HDRP/SMAA.cs +++ b/Scripts/RenderPipelines/HDRP/SMAA.cs @@ -81,6 +81,10 @@ protected override void Execute(CustomPassContext ctx) if (Helpers.isMainG3DCamera(camera)) { runSMAA(ctx, hdCamera); + + // Blit the result of the compute shader to the camera's color buffer + CoreUtils.SetRenderTarget(ctx.cmd, mosaicImageHandle, ClearFlag.None); + CoreUtils.DrawFullScreen(ctx.cmd, blitMaterial, shaderPassId: 0); } } From 4dee9a52e458c91517341209883f4a1a298c58a4 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 19 Mar 2026 11:33:13 +0100 Subject: [PATCH 110/149] only moved methods around no changes to the actual code --- G3DCamera.cs | 432 ++++++++++++++++++------------------ G3DCameraMosaicMultiview.cs | 376 +++++++++++++++---------------- 2 files changed, 404 insertions(+), 404 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index cf356a5..2d9b302 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -377,18 +377,6 @@ void Start() } } - private void InitMainCamera() - { - if (mainCamera != null) - { - return; - } - mainCamera = GetComponent(); - mainCamCullingMask = mainCamera.cullingMask; - originalMainFOV = mainCamera.fieldOfView; - originalMainClearFlags = mainCamera.clearFlags; - } - void OnApplicationQuit() { deinitLibrary(); @@ -534,37 +522,6 @@ public void loadShaderParametersFromCalibrationFile() } } - private void loadMultiviewViewSeparationFromCalibration(CalibrationProvider calibration) - { - if (mode != G3DCameraMode.MULTIVIEW) - { - return; - } - - int BasicWorkingDistanceMM = calibration.getInt("BasicWorkingDistanceMM"); - int NativeViewcount = calibration.getInt("NativeViewcount"); - float ApertureAngle = 14.0f; - try - { - ApertureAngle = calibration.getFloat("ApertureAngle"); - } - catch (Exception e) - { - Debug.LogWarning(e.Message); - } - - float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f; - float halfZoneOpeningAngleRad = ApertureAngle * Mathf.Deg2Rad / 2.0f; - float halfWidthZoneAtbasicDistance = - Mathf.Tan(halfZoneOpeningAngleRad) * BasicWorkingDistanceMeter; - - // calculate eye separation/ view separation - if (mode == G3DCameraMode.MULTIVIEW) - { - viewSeparation = halfWidthZoneAtbasicDistance * 2 / NativeViewcount; - } - } - /// /// Updates all camera parameters based on the calibration file (i.e. focus distance, fov, etc.). /// Includes shader parameters (i.e. lense shear angle, camera count, etc.). @@ -675,6 +632,222 @@ public void setupCameras(bool calledFromValidate = false) loadShaderParametersFromCalibrationFile(); } + public void updateShaderRenderTextures() + { + if (material == null) + return; + if (cameras == null) + return; + + //prevent any memory leaks + for (int i = 0; i < MAX_CAMERAS; i++) + cameras[i].targetTexture?.Release(); + + for (int i = 0; i < colorRenderTextures?.Length; i++) + { + if (colorRenderTextures[i] != null) + colorRenderTextures[i].Release(); + } + + colorRenderTextures = new RenderTexture[internalCameraCount]; + + if (generateViews) + { + addRenderTextureToCamera(colorRenderTextures, 0, 0, "_leftCamTex"); // left camera + addRenderTextureToCamera( + colorRenderTextures, + 1, + internalCameraCount / 2, + "_middleCamTex" + ); // middle camera + addRenderTextureToCamera( + colorRenderTextures, + 2, + internalCameraCount - 1, + "_rightCamTex" + ); // right camera + } + else + { + //set only those we need + for (int i = 0; i < internalCameraCount; i++) + { + addRenderTextureToCamera(colorRenderTextures, i, i); + } + } + } + + /// + /// always use this method to get the current head position. + /// NEVER access headPosition directly, as it is updated in a different thread. + /// + /// + /// + public HeadPosition getHeadPosition() + { + lock (headPosLock) + { + return headPosition; + } + } + + /// + /// always use this method to get the smoothed head position. + /// NEVER access headPosition directly, as it is updated in a different thread. + /// + /// + /// + public HeadPosition getFilteredHeadPosition() + { + lock (headPosLock) + { + return filteredHeadPosition; + } + } + + public void logCameraPositionsToFile() + { + System.IO.StreamWriter writer = new System.IO.StreamWriter( + Application.dataPath + "/HeadPositionLog.csv", + false + ); + writer.WriteLine( + "Camera update time; Camera X; Camera Y; Camera Z; Head detected; Image position valid; Filtered X; Filtered Y; Filtered Z" + ); + string[] headPoitionLogArray = headPositionLog.ToArray(); + for (int i = 0; i < headPoitionLogArray.Length; i++) + { + writer.WriteLine(headPoitionLogArray[i]); + } + writer.Close(); + } + + public void shiftViewToLeft() + { + if (mode == G3DCameraMode.MULTIVIEW) + { + return; + } + try + { + libInterface.shiftViewToLeft(); + } + catch (Exception e) + { + Debug.LogError("Failed to shift view to left: " + e.Message); + } + } + + public void shiftViewToRight() + { + if (mode == G3DCameraMode.MULTIVIEW) + { + return; + } + try + { + libInterface.shiftViewToRight(); + } + catch (Exception e) + { + Debug.LogError("Failed to shift view to right: " + e.Message); + } + } + + public void toggleTestFrame() + { + showTestFrame = !showTestFrame; + } + + public void toggleHeadTracking() + { + if (mode == G3DCameraMode.MULTIVIEW) + { + return; + } + Debug.Log("Toggling head tracking status"); + if (libInterface == null || !libInterface.isInitialized()) + { + return; + } + + try + { + HeadTrackingStatus headtrackingHandler = libInterface.getHeadTrackingStatus(); + if (headtrackingHandler.hasTrackingDevice) + { + if (!headtrackingHandler.isTrackingActive) + { + libInterface.startHeadTracking(); + } + else + { + libInterface.stopHeadTracking(); + } + } + } + catch (Exception e) + { + Debug.LogError("Failed to toggle head tracking status: " + e.Message); + } + } + + public G3DShaderParameters GetShaderParameters() + { + lock (shaderLock) + { + return shaderParameters; + } + } + + public void setOriginalFOV(float fov) + { + originalMainFOV = fov; + } + + private void InitMainCamera() + { + if (mainCamera != null) + { + return; + } + mainCamera = GetComponent(); + mainCamCullingMask = mainCamera.cullingMask; + originalMainFOV = mainCamera.fieldOfView; + originalMainClearFlags = mainCamera.clearFlags; + } + + private void loadMultiviewViewSeparationFromCalibration(CalibrationProvider calibration) + { + if (mode != G3DCameraMode.MULTIVIEW) + { + return; + } + + int BasicWorkingDistanceMM = calibration.getInt("BasicWorkingDistanceMM"); + int NativeViewcount = calibration.getInt("NativeViewcount"); + float ApertureAngle = 14.0f; + try + { + ApertureAngle = calibration.getFloat("ApertureAngle"); + } + catch (Exception e) + { + Debug.LogWarning(e.Message); + } + + float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f; + float halfZoneOpeningAngleRad = ApertureAngle * Mathf.Deg2Rad / 2.0f; + float halfWidthZoneAtbasicDistance = + Mathf.Tan(halfZoneOpeningAngleRad) * BasicWorkingDistanceMeter; + + // calculate eye separation/ view separation + if (mode == G3DCameraMode.MULTIVIEW) + { + viewSeparation = halfWidthZoneAtbasicDistance * 2 / NativeViewcount; + } + } + /// /// Initializes the cameras and their parents. /// if cameras are already initialized, this function only returns the focus plane. @@ -1302,51 +1475,6 @@ private void addRenderTextureToCamera( } } - public void updateShaderRenderTextures() - { - if (material == null) - return; - if (cameras == null) - return; - - //prevent any memory leaks - for (int i = 0; i < MAX_CAMERAS; i++) - cameras[i].targetTexture?.Release(); - - for (int i = 0; i < colorRenderTextures?.Length; i++) - { - if (colorRenderTextures[i] != null) - colorRenderTextures[i].Release(); - } - - colorRenderTextures = new RenderTexture[internalCameraCount]; - - if (generateViews) - { - addRenderTextureToCamera(colorRenderTextures, 0, 0, "_leftCamTex"); // left camera - addRenderTextureToCamera( - colorRenderTextures, - 1, - internalCameraCount / 2, - "_middleCamTex" - ); // middle camera - addRenderTextureToCamera( - colorRenderTextures, - 2, - internalCameraCount - 1, - "_rightCamTex" - ); // right camera - } - else - { - //set only those we need - for (int i = 0; i < internalCameraCount; i++) - { - addRenderTextureToCamera(colorRenderTextures, i, i); - } - } - } - private bool windowResized() { var window_dim = new Vector2Int(Screen.width, Screen.height); @@ -1416,34 +1544,6 @@ void OnRenderImage(RenderTexture source, RenderTexture destination) } #endregion - /// - /// always use this method to get the current head position. - /// NEVER access headPosition directly, as it is updated in a different thread. - /// - /// - /// - public HeadPosition getHeadPosition() - { - lock (headPosLock) - { - return headPosition; - } - } - - /// - /// always use this method to get the smoothed head position. - /// NEVER access headPosition directly, as it is updated in a different thread. - /// - /// - /// - public HeadPosition getFilteredHeadPosition() - { - lock (headPosLock) - { - return filteredHeadPosition; - } - } - #region callback handling void ITNewHeadPositionCallback.NewHeadPositionCallback( bool headDetected, @@ -1753,104 +1853,4 @@ private bool usePositionFiltering() { return headPositionFilter.x != 0 || headPositionFilter.y != 0 || headPositionFilter.z != 0; } - - public void logCameraPositionsToFile() - { - System.IO.StreamWriter writer = new System.IO.StreamWriter( - Application.dataPath + "/HeadPositionLog.csv", - false - ); - writer.WriteLine( - "Camera update time; Camera X; Camera Y; Camera Z; Head detected; Image position valid; Filtered X; Filtered Y; Filtered Z" - ); - string[] headPoitionLogArray = headPositionLog.ToArray(); - for (int i = 0; i < headPoitionLogArray.Length; i++) - { - writer.WriteLine(headPoitionLogArray[i]); - } - writer.Close(); - } - - public void shiftViewToLeft() - { - if (mode == G3DCameraMode.MULTIVIEW) - { - return; - } - try - { - libInterface.shiftViewToLeft(); - } - catch (Exception e) - { - Debug.LogError("Failed to shift view to left: " + e.Message); - } - } - - public void shiftViewToRight() - { - if (mode == G3DCameraMode.MULTIVIEW) - { - return; - } - try - { - libInterface.shiftViewToRight(); - } - catch (Exception e) - { - Debug.LogError("Failed to shift view to right: " + e.Message); - } - } - - public void toggleTestFrame() - { - showTestFrame = !showTestFrame; - } - - public void toggleHeadTracking() - { - if (mode == G3DCameraMode.MULTIVIEW) - { - return; - } - Debug.Log("Toggling head tracking status"); - if (libInterface == null || !libInterface.isInitialized()) - { - return; - } - - try - { - HeadTrackingStatus headtrackingHandler = libInterface.getHeadTrackingStatus(); - if (headtrackingHandler.hasTrackingDevice) - { - if (!headtrackingHandler.isTrackingActive) - { - libInterface.startHeadTracking(); - } - else - { - libInterface.stopHeadTracking(); - } - } - } - catch (Exception e) - { - Debug.LogError("Failed to toggle head tracking status: " + e.Message); - } - } - - public G3DShaderParameters GetShaderParameters() - { - lock (shaderLock) - { - return shaderParameters; - } - } - - public void setOriginalFOV(float fov) - { - originalMainFOV = fov; - } } diff --git a/G3DCameraMosaicMultiview.cs b/G3DCameraMosaicMultiview.cs index f1044dc..956c649 100644 --- a/G3DCameraMosaicMultiview.cs +++ b/G3DCameraMosaicMultiview.cs @@ -175,6 +175,194 @@ void Start() updateIndexMap(); } +#if G3D_URP + private void OnEnable() + { + RenderPipelineManager.beginCameraRendering += OnBeginCamera; + } + + private void OnDisable() + { + RenderPipelineManager.beginCameraRendering -= OnBeginCamera; + } + + private void OnBeginCamera(ScriptableRenderContext context, Camera cam) + { + // Use the EnqueuePass method to inject a custom render pass + cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass); + } +#endif + + #endregion + + #region Updates + + /// + /// OnValidate gets called every time the script is changed in the editor. + /// This is used to react to changes made to the parameters. + /// + void OnValidate() + { + if (isActiveAndEnabled == false) + { + // do not run this code if the script is not enabled + return; + } + + if (calibrationFile != previousValues.calibrationFile) + { + previousValues.calibrationFile = calibrationFile; + updateShaderFromCalibrationFile(); + } + + if ( + previousValues.indexMapYoyoStart != indexMapYoyoStart + || previousValues.invertIndexMap != invertIndexMap + || previousValues.invertIndexMapIndices != invertIndexMapIndices + ) + { + previousValues.indexMapYoyoStart = indexMapYoyoStart; + previousValues.invertIndexMap = invertIndexMap; + previousValues.invertIndexMapIndices = invertIndexMapIndices; + + indexMap.UpdateIndexMap( + shaderParameters.nativeViewCount, + mosaicColumnCount * mosaicRowCount, + indexMapYoyoStart, + invertIndexMap, + invertIndexMapIndices + ); + } + } + + void Update() + { + updateShaderParameters(); + + if (windowResized() || windowMoved()) + { + updateScreenViewportProperties(); + } + } + + public void reinitializeShader() + { + material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); + setCorrectMosaicTexture(); + + updateScreenViewportProperties(); + updateShaderParameters(); + +#if G3D_HDRP + customPass.fullscreenPassMaterial = material; +#endif +#if G3D_URP + customPass.updateMaterial(material); +#endif + } + + public void updateShaderFromCalibrationFile(TextAsset calibrationFile) + { + if (calibrationFile == null || calibrationFile.text == "") + { + return; + } + this.calibrationFile = calibrationFile; + updateShaderFromCalibrationFile(); + } + + public void updateShaderFromCalibrationFile() + { + if (calibrationFile == null || calibrationFile.text == "") + { + return; + } + + CalibrationProvider calibrationProvider = CalibrationProvider.getFromString( + calibrationFile.text + ); + shaderParameters = calibrationProvider.getShaderParameters(); + } + + /// + /// The provided file uri has to be a display calibration ini file. + /// + /// + public void UpdateShaderParametersFromURI(string uri) + { + if (uri == null || uri == "") + { + return; + } + + try + { + CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromURI( + uri, + (CalibrationProvider provider) => + { + shaderParameters = provider.getShaderParameters(); + updateShaderParameters(); + return 0; + } + ); + } + catch (Exception e) + { + Debug.LogError("Failed to update shader parameters from uri: " + e.Message); + } + } + + /// + /// The provided file path has to be a display calibration ini file. + /// + /// + public void UpdateShaderParametersFromFile(string filePath) + { + if (filePath == null || filePath == "" || filePath.EndsWith(".ini") == false) + { + return; + } + + try + { + CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromConfigFile( + filePath + ); + shaderParameters = defaultCalibrationProvider.getShaderParameters(); + updateShaderParameters(); + } + catch (Exception e) + { + Debug.LogError("Failed to update shader parameters from file: " + e.Message); + } + } + + /// + /// The provided string has to be a display calibration ini file. + /// + /// + public void UpdateShaderParametersFromINIString(string iniFile) + { + if (iniFile == null || iniFile == "") + { + return; + } + + try + { + CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromString( + iniFile + ); + shaderParameters = defaultCalibrationProvider.getShaderParameters(); + updateShaderParameters(); + } + catch (Exception e) + { + Debug.LogError("Failed to update shader parameters from json: " + e.Message); + } + } + private void updateIndexMap() { int availableViews = shaderParameters.nativeViewCount; @@ -297,115 +485,6 @@ private void setCorrectMosaicTexture() } } - public void reinitializeShader() - { - material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); - setCorrectMosaicTexture(); - - updateScreenViewportProperties(); - updateShaderParameters(); - -#if G3D_HDRP - customPass.fullscreenPassMaterial = material; -#endif -#if G3D_URP - customPass.updateMaterial(material); -#endif - } - -#if G3D_URP - private void OnEnable() - { - RenderPipelineManager.beginCameraRendering += OnBeginCamera; - } - - private void OnDisable() - { - RenderPipelineManager.beginCameraRendering -= OnBeginCamera; - } - - private void OnBeginCamera(ScriptableRenderContext context, Camera cam) - { - // Use the EnqueuePass method to inject a custom render pass - cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass); - } -#endif - - #endregion - - #region Updates - - /// - /// OnValidate gets called every time the script is changed in the editor. - /// This is used to react to changes made to the parameters. - /// - void OnValidate() - { - if (isActiveAndEnabled == false) - { - // do not run this code if the script is not enabled - return; - } - - if (calibrationFile != previousValues.calibrationFile) - { - previousValues.calibrationFile = calibrationFile; - updateShaderFromCalibrationFile(); - } - - if ( - previousValues.indexMapYoyoStart != indexMapYoyoStart - || previousValues.invertIndexMap != invertIndexMap - || previousValues.invertIndexMapIndices != invertIndexMapIndices - ) - { - previousValues.indexMapYoyoStart = indexMapYoyoStart; - previousValues.invertIndexMap = invertIndexMap; - previousValues.invertIndexMapIndices = invertIndexMapIndices; - - indexMap.UpdateIndexMap( - shaderParameters.nativeViewCount, - mosaicColumnCount * mosaicRowCount, - indexMapYoyoStart, - invertIndexMap, - invertIndexMapIndices - ); - } - } - - public void updateShaderFromCalibrationFile(TextAsset calibrationFile) - { - if (calibrationFile == null || calibrationFile.text == "") - { - return; - } - this.calibrationFile = calibrationFile; - updateShaderFromCalibrationFile(); - } - - public void updateShaderFromCalibrationFile() - { - if (calibrationFile == null || calibrationFile.text == "") - { - return; - } - - CalibrationProvider calibrationProvider = CalibrationProvider.getFromString( - calibrationFile.text - ); - shaderParameters = calibrationProvider.getShaderParameters(); - } - - void Update() - { - updateShaderParameters(); - - if (windowResized() || windowMoved()) - { - updateScreenViewportProperties(); - } - } - private void updateScreenViewportProperties() { try @@ -507,83 +586,4 @@ void OnRenderImage(RenderTexture source, RenderTexture destination) #endif } #endregion - - /// - /// The provided file uri has to be a display calibration ini file. - /// - /// - public void UpdateShaderParametersFromURI(string uri) - { - if (uri == null || uri == "") - { - return; - } - - try - { - CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromURI( - uri, - (CalibrationProvider provider) => - { - shaderParameters = provider.getShaderParameters(); - updateShaderParameters(); - return 0; - } - ); - } - catch (Exception e) - { - Debug.LogError("Failed to update shader parameters from uri: " + e.Message); - } - } - - /// - /// The provided file path has to be a display calibration ini file. - /// - /// - public void UpdateShaderParametersFromFile(string filePath) - { - if (filePath == null || filePath == "" || filePath.EndsWith(".ini") == false) - { - return; - } - - try - { - CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromConfigFile( - filePath - ); - shaderParameters = defaultCalibrationProvider.getShaderParameters(); - updateShaderParameters(); - } - catch (Exception e) - { - Debug.LogError("Failed to update shader parameters from file: " + e.Message); - } - } - - /// - /// The provided string has to be a display calibration ini file. - /// - /// - public void UpdateShaderParametersFromINIString(string iniFile) - { - if (iniFile == null || iniFile == "") - { - return; - } - - try - { - CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromString( - iniFile - ); - shaderParameters = defaultCalibrationProvider.getShaderParameters(); - updateShaderParameters(); - } - catch (Exception e) - { - Debug.LogError("Failed to update shader parameters from json: " + e.Message); - } - } } From 4b44cc71cc9fce25ccf06cbc21dd12ea268b2cbb Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 19 Mar 2026 11:37:52 +0100 Subject: [PATCH 111/149] removed vector map generation code in g3d Camera --- G3DCamera.cs | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 2d9b302..0abb666 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -55,8 +55,6 @@ public class G3DCamera [Tooltip("Only actually renders three views. The rest are generated. IF turned on anti ali")] public bool generateViews = true; - - private bool useVectorMapViewGeneration = false; #endregion @@ -350,31 +348,6 @@ void Start() invertIndexMap, invertIndexMapIndices ); - - if (useVectorMapViewGeneration) - { - int invert = shaderParameters.leftLensOrientation == 1 ? 1 : -1; - Texture2D viewMap = ViewmapGeneratorInterface.getViewMap( - (uint)shaderParameters.screenWidth, // PixelCountX - (uint)shaderParameters.screenHeight, // PixelCountY - (uint)shaderParameters.nativeViewCount, // ViewCount - (uint)shaderParameters.angleRatioDenominator, // LensWidth - invert * shaderParameters.angleRatioNumerator, // LensAngleCounter - false, // ViewOrderInverted - false, // Rotated - false, // FullPixel - shaderParameters.BGRPixelLayout != 0 // BGRMode - ); - viewMap.Apply(); - material?.SetTexture("_viewMap", viewMap); - - float[] indexMap = new float[shaderParameters.nativeViewCount]; - for (int i = 0; i < shaderParameters.nativeViewCount; i++) - { - indexMap[i] = i; - } - material?.SetFloatArray("indexMap", indexMap); - } } void OnApplicationQuit() @@ -1038,11 +1011,7 @@ private void reinitializeShader() { if (mode == G3DCameraMode.MULTIVIEW) { - if (useVectorMapViewGeneration && generateViews) - { - material = new Material(Shader.Find("G3D/MultiviewMosaicVector")); - } - else if (!useVectorMapViewGeneration && generateViews) + if (generateViews) { material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); } From f032677d37610272aefcc5118230f737f12c87ae Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 19 Mar 2026 14:10:16 +0100 Subject: [PATCH 112/149] moved headtracking handling to own class --- G3DCamera.cs | 543 ++----------- G3DCameraMosaicMultiview.cs | 16 +- Scripts/HeadTracking/HeadTrackingPretender.cs | 2 +- Scripts/HeadtrackingConnection.cs | 732 ++++++++++++++++++ Scripts/HeadtrackingConnection.cs.meta | 2 + Scripts/HeadtrackingHandler.cs | 277 ------- Scripts/HeadtrackingHandler.cs.meta | 2 - Scripts/StuctsAndEnums.cs | 30 + 8 files changed, 837 insertions(+), 767 deletions(-) create mode 100644 Scripts/HeadtrackingConnection.cs create mode 100644 Scripts/HeadtrackingConnection.cs.meta delete mode 100644 Scripts/HeadtrackingHandler.cs delete mode 100644 Scripts/HeadtrackingHandler.cs.meta diff --git a/G3DCamera.cs b/G3DCamera.cs index 0abb666..21cc5be 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -17,11 +17,7 @@ /// [RequireComponent(typeof(Camera))] [DisallowMultipleComponent] -public class G3DCamera - : MonoBehaviour, - ITNewHeadPositionCallback, - ITNewShaderParametersCallback, - ITNewErrorMessageCallback +public class G3DCamera : MonoBehaviour { [Tooltip("Drop the calibration file for the display you want to use here.")] public TextAsset calibrationFile; @@ -85,7 +81,7 @@ public class G3DCamera #region Advanced settings [Tooltip( - "Smoothes the head position (Size of the filter kernel). Not filtering is applied, if set to all zeros. DO NOT CHANGE THIS WHILE GAME IS ALREADY RUNNING!" + "Smoothes the head position (Size of the filter kernel). No filtering is applied, if set to all zeros. DO NOT CHANGE THIS WHILE GAME IS ALREADY RUNNING!" )] public Vector3Int headPositionFilter = new Vector3Int(5, 5, 5); public LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE; @@ -121,21 +117,21 @@ public class G3DCamera [Tooltip("Inverts the indices in the index map. Index map contains the order of views.")] public bool invertIndexMapIndices = false; + public HeadtrackingConnection headtrackingConnection; + #endregion #region Private variables private PreviousValues previousValues; private IndexMap indexMap = IndexMap.Instance; - private LibInterface libInterface; - - private string calibrationPath; // distance between the two cameras for diorama mode (in meters). DO NOT USE FOR MULTIVIEW MODE! private float viewSeparation = 0.065f; /// /// The distance between the camera and the focus plane in meters. Default is 70 cm. - /// Is read from calibration file at startup + /// Is read from calibration file at startup. + /// DO NOT SET THIS PARAMETER DIRECTLY. Use the SetFocusDistance function instead. /// private float focusDistance = 0.7f; @@ -143,15 +139,6 @@ public class G3DCamera private int internalCameraCount = 2; private int oldRenderResolutionScale = 100; - /// - /// This struct is used to store the current head position. - /// It is updated in a different thread, so always use getHeadPosition() to get the current head position. - /// NEVER use headPosition directly. - /// - private HeadPosition headPosition; - private HeadPosition filteredHeadPosition; - - private static object headPosLock = new object(); private static object shaderLock = new object(); private Camera mainCamera; @@ -179,10 +166,6 @@ public class G3DCamera // half of the width of field of view at start at focus distance private float halfCameraWidthAtStart = 1.0f; - private Queue headPositionLog; - - private HeadtrackingHandler headtrackingHandler; - /// /// This value is calculated based on the calibration file /// @@ -231,7 +214,7 @@ private float scaledHalfCameraWidthAtStart private G3D.RenderPipeline.HDRP.CustomPassController customPassController; #endif - // TODO Handle viewport resizing/ moving + private bool mainCamInactiveLastFrame = false; #region Initialization @@ -244,15 +227,6 @@ void Start() { previousValues.init(); - calibrationPath = System.Environment.GetFolderPath( - Environment.SpecialFolder.CommonDocuments - ); - calibrationPath = Path.Combine(calibrationPath, "3D Global", "calibrations"); - if (!string.IsNullOrEmpty(calibrationPathOverwrite)) - { - calibrationPath = calibrationPathOverwrite; - } - oldRenderResolutionScale = renderResolutionScale; setupCameras(); @@ -276,59 +250,30 @@ void Start() mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None; #endif - shaderHandles = new ShaderHandles() - { - leftViewportPosition = Shader.PropertyToID("viewport_pos_x"), - bottomViewportPosition = Shader.PropertyToID("viewport_pos_y"), - screenWidth = Shader.PropertyToID("screen_width"), - screenHeight = Shader.PropertyToID("screen_height"), - nativeViewCount = Shader.PropertyToID("nativeViewCount"), - angleRatioNumerator = Shader.PropertyToID("zwinkel"), - angleRatioDenominator = Shader.PropertyToID("nwinkel"), - leftLensOrientation = Shader.PropertyToID("isleft"), - BGRPixelLayout = Shader.PropertyToID("isBGR"), - mstart = Shader.PropertyToID("mstart"), - showTestFrame = Shader.PropertyToID("test"), - showTestStripe = Shader.PropertyToID("stest"), - testGapWidth = Shader.PropertyToID("testgap"), - track = Shader.PropertyToID("track"), - hqViewCount = Shader.PropertyToID("hqview"), - hviews1 = Shader.PropertyToID("hviews1"), - hviews2 = Shader.PropertyToID("hviews2"), - blur = Shader.PropertyToID("blur"), - blackBorder = Shader.PropertyToID("bborder"), - blackSpace = Shader.PropertyToID("bspace"), - bls = Shader.PropertyToID("bls"), - ble = Shader.PropertyToID("ble"), - brs = Shader.PropertyToID("brs"), - bre = Shader.PropertyToID("bre"), - zCorrectionValue = Shader.PropertyToID("tvx"), - zCompensationValue = Shader.PropertyToID("zkom"), - }; - + shaderHandles = new ShaderHandles(); + shaderHandles.init(); + + headtrackingConnection = new HeadtrackingConnection( + focusDistance, + headTrackingScale, + sceneScaleFactor, + calibrationPathOverwrite, + this, + debugMessages, + headPositionFilter, + latencyCorrectionMode + ); if (mode == G3DCameraMode.DIORAMA) { - initLibrary(); + headtrackingConnection.initLibrary(); + headtrackingConnection.startHeadTracking(); } + updateScreenViewportProperties(); loadShaderParametersFromCalibrationFile(); updateShaderParameters(); - if (mode == G3DCameraMode.DIORAMA) - { - try - { - libInterface.startHeadTracking(); - } - catch (Exception e) - { - Debug.LogError("Failed to start head tracking: " + e.Message); - } - } - - headtrackingHandler = new HeadtrackingHandler(focusDistance); - updateCameras(); updateShaderRenderTextures(); @@ -339,8 +284,6 @@ void Start() ); cachedWindowSize = new Vector2Int(Screen.width, Screen.height); - headPositionLog = new Queue(10000); - indexMap.UpdateIndexMap( getCameraCountFromCalibrationFile(), internalCameraCount, @@ -352,7 +295,7 @@ void Start() void OnApplicationQuit() { - deinitLibrary(); + headtrackingConnection.deinitLibrary(); } private void OnEnable() @@ -415,6 +358,7 @@ void OnValidate() return; } + // reset cameras if calibration file or scene scale factor changed if ( calibrationFile != previousValues.calibrationFile || previousValues.sceneScaleFactor != sceneScaleFactor @@ -424,6 +368,22 @@ void OnValidate() setupCameras(true); } + // update cached calibration file if it changed + if (calibrationFile != previousValues.calibrationFile) + { + previousValues.calibrationFile = calibrationFile; + } + + // update cached scene scale factor if it changed + if (previousValues.sceneScaleFactor != sceneScaleFactor) + { + if (headtrackingConnection != null) + { + headtrackingConnection.sceneScaleFactor = sceneScaleFactor; + } + previousValues.sceneScaleFactor = sceneScaleFactor; + } + if (previousValues.mode != mode) { previousValues.mode = mode; @@ -530,7 +490,7 @@ public void setupCameras(bool calledFromValidate = false) "No calibration file set. Please set a calibration file. Using default values." ); mainCamera.fieldOfView = 16.0f; - focusDistance = 0.7f; + SetFocusDistance(0.7f); viewSeparation = 0.065f; if (mode == G3DCameraMode.MULTIVIEW) { @@ -559,7 +519,7 @@ public void setupCameras(bool calledFromValidate = false) 2 * Mathf.Atan(physicalSizeInMeter / 2.0f / BasicWorkingDistanceMeter) * Mathf.Rad2Deg; // set focus distance - focusDistance = (float)BasicWorkingDistanceMeter; + SetFocusDistance((float)BasicWorkingDistanceMeter); // set camera fov baseFieldOfView = Camera.HorizontalToVerticalFieldOfView(FOV, aspectRatio); @@ -650,49 +610,9 @@ public void updateShaderRenderTextures() } } - /// - /// always use this method to get the current head position. - /// NEVER access headPosition directly, as it is updated in a different thread. - /// - /// - /// - public HeadPosition getHeadPosition() - { - lock (headPosLock) - { - return headPosition; - } - } - - /// - /// always use this method to get the smoothed head position. - /// NEVER access headPosition directly, as it is updated in a different thread. - /// - /// - /// - public HeadPosition getFilteredHeadPosition() - { - lock (headPosLock) - { - return filteredHeadPosition; - } - } - public void logCameraPositionsToFile() { - System.IO.StreamWriter writer = new System.IO.StreamWriter( - Application.dataPath + "/HeadPositionLog.csv", - false - ); - writer.WriteLine( - "Camera update time; Camera X; Camera Y; Camera Z; Head detected; Image position valid; Filtered X; Filtered Y; Filtered Z" - ); - string[] headPoitionLogArray = headPositionLog.ToArray(); - for (int i = 0; i < headPoitionLogArray.Length; i++) - { - writer.WriteLine(headPoitionLogArray[i]); - } - writer.Close(); + headtrackingConnection.logCameraPositionsToFile(); } public void shiftViewToLeft() @@ -701,14 +621,7 @@ public void shiftViewToLeft() { return; } - try - { - libInterface.shiftViewToLeft(); - } - catch (Exception e) - { - Debug.LogError("Failed to shift view to left: " + e.Message); - } + headtrackingConnection.shiftViewToLeft(); } public void shiftViewToRight() @@ -717,14 +630,7 @@ public void shiftViewToRight() { return; } - try - { - libInterface.shiftViewToRight(); - } - catch (Exception e) - { - Debug.LogError("Failed to shift view to right: " + e.Message); - } + headtrackingConnection.shiftViewToRight(); } public void toggleTestFrame() @@ -738,38 +644,22 @@ public void toggleHeadTracking() { return; } - Debug.Log("Toggling head tracking status"); - if (libInterface == null || !libInterface.isInitialized()) - { - return; - } + headtrackingConnection.toggleHeadTracking(); + } - try - { - HeadTrackingStatus headtrackingHandler = libInterface.getHeadTrackingStatus(); - if (headtrackingHandler.hasTrackingDevice) - { - if (!headtrackingHandler.isTrackingActive) - { - libInterface.startHeadTracking(); - } - else - { - libInterface.stopHeadTracking(); - } - } - } - catch (Exception e) + public G3DShaderParameters GetShaderParameters() + { + lock (shaderLock) { - Debug.LogError("Failed to toggle head tracking status: " + e.Message); + return shaderParameters; } } - public G3DShaderParameters GetShaderParameters() + public void setShaderParameters(G3DShaderParameters parameters) { lock (shaderLock) { - return shaderParameters; + shaderParameters = parameters; } } @@ -778,6 +668,15 @@ public void setOriginalFOV(float fov) originalMainFOV = fov; } + private void SetFocusDistance(float distance) + { + focusDistance = distance; + if (headtrackingConnection != null) + { + headtrackingConnection.focusDistance = distance; + } + } + private void InitMainCamera() { if (mainCamera != null) @@ -907,106 +806,6 @@ private Vector2Int getDisplayResolutionFromCalibrationFile() return new Vector2Int(HorizontalResolution, VerticalResolution); } - private void initLibrary() - { - string applicationName = Application.productName; - if (string.IsNullOrEmpty(applicationName)) - { - applicationName = "Unity"; - } - var invalids = System.IO.Path.GetInvalidFileNameChars(); - applicationName = String - .Join("_", applicationName.Split(invalids, StringSplitOptions.RemoveEmptyEntries)) - .TrimEnd('.'); - applicationName = applicationName + "_G3D_Config.ini"; - - try - { - bool useHimaxD2XXDevices = true; - bool useHimaxRP2040Devices = true; - bool usePmdFlexxDevices = true; - - libInterface = LibInterface.Instance; - libInterface.init( - calibrationPath, - Application.persistentDataPath, - applicationName, - this, - this, - this, - debugMessages, - useHimaxD2XXDevices, - useHimaxRP2040Devices, - usePmdFlexxDevices - ); - } - catch (Exception e) - { - Debug.LogError("Failed to initialize library: " + e.Message); - return; - } - - // set initial values - // intialize head position at focus distance from focus plane - headPosition = new HeadPosition - { - headDetected = false, - imagePosIsValid = false, - imagePosX = 0, - imagePosY = 0, - worldPosX = 0.0, - worldPosY = 0.0, - worldPosZ = -focusDistance - }; - filteredHeadPosition = new HeadPosition - { - headDetected = false, - imagePosIsValid = false, - imagePosX = 0, - imagePosY = 0, - worldPosX = 0.0, - worldPosY = 0.0, - worldPosZ = -focusDistance - }; - - if (usePositionFiltering()) - { - try - { - libInterface.initializePositionFilter( - headPositionFilter.x, - headPositionFilter.y, - headPositionFilter.z - ); - } - catch (Exception e) - { - Debug.LogError("Failed to initialize position filter: " + e.Message); - } - } - } - - private void deinitLibrary() - { - if (libInterface == null || !libInterface.isInitialized()) - { - return; - } - - try - { - libInterface.stopHeadTracking(); - libInterface.unregisterHeadPositionChangedCallback(this); - libInterface.unregisterShaderParametersChangedCallback(this); - libInterface.unregisterMessageCallback(this); - libInterface.deinit(); - } - catch (Exception e) - { - Debug.Log(e); - } - } - private void reinitializeShader() { if (mode == G3DCameraMode.MULTIVIEW) @@ -1028,14 +827,10 @@ private void reinitializeShader() #endregion #region Updates - private bool mainCamInactiveLastFrame = false; - private bool windowMovedLastFrame = false; - private bool windowResizedLastFrame = false; - void Update() { - windowMovedLastFrame = windowMoved(); - windowResizedLastFrame = windowResized(); + bool windowMovedLastFrame = windowMoved(); + bool windowResizedLastFrame = windowResized(); if (mainCamera.enabled == false) { @@ -1061,11 +856,7 @@ void Update() // update the shader parameters (only in diorama mode) if (mode == G3DCameraMode.DIORAMA) { - libInterface.calculateShaderParameters(latencyCorrectionMode); - lock (shaderLock) - { - shaderParameters = libInterface.getCurrentShaderParameters(); - } + headtrackingConnection.calculateShaderParameters(); } bool cameraCountChanged = updateCameraCountBasedOnMode(); @@ -1111,26 +902,7 @@ private void updateScreenViewportProperties() } else { - try - { - // This is the size of the entire monitor screen - libInterface.setScreenSize(displayResolution.x, displayResolution.y); - - // this refers to the window in which the 3D effect is rendered (including eg windows top window menu) - libInterface.setWindowSize(Screen.width, Screen.height); - libInterface.setWindowPosition( - Screen.mainWindowPosition.x, - Screen.mainWindowPosition.y - ); - - // This refers to the actual viewport in which the 3D effect is rendered - libInterface.setViewportSize(Screen.width, Screen.height); - libInterface.setViewportOffset(0, 0); - } - catch (Exception e) - { - Debug.LogError("Failed to update screen viewport properties: " + e.Message); - } + headtrackingConnection.updateScreenViewportProperties(displayResolution); } // this parameter is used in the shader to invert the y axis @@ -1223,7 +995,7 @@ private void updateShaderParameters() } } - void updateCameras() + private void updateCameras() { Vector3 targetPosition = new Vector3(0, 0, -scaledFocusDistance); // position for the camera center (base position from which all other cameras are offset) float targetViewSeparation = 0.0f; @@ -1231,23 +1003,11 @@ void updateCameras() // calculate the camera center position and eye separation if head tracking and the diorama effect are enabled if (mode == G3DCameraMode.DIORAMA) { - HeadPosition headPosition; - if (usePositionFiltering()) - { - headPosition = getFilteredHeadPosition(); - } - else - { - headPosition = getHeadPosition(); - } - - headtrackingHandler.handleHeadTrackingState( - ref headPosition, + headtrackingConnection.handleHeadTrackingState( ref targetPosition, ref targetViewSeparation, scaledViewSeparation, - scaledFocusDistance, - cameraParent.transform.localPosition + scaledFocusDistance ); } else if (mode == G3DCameraMode.MULTIVIEW) @@ -1513,160 +1273,6 @@ void OnRenderImage(RenderTexture source, RenderTexture destination) } #endregion - #region callback handling - void ITNewHeadPositionCallback.NewHeadPositionCallback( - bool headDetected, - bool imagePosIsValid, - int imagePosX, - int imagePosY, - double worldPosX, - double worldPosY, - double worldPosZ - ) - { - lock (headPosLock) - { - string logEntry = - DateTime.Now.ToString("HH:mm::ss.fff") - + ";" - + worldPosX - + ";" - + worldPosY - + ";" - + worldPosZ - + ";" - + headDetected - + ";" - + imagePosIsValid - + ";"; - - headPosition.headDetected = headDetected; - headPosition.imagePosIsValid = imagePosIsValid; - - int millimeterToMeter = 1000; - - Vector3 headPos = new Vector3( - (float)-worldPosX / millimeterToMeter, - (float)worldPosY / millimeterToMeter, - (float)-worldPosZ / millimeterToMeter - ); - - int scaleFactorInt = (int)sceneScaleFactor * (int)headTrackingScale; - float scaleFactor = sceneScaleFactor * headTrackingScale; - - headPosition.imagePosX = imagePosX / (int)millimeterToMeter * scaleFactorInt; - headPosition.imagePosY = imagePosY / (int)millimeterToMeter * scaleFactorInt; - headPosition.worldPosX = headPos.x * scaleFactor; - headPosition.worldPosY = headPos.y * scaleFactor; - headPosition.worldPosZ = headPos.z * sceneScaleFactor; - - if (usePositionFiltering()) - { - double filteredPositionX; - double filteredPositionY; - double filteredPositionZ; - - if (headDetected) - { - try - { - libInterface.applyPositionFilter( - worldPosX, - worldPosY, - worldPosZ, - out filteredPositionX, - out filteredPositionY, - out filteredPositionZ - ); - - filteredHeadPosition.worldPosX = - -filteredPositionX / millimeterToMeter * scaleFactor; - filteredHeadPosition.worldPosY = - filteredPositionY / millimeterToMeter * scaleFactor; - filteredHeadPosition.worldPosZ = - -filteredPositionZ / millimeterToMeter * sceneScaleFactor; - } - catch (Exception e) - { - Debug.LogError("Failed to apply position filter: " + e.Message); - } - } - - filteredHeadPosition.headDetected = headDetected; - filteredHeadPosition.imagePosIsValid = imagePosIsValid; - - logEntry += - filteredHeadPosition.worldPosX - + ";" - + filteredHeadPosition.worldPosY - + ";" - + filteredHeadPosition.worldPosZ - + ";"; - } - - headPositionLog.Enqueue(logEntry); - } - } - - void ITNewErrorMessageCallback.NewErrorMessageCallback( - EMessageSeverity severity, - string sender, - string caption, - string cause, - string remedy - ) - { - string messageText = formatErrorMessage(caption, cause, remedy); - switch (severity) - { - case EMessageSeverity.MS_EXCEPTION: - Debug.LogError(messageText); - break; - case EMessageSeverity.MS_ERROR: - Debug.LogError(messageText); - break; - case EMessageSeverity.MS_WARNING: - Debug.LogWarning(messageText); - break; - case EMessageSeverity.MS_INFO: - - Debug.Log(messageText); - break; - default: - Debug.Log(messageText); - break; - } - } - - /// - /// The shader parameters contain everything necessary for the shader to render the 3D effect. - /// These are updated every time a new head position is received. - /// They do not update the head position itself. - /// - /// - void ITNewShaderParametersCallback.NewShaderParametersCallback( - G3DShaderParameters shaderParameters - ) - { - lock (shaderLock) - { - this.shaderParameters = shaderParameters; - } - } - - private string formatErrorMessage(string caption, string cause, string remedy) - { - string messageText = caption + ": " + cause; - - if (string.IsNullOrEmpty(remedy) == false) - { - messageText = messageText + "\\n" + remedy; - } - - return messageText; - } - #endregion - #region Debugging #if UNITY_EDITOR void OnDrawGizmos() @@ -1813,13 +1419,4 @@ int tmpCameraCount return offset * flip; } - - /// - /// Returns false if all values of the position filter are set to zero. - /// - /// - private bool usePositionFiltering() - { - return headPositionFilter.x != 0 || headPositionFilter.y != 0 || headPositionFilter.z != 0; - } } diff --git a/G3DCameraMosaicMultiview.cs b/G3DCameraMosaicMultiview.cs index 956c649..6ec5753 100644 --- a/G3DCameraMosaicMultiview.cs +++ b/G3DCameraMosaicMultiview.cs @@ -117,20 +117,8 @@ void Start() //initialize cameras - shaderHandles = new ShaderHandles() - { - leftViewportPosition = Shader.PropertyToID("viewport_pos_x"), - bottomViewportPosition = Shader.PropertyToID("viewport_pos_y"), - screenHeight = Shader.PropertyToID("screen_height"), - screenWidth = Shader.PropertyToID("screen_width"), - nativeViewCount = Shader.PropertyToID("nativeViewCount"), - angleRatioNumerator = Shader.PropertyToID("zwinkel"), - angleRatioDenominator = Shader.PropertyToID("nwinkel"), - leftLensOrientation = Shader.PropertyToID("isleft"), - BGRPixelLayout = Shader.PropertyToID("isBGR"), - hqViewCount = Shader.PropertyToID("hqview"), - mstart = Shader.PropertyToID("mstart"), - }; + shaderHandles = new ShaderHandles(); + shaderHandles.init(); // This has to be done after the cameras are updated cachedWindowPosition = new Vector2Int( diff --git a/Scripts/HeadTracking/HeadTrackingPretender.cs b/Scripts/HeadTracking/HeadTrackingPretender.cs index 05de906..b5a5acc 100644 --- a/Scripts/HeadTracking/HeadTrackingPretender.cs +++ b/Scripts/HeadTracking/HeadTrackingPretender.cs @@ -23,7 +23,7 @@ void Update() headPosition = -headPosition; headPosition = headPosition * 1000; headPosition.z += initialOffsetZ * 1000; - ((ITNewHeadPositionCallback)g3dCamera).NewHeadPositionCallback( + ((ITNewHeadPositionCallback)g3dCamera.headtrackingConnection).NewHeadPositionCallback( headDetected, true, (int)headPosition.x, diff --git a/Scripts/HeadtrackingConnection.cs b/Scripts/HeadtrackingConnection.cs new file mode 100644 index 0000000..16e994b --- /dev/null +++ b/Scripts/HeadtrackingConnection.cs @@ -0,0 +1,732 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +public class HeadtrackingConnection + : ITNewHeadPositionCallback, + ITNewShaderParametersCallback, + ITNewErrorMessageCallback +{ + [Tooltip("Time it takes till the reset animation starts in seconds.")] + public float headLostTimeoutInSec = 3.0f; + + [Tooltip("Reset animation duratuion in seconds.")] + public float transitionDuration = 0.5f; + + public Vector3Int headPositionFilter = new Vector3Int(5, 5, 5); + public LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE; + + public float focusDistance = 1.0f; + public float headTrackingScale = 1.0f; + public float sceneScaleFactor = 1.0f; + + private bool debugMessages; + + private Vector3 lastHeadPosition = new Vector3(0, 0, 0); + + private float headLostTimer = 0.0f; + private float transitionTime = 0.0f; + + private enum HeadTrackingState + { + TRACKING, + LOST, + TRANSITIONTOLOST, + TRANSITIONTOTRACKING, + LOSTGRACEPERIOD + } + + private HeadTrackingState prevHeadTrackingState = HeadTrackingState.LOST; + + private LibInterface libInterface; + private string calibrationPath; + + /// + /// This struct is used to store the current head position. + /// It is updated in a different thread, so always use getHeadPosition() to get the current head position. + /// NEVER use headPosition directly. + /// + private HeadPosition headPosition; + private HeadPosition filteredHeadPosition; + + private static object headPosLock = new object(); + private static object shaderLock = new object(); + + private Queue headPositionLog; + + private G3DCamera g3dCamera; + + public HeadtrackingConnection( + float focusDistance, + float headTrackingScale, + float sceneScaleFactor, + string calibrationPathOverwrite, + G3DCamera g3dCamera, + bool debugMessages = false, + Vector3Int headPositionFilter = new Vector3Int(), + LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE + ) + { + lastHeadPosition = new Vector3(0, 0, -focusDistance); + this.focusDistance = focusDistance; + this.headTrackingScale = headTrackingScale; + this.sceneScaleFactor = sceneScaleFactor; + + calibrationPath = System.Environment.GetFolderPath( + Environment.SpecialFolder.CommonDocuments + ); + calibrationPath = Path.Combine(calibrationPath, "3D Global", "calibrations"); + if (!string.IsNullOrEmpty(calibrationPathOverwrite)) + { + calibrationPath = calibrationPathOverwrite; + } + + this.debugMessages = debugMessages; + this.headPositionFilter = headPositionFilter; + this.latencyCorrectionMode = latencyCorrectionMode; + + this.g3dCamera = g3dCamera; + + headPositionLog = new Queue(10000); + } + + public void startHeadTracking() + { + try + { + libInterface.startHeadTracking(); + } + catch (Exception e) + { + Debug.LogError("Failed to start head tracking: " + e.Message); + } + } + + public void shiftViewToLeft() + { + try + { + libInterface.shiftViewToLeft(); + } + catch (Exception e) + { + Debug.LogError("Failed to shift view to left: " + e.Message); + } + } + + public void shiftViewToRight() + { + try + { + libInterface.shiftViewToRight(); + } + catch (Exception e) + { + Debug.LogError("Failed to shift view to right: " + e.Message); + } + } + + public void toggleHeadTracking() + { + if (libInterface == null || !libInterface.isInitialized()) + { + return; + } + + try + { + HeadTrackingStatus headtrackingConnection = libInterface.getHeadTrackingStatus(); + if (headtrackingConnection.hasTrackingDevice) + { + if (!headtrackingConnection.isTrackingActive) + { + libInterface.startHeadTracking(); + } + else + { + libInterface.stopHeadTracking(); + } + } + } + catch (Exception e) + { + Debug.LogError("Failed to toggle head tracking status: " + e.Message); + } + } + + /// + /// always use this method to get the current head position. + /// NEVER access headPosition directly, as it is updated in a different thread. + /// + /// + /// + public HeadPosition getHeadPosition() + { + HeadPosition currentHeadPosition; + lock (headPosLock) + { + if (usePositionFiltering()) + { + currentHeadPosition = filteredHeadPosition; + } + else + { + currentHeadPosition = headPosition; + } + } + return currentHeadPosition; + } + + /** + * Calculate the new camera center position based on the head tracking. + * If head tracking is lost, or the head moves to far away from the tracking camera a grace periope is started. + * Afterwards the camera center will be animated back towards the default position. + */ + public void handleHeadTrackingState( + ref Vector3 targetPosition, + ref float targetViewSeparation, + float viewSeparation, + float focusDistance + ) + { + HeadPosition headPos = getHeadPosition(); + // get new state + HeadTrackingState newState = getNewTrackingState(prevHeadTrackingState, ref headPos); + + prevHeadTrackingState = newState; + + // handle lost state + if (newState == HeadTrackingState.LOST) + { + targetPosition = new Vector3(0, 0, -focusDistance); + targetViewSeparation = 0.0f; + } + // handle tracking state + else if (newState == HeadTrackingState.TRACKING) + { + Vector3 headPositionWorld = new Vector3( + (float)headPos.worldPosX, + (float)headPos.worldPosY, + (float)headPos.worldPosZ + ); + + targetPosition = headPositionWorld; + targetViewSeparation = viewSeparation; + lastHeadPosition = targetPosition; + } + // if lost, start grace period + else if (newState == HeadTrackingState.LOSTGRACEPERIOD) + { + // if we have waited for the timeout + if (headLostTimer > headLostTimeoutInSec) + { + newState = HeadTrackingState.TRANSITIONTOLOST; + headLostTimer = 0.0f; + transitionTime = 0.0f; + } + else + { + headLostTimer += Time.deltaTime; + targetPosition = lastHeadPosition; + targetViewSeparation = viewSeparation; + } + } + // handle transitions + else if ( + newState == HeadTrackingState.TRANSITIONTOLOST + || newState == HeadTrackingState.TRANSITIONTOTRACKING + ) + { + // init with values for transition to lost + Vector3 originPosition = lastHeadPosition; + Vector3 transitionTargetPosition = new Vector3(0, 0, -focusDistance); + float transitionViewSeparation = 0.0f; + float originSeparation = viewSeparation; + + if (newState == HeadTrackingState.TRANSITIONTOTRACKING) + { + originPosition = new Vector3(0, 0, -focusDistance); + transitionViewSeparation = viewSeparation; + originSeparation = 0.0f; + + if (headPos.headDetected) + { + Vector3 headPositionWorld = new Vector3( + (float)headPos.worldPosX, + (float)headPos.worldPosY, + (float)headPos.worldPosZ + ); + transitionTargetPosition = headPositionWorld; + } + else + { + // if no head is detected use last known head position + transitionTargetPosition = lastHeadPosition; + } + } + + bool isEndReached = handleTransition( + originPosition, + transitionTargetPosition, + originSeparation, + transitionViewSeparation, + ref targetPosition, + ref targetViewSeparation + ); + if (isEndReached) + { + transitionTime = 0.0f; + // if we have reached the target position, we are no longer in transition + if (newState == HeadTrackingState.TRANSITIONTOLOST) + { + newState = HeadTrackingState.LOST; + } + else + { + newState = HeadTrackingState.TRACKING; + } + } + } + + // reset lost timer if we are not in grace period + if (newState != HeadTrackingState.LOSTGRACEPERIOD) + { + headLostTimer = 0.0f; + } + + prevHeadTrackingState = newState; + } + + public void initLibrary() + { + string applicationName = Application.productName; + if (string.IsNullOrEmpty(applicationName)) + { + applicationName = "Unity"; + } + var invalids = System.IO.Path.GetInvalidFileNameChars(); + applicationName = String + .Join("_", applicationName.Split(invalids, StringSplitOptions.RemoveEmptyEntries)) + .TrimEnd('.'); + applicationName = applicationName + "_G3D_Config.ini"; + + try + { + bool useHimaxD2XXDevices = true; + bool useHimaxRP2040Devices = true; + bool usePmdFlexxDevices = true; + + libInterface = LibInterface.Instance; + libInterface.init( + calibrationPath, + Application.persistentDataPath, + applicationName, + this, + this, + this, + debugMessages, + useHimaxD2XXDevices, + useHimaxRP2040Devices, + usePmdFlexxDevices + ); + } + catch (Exception e) + { + Debug.LogError("Failed to initialize library: " + e.Message); + return; + } + + // set initial values + // intialize head position at focus distance from focus plane + headPosition = new HeadPosition + { + headDetected = false, + imagePosIsValid = false, + imagePosX = 0, + imagePosY = 0, + worldPosX = 0.0, + worldPosY = 0.0, + worldPosZ = -focusDistance + }; + filteredHeadPosition = new HeadPosition + { + headDetected = false, + imagePosIsValid = false, + imagePosX = 0, + imagePosY = 0, + worldPosX = 0.0, + worldPosY = 0.0, + worldPosZ = -focusDistance + }; + + if (usePositionFiltering()) + { + try + { + libInterface.initializePositionFilter( + headPositionFilter.x, + headPositionFilter.y, + headPositionFilter.z + ); + } + catch (Exception e) + { + Debug.LogError("Failed to initialize position filter: " + e.Message); + } + } + } + + public void deinitLibrary() + { + if (libInterface == null || !libInterface.isInitialized()) + { + return; + } + + try + { + libInterface.stopHeadTracking(); + libInterface.unregisterHeadPositionChangedCallback(this); + libInterface.unregisterShaderParametersChangedCallback(this); + libInterface.unregisterMessageCallback(this); + libInterface.deinit(); + } + catch (Exception e) + { + Debug.Log(e); + } + } + + public void logCameraPositionsToFile() + { + StreamWriter writer = new StreamWriter( + Application.dataPath + "/HeadPositionLog.csv", + false + ); + writer.WriteLine( + "Camera update time; Camera X; Camera Y; Camera Z; Head detected; Image position valid; Filtered X; Filtered Y; Filtered Z" + ); + string[] headPoitionLogArray = headPositionLog.ToArray(); + for (int i = 0; i < headPoitionLogArray.Length; i++) + { + writer.WriteLine(headPoitionLogArray[i]); + } + writer.Close(); + } + + public void calculateShaderParameters() + { + libInterface.calculateShaderParameters(latencyCorrectionMode); + g3dCamera.setShaderParameters(libInterface.getCurrentShaderParameters()); + } + + public void updateScreenViewportProperties(Vector2Int displayResolution) + { + try + { + // This is the size of the entire monitor screen + libInterface.setScreenSize(displayResolution.x, displayResolution.y); + + // this refers to the window in which the 3D effect is rendered (including eg windows top window menu) + libInterface.setWindowSize(Screen.width, Screen.height); + libInterface.setWindowPosition( + Screen.mainWindowPosition.x, + Screen.mainWindowPosition.y + ); + + // This refers to the actual viewport in which the 3D effect is rendered + libInterface.setViewportSize(Screen.width, Screen.height); + libInterface.setViewportOffset(0, 0); + } + catch (Exception e) + { + Debug.LogError("Failed to update screen viewport properties: " + e.Message); + } + } + + /// + /// returns true if transition end is reached + /// + /// + /// + /// + /// + /// + /// is transition end reached + private bool handleTransition( + Vector3 originPosition, + Vector3 transitionTargetPosition, + float originSeparation, + float transitionViewSeparation, + ref Vector3 targetPosition, + ref float targetViewSeparation + ) + { + // interpolate values + float transitionPercentage = transitionTime / transitionDuration; + transitionTime += Time.deltaTime; + + // set to default position + Vector3 interpolatedPosition = Vector3.Lerp( + originPosition, + transitionTargetPosition, + transitionPercentage + ); + float interpolatedEyeSeparation = Mathf.Lerp( + originSeparation, + transitionViewSeparation, + transitionPercentage + ); + + // check if end is reached (with a small tolerance) + if (transitionPercentage + 0.01f < 1.0f) + { + // apply values + targetPosition = interpolatedPosition; + targetViewSeparation = interpolatedEyeSeparation; + return false; + } + else + { + return true; + } + } + + private HeadTrackingState getNewTrackingState( + HeadTrackingState currentHeadTrackingState, + ref HeadPosition headPosition + ) + { + HeadTrackingState newState; + + if (headPosition.headDetected) + { + // if head detected + if (currentHeadTrackingState == HeadTrackingState.TRACKING) + { + newState = HeadTrackingState.TRACKING; + } + else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD) + { + newState = HeadTrackingState.TRACKING; + } + else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING) + { + newState = HeadTrackingState.TRANSITIONTOTRACKING; + } + else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST) + { + newState = HeadTrackingState.TRANSITIONTOLOST; + } + else //(currentHeadTrackingState == HeadTrackingState.LOST) + { + newState = HeadTrackingState.TRANSITIONTOTRACKING; + } + } + else + { + // if head not detected + if (currentHeadTrackingState == HeadTrackingState.TRACKING) + { + newState = HeadTrackingState.LOSTGRACEPERIOD; + } + else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD) + { + newState = HeadTrackingState.LOSTGRACEPERIOD; + } + else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING) + { + newState = HeadTrackingState.TRANSITIONTOTRACKING; + } + else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST) + { + newState = HeadTrackingState.TRANSITIONTOLOST; + } + else //(currentHeadTrackingState == HeadTrackingState.LOST) + { + newState = HeadTrackingState.LOST; + } + } + return newState; + } + + private string headTrackingStateToString() + { + switch (prevHeadTrackingState) + { + case HeadTrackingState.TRACKING: + return "TRACKING"; + case HeadTrackingState.LOST: + return "LOST"; + case HeadTrackingState.LOSTGRACEPERIOD: + return "LOSTGRACEPERIOD"; + case HeadTrackingState.TRANSITIONTOLOST: + return "TRANSITIONTOLOST"; + case HeadTrackingState.TRANSITIONTOTRACKING: + return "TRANSITIONTOTRACKING"; + default: + return "UNKNOWN"; + } + } + + #region callback handling + void ITNewHeadPositionCallback.NewHeadPositionCallback( + bool headDetected, + bool imagePosIsValid, + int imagePosX, + int imagePosY, + double worldPosX, + double worldPosY, + double worldPosZ + ) + { + lock (headPosLock) + { + string logEntry = + DateTime.Now.ToString("HH:mm::ss.fff") + + ";" + + worldPosX + + ";" + + worldPosY + + ";" + + worldPosZ + + ";" + + headDetected + + ";" + + imagePosIsValid + + ";"; + + headPosition.headDetected = headDetected; + headPosition.imagePosIsValid = imagePosIsValid; + + int millimeterToMeter = 1000; + + Vector3 headPos = new Vector3( + (float)-worldPosX / millimeterToMeter, + (float)worldPosY / millimeterToMeter, + (float)-worldPosZ / millimeterToMeter + ); + + int scaleFactorInt = (int)sceneScaleFactor * (int)headTrackingScale; + float scaleFactor = sceneScaleFactor * headTrackingScale; + + headPosition.imagePosX = imagePosX / (int)millimeterToMeter * scaleFactorInt; + headPosition.imagePosY = imagePosY / (int)millimeterToMeter * scaleFactorInt; + headPosition.worldPosX = headPos.x * scaleFactor; + headPosition.worldPosY = headPos.y * scaleFactor; + headPosition.worldPosZ = headPos.z * sceneScaleFactor; + + if (usePositionFiltering()) + { + double filteredPositionX; + double filteredPositionY; + double filteredPositionZ; + + if (headDetected) + { + try + { + libInterface.applyPositionFilter( + worldPosX, + worldPosY, + worldPosZ, + out filteredPositionX, + out filteredPositionY, + out filteredPositionZ + ); + + filteredHeadPosition.worldPosX = + -filteredPositionX / millimeterToMeter * scaleFactor; + filteredHeadPosition.worldPosY = + filteredPositionY / millimeterToMeter * scaleFactor; + filteredHeadPosition.worldPosZ = + -filteredPositionZ / millimeterToMeter * sceneScaleFactor; + } + catch (Exception e) + { + Debug.LogError("Failed to apply position filter: " + e.Message); + } + } + + filteredHeadPosition.headDetected = headDetected; + filteredHeadPosition.imagePosIsValid = imagePosIsValid; + + logEntry += + filteredHeadPosition.worldPosX + + ";" + + filteredHeadPosition.worldPosY + + ";" + + filteredHeadPosition.worldPosZ + + ";"; + } + + headPositionLog.Enqueue(logEntry); + } + } + + void ITNewErrorMessageCallback.NewErrorMessageCallback( + EMessageSeverity severity, + string sender, + string caption, + string cause, + string remedy + ) + { + string messageText = formatErrorMessage(caption, cause, remedy); + switch (severity) + { + case EMessageSeverity.MS_EXCEPTION: + Debug.LogError(messageText); + break; + case EMessageSeverity.MS_ERROR: + Debug.LogError(messageText); + break; + case EMessageSeverity.MS_WARNING: + Debug.LogWarning(messageText); + break; + case EMessageSeverity.MS_INFO: + + Debug.Log(messageText); + break; + default: + Debug.Log(messageText); + break; + } + } + + /// + /// The shader parameters contain everything necessary for the shader to render the 3D effect. + /// These are updated every time a new head position is received. + /// They do not update the head position itself. + /// + /// + void ITNewShaderParametersCallback.NewShaderParametersCallback( + G3DShaderParameters shaderParameters + ) + { + g3dCamera.setShaderParameters(shaderParameters); + } + + private string formatErrorMessage(string caption, string cause, string remedy) + { + string messageText = caption + ": " + cause; + + if (string.IsNullOrEmpty(remedy) == false) + { + messageText = messageText + "\n" + remedy; + } + + return messageText; + } + #endregion + + /// + /// Returns false if all values of the position filter are set to zero. + /// + /// + private bool usePositionFiltering() + { + return headPositionFilter.x != 0 || headPositionFilter.y != 0 || headPositionFilter.z != 0; + } +} diff --git a/Scripts/HeadtrackingConnection.cs.meta b/Scripts/HeadtrackingConnection.cs.meta new file mode 100644 index 0000000..f474a10 --- /dev/null +++ b/Scripts/HeadtrackingConnection.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 14b0027f55e52ab43b31007b4669e221 \ No newline at end of file diff --git a/Scripts/HeadtrackingHandler.cs b/Scripts/HeadtrackingHandler.cs deleted file mode 100644 index 7bb0f13..0000000 --- a/Scripts/HeadtrackingHandler.cs +++ /dev/null @@ -1,277 +0,0 @@ -using UnityEngine; - -public class HeadtrackingHandler -{ - [Tooltip("Time it takes till the reset animation starts in seconds.")] - public float headLostTimeoutInSec = 3.0f; - - [Tooltip("Reset animation duratuion in seconds.")] - public float transitionDuration = 0.5f; - - private Vector3 lastHeadPosition = new Vector3(0, 0, 0); - - private float headLostTimer = 0.0f; - private float transitionTime = 0.0f; - - private enum HeadTrackingState - { - TRACKING, - LOST, - TRANSITIONTOLOST, - TRANSITIONTOTRACKING, - LOSTGRACEPERIOD - } - - private HeadTrackingState prevHeadTrackingState = HeadTrackingState.LOST; - - public HeadtrackingHandler(float focusDistance) - { - lastHeadPosition = new Vector3(0, 0, -focusDistance); - } - - /** - * Calculate the new camera center position based on the head tracking. - * If head tracking is lost, or the head moves to far away from the tracking camera a grace periope is started. - * Afterwards the camera center will be animated back towards the default position. - */ - public void handleHeadTrackingState( - ref HeadPosition headPosition, - ref Vector3 targetPosition, - ref float targetViewSeparation, - float viewSeparation, - float focusDistance, - Vector3 cameraParentLocalPosition - ) - { - // get new state - HeadTrackingState newState = getNewStateState(prevHeadTrackingState, ref headPosition); - - prevHeadTrackingState = newState; - - // handle lost state - if (newState == HeadTrackingState.LOST) - { - targetPosition = new Vector3(0, 0, -focusDistance); - targetViewSeparation = 0.0f; - } - // handle tracking state - else if (newState == HeadTrackingState.TRACKING) - { - Vector3 headPositionWorld = new Vector3( - (float)headPosition.worldPosX, - (float)headPosition.worldPosY, - (float)headPosition.worldPosZ - ); - - targetPosition = headPositionWorld; - targetViewSeparation = viewSeparation; - lastHeadPosition = targetPosition; - } - // if lost, start grace period - else if (newState == HeadTrackingState.LOSTGRACEPERIOD) - { - // if we have waited for the timeout - if (headLostTimer > headLostTimeoutInSec) - { - newState = HeadTrackingState.TRANSITIONTOLOST; - headLostTimer = 0.0f; - transitionTime = 0.0f; - } - else - { - headLostTimer += Time.deltaTime; - targetPosition = lastHeadPosition; - targetViewSeparation = viewSeparation; - } - } - // handle transitions - else if ( - newState == HeadTrackingState.TRANSITIONTOLOST - || newState == HeadTrackingState.TRANSITIONTOTRACKING - ) - { - // init with values for transition to lost - Vector3 originPosition = lastHeadPosition; - Vector3 transitionTargetPosition = new Vector3(0, 0, -focusDistance); - float transitionViewSeparation = 0.0f; - float originSeparation = viewSeparation; - - if (newState == HeadTrackingState.TRANSITIONTOTRACKING) - { - originPosition = new Vector3(0, 0, -focusDistance); - transitionViewSeparation = viewSeparation; - originSeparation = 0.0f; - - if (headPosition.headDetected) - { - Vector3 headPositionWorld = new Vector3( - (float)headPosition.worldPosX, - (float)headPosition.worldPosY, - (float)headPosition.worldPosZ - ); - transitionTargetPosition = headPositionWorld; - } - else - { - // if no head is detected use last known head position - transitionTargetPosition = lastHeadPosition; - } - } - - bool isEndReached = handleTransition( - originPosition, - transitionTargetPosition, - originSeparation, - transitionViewSeparation, - ref targetPosition, - ref targetViewSeparation - ); - if (isEndReached) - { - transitionTime = 0.0f; - // if we have reached the target position, we are no longer in transition - if (newState == HeadTrackingState.TRANSITIONTOLOST) - { - newState = HeadTrackingState.LOST; - } - else - { - newState = HeadTrackingState.TRACKING; - } - } - } - - // reset lost timer if we are not in grace period - if (newState != HeadTrackingState.LOSTGRACEPERIOD) - { - headLostTimer = 0.0f; - } - - prevHeadTrackingState = newState; - } - - /// - /// returns true if transition end is reached - /// - /// - /// - /// - /// - /// - /// is transition end reached - private bool handleTransition( - Vector3 originPosition, - Vector3 transitionTargetPosition, - float originSeparation, - float transitionViewSeparation, - ref Vector3 targetPosition, - ref float targetViewSeparation - ) - { - // interpolate values - float transitionPercentage = transitionTime / transitionDuration; - transitionTime += Time.deltaTime; - - // set to default position - Vector3 interpolatedPosition = Vector3.Lerp( - originPosition, - transitionTargetPosition, - transitionPercentage - ); - float interpolatedEyeSeparation = Mathf.Lerp( - originSeparation, - transitionViewSeparation, - transitionPercentage - ); - - // check if end is reached (with a small tolerance) - if (transitionPercentage + 0.01f < 1.0f) - { - // apply values - targetPosition = interpolatedPosition; - targetViewSeparation = interpolatedEyeSeparation; - return false; - } - else - { - return true; - } - } - - private HeadTrackingState getNewStateState( - HeadTrackingState currentHeadTrackingState, - ref HeadPosition headPosition - ) - { - HeadTrackingState newState; - - if (headPosition.headDetected) - { - // if head detected - if (currentHeadTrackingState == HeadTrackingState.TRACKING) - { - newState = HeadTrackingState.TRACKING; - } - else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD) - { - newState = HeadTrackingState.TRACKING; - } - else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING) - { - newState = HeadTrackingState.TRANSITIONTOTRACKING; - } - else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST) - { - newState = HeadTrackingState.TRANSITIONTOLOST; - } - else //(currentHeadTrackingState == HeadTrackingState.LOST) - { - newState = HeadTrackingState.TRANSITIONTOTRACKING; - } - } - else - { - // if head not detected - if (currentHeadTrackingState == HeadTrackingState.TRACKING) - { - newState = HeadTrackingState.LOSTGRACEPERIOD; - } - else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD) - { - newState = HeadTrackingState.LOSTGRACEPERIOD; - } - else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING) - { - newState = HeadTrackingState.TRANSITIONTOTRACKING; - } - else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST) - { - newState = HeadTrackingState.TRANSITIONTOLOST; - } - else //(currentHeadTrackingState == HeadTrackingState.LOST) - { - newState = HeadTrackingState.LOST; - } - } - return newState; - } - - private string headTrackingStateToString() - { - switch (prevHeadTrackingState) - { - case HeadTrackingState.TRACKING: - return "TRACKING"; - case HeadTrackingState.LOST: - return "LOST"; - case HeadTrackingState.LOSTGRACEPERIOD: - return "LOSTGRACEPERIOD"; - case HeadTrackingState.TRANSITIONTOLOST: - return "TRANSITIONTOLOST"; - case HeadTrackingState.TRANSITIONTOTRACKING: - return "TRANSITIONTOTRACKING"; - default: - return "UNKNOWN"; - } - } -} diff --git a/Scripts/HeadtrackingHandler.cs.meta b/Scripts/HeadtrackingHandler.cs.meta deleted file mode 100644 index 7740871..0000000 --- a/Scripts/HeadtrackingHandler.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 6e134d4f9564d2e4380aa95380f61a15 \ No newline at end of file diff --git a/Scripts/StuctsAndEnums.cs b/Scripts/StuctsAndEnums.cs index 7715c84..492d481 100644 --- a/Scripts/StuctsAndEnums.cs +++ b/Scripts/StuctsAndEnums.cs @@ -55,6 +55,36 @@ struct ShaderHandles public int zCorrectionValue; public int zCompensationValue; + + public void init() + { + leftViewportPosition = Shader.PropertyToID("viewport_pos_x"); + bottomViewportPosition = Shader.PropertyToID("viewport_pos_y"); + screenWidth = Shader.PropertyToID("screen_width"); + screenHeight = Shader.PropertyToID("screen_height"); + nativeViewCount = Shader.PropertyToID("nativeViewCount"); + angleRatioNumerator = Shader.PropertyToID("zwinkel"); + angleRatioDenominator = Shader.PropertyToID("nwinkel"); + leftLensOrientation = Shader.PropertyToID("isleft"); + BGRPixelLayout = Shader.PropertyToID("isBGR"); + mstart = Shader.PropertyToID("mstart"); + showTestFrame = Shader.PropertyToID("test"); + showTestStripe = Shader.PropertyToID("stest"); + testGapWidth = Shader.PropertyToID("testgap"); + track = Shader.PropertyToID("track"); + hqViewCount = Shader.PropertyToID("hqview"); + hviews1 = Shader.PropertyToID("hviews1"); + hviews2 = Shader.PropertyToID("hviews2"); + blur = Shader.PropertyToID("blur"); + blackBorder = Shader.PropertyToID("bborder"); + blackSpace = Shader.PropertyToID("bspace"); + bls = Shader.PropertyToID("bls"); + ble = Shader.PropertyToID("ble"); + brs = Shader.PropertyToID("brs"); + bre = Shader.PropertyToID("bre"); + zCorrectionValue = Shader.PropertyToID("tvx"); + zCompensationValue = Shader.PropertyToID("zkom"); + } } public struct PreviousValues From 79de5a679db7d14ce0d1f8f64937f5650969caf1 Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 19 Mar 2026 15:09:44 +0100 Subject: [PATCH 113/149] fixes for URP hide view generation in inspector in URP; generateViews private in URP; fix to Helpers for URP --- G3DCamera.cs | 10 +++++++++- Scripts/InspectorG3DCamera.cs | 5 +++++ Scripts/RenderPipelines/Helpers.cs | 6 ++++-- Scripts/RenderPipelines/URP/ScriptableRP.cs | 4 ++-- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/G3DCamera.cs b/G3DCamera.cs index 21cc5be..6110aad 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -49,8 +49,12 @@ public class G3DCamera : MonoBehaviour [Range(1, 100)] public int renderResolutionScale = 100; +#if G3D_URP + private bool generateViews = false; +#elif G3D_HDRP [Tooltip("Only actually renders three views. The rest are generated. IF turned on anti ali")] - public bool generateViews = true; + public bool generateViews = false; +#endif #endregion @@ -879,10 +883,12 @@ void Update() recreatedRenderTextures = true; } +#if G3D_HDRP customPassController.cameraCountChanged = cameraCountChanged; customPassController.resolutionScaleChanged = oldRenderResolutionScale != renderResolutionScale; customPassController.debugRendering = debugRendering; +#endif if (recreatedRenderTextures) { @@ -987,11 +993,13 @@ private void updateShaderParameters() material?.SetInt(Shader.PropertyToID("mosaic_rows"), 4); material?.SetInt(Shader.PropertyToID("mosaic_columns"), 4); +#if G3D_HDRP if (generateViews) { viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); } +#endif } } diff --git a/Scripts/InspectorG3DCamera.cs b/Scripts/InspectorG3DCamera.cs index 1f6cefe..5bcb6ea 100644 --- a/Scripts/InspectorG3DCamera.cs +++ b/Scripts/InspectorG3DCamera.cs @@ -100,8 +100,13 @@ public override VisualElement CreateInspectorGUI() } ); +#if G3D_URP + // hide in URP + viewGenerationContainer.style.display = DisplayStyle.None; +#elif G3D_HDRP // setup UI setViewgenerationDisplay((target as G3DCamera).generateViews); +#endif return mainInspector; } diff --git a/Scripts/RenderPipelines/Helpers.cs b/Scripts/RenderPipelines/Helpers.cs index 07b4822..fdc02c3 100644 --- a/Scripts/RenderPipelines/Helpers.cs +++ b/Scripts/RenderPipelines/Helpers.cs @@ -1,7 +1,8 @@ -#if G3D_HDRP using UnityEngine; using UnityEngine.Rendering; +#if G3D_HDRP using UnityEngine.Rendering.HighDefinition; +#endif namespace G3D.RenderPipeline { @@ -15,6 +16,7 @@ internal enum AntialiasingMode internal class Helpers { +#if G3D_HDRP private static RTHandleSystem m_RTHandleSystem; public static RTHandleSystem GetRTHandleSystem() @@ -26,6 +28,7 @@ public static RTHandleSystem GetRTHandleSystem() } return m_RTHandleSystem; } +#endif /// /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set. @@ -57,4 +60,3 @@ internal static bool isMainG3DCamera(Camera camera) } } } -#endif diff --git a/Scripts/RenderPipelines/URP/ScriptableRP.cs b/Scripts/RenderPipelines/URP/ScriptableRP.cs index 9af5bd7..13b011e 100644 --- a/Scripts/RenderPipelines/URP/ScriptableRP.cs +++ b/Scripts/RenderPipelines/URP/ScriptableRP.cs @@ -43,7 +43,7 @@ ref RenderingData renderingData { var camera = renderingData.cameraData.camera; - if (Helpers.isMainG3DCamera(camera, m_Material)) + if (Helpers.isMainG3DCamera(camera)) { CommandBuffer cmd = CommandBufferPool.Get(); Blitter.BlitCameraTexture( @@ -82,7 +82,7 @@ ref PassData passData static void ExecutePass(PassData data, RasterGraphContext context) { var camera = data.camera; - if (Helpers.isMainG3DCamera(camera, data.blitMaterial)) + if (Helpers.isMainG3DCamera(camera)) { Blitter.BlitTexture( context.cmd, From fc3771cfe6cc8f0d189aaf76d92038651ae0bcaa Mon Sep 17 00:00:00 2001 From: Phillip Hohenester Date: Thu, 19 Mar 2026 16:43:59 +0100 Subject: [PATCH 114/149] moved all code into G3D namespace --- G3DCamera.cs | 2288 +++++----- G3DCameraMosaicMultiview.cs | 935 ++-- Scripts/CalibrationProvider.cs | 423 +- Scripts/HeadTracking/HeadTrackingPretender.cs | 59 +- .../HeadTracking/HeadtrackingConnection.cs | 737 +++ .../HeadtrackingConnection.cs.meta | 0 .../Interfaces/ITNewErrorMessageCallback.cs | 23 +- .../Interfaces/ITNewHeadPositionCallback.cs | 27 +- .../ITNewShaderParametersCallback.cs | 11 +- Scripts/HeadTracking/LibInterface.cs | 4007 +++++++++-------- Scripts/HeadtrackingConnection.cs | 732 --- Scripts/IndexMap.cs | 1763 ++++---- Scripts/InspectorG3DCamera.cs | 177 +- Scripts/InspectorG3DMosaicCamera.cs | 129 +- Scripts/PackageManagerInteractor.cs | 149 +- Scripts/StuctsAndEnums.cs | 195 +- .../ViewmapGeneratorInterface.cs | 397 +- 17 files changed, 6113 insertions(+), 5939 deletions(-) create mode 100644 Scripts/HeadTracking/HeadtrackingConnection.cs rename Scripts/{ => HeadTracking}/HeadtrackingConnection.cs.meta (100%) delete mode 100644 Scripts/HeadtrackingConnection.cs diff --git a/G3DCamera.cs b/G3DCamera.cs index 6110aad..fd7040b 100644 --- a/G3DCamera.cs +++ b/G3DCamera.cs @@ -12,1419 +12,1461 @@ using UnityEngine.UI; #endif -/// -/// IMPORTANT: This script must not be attached to a camera already using a G3D camera script. -/// -[RequireComponent(typeof(Camera))] -[DisallowMultipleComponent] -public class G3DCamera : MonoBehaviour +namespace G3D { - [Tooltip("Drop the calibration file for the display you want to use here.")] - public TextAsset calibrationFile; - - [Tooltip( - "This path has to be set to the directory where the folder containing the calibration files for your monitor are located. The folder has to have the same name as your camera model." - )] - public string calibrationPathOverwrite = ""; - - #region 3D Effect settings - public G3DCameraMode mode = G3DCameraMode.DIORAMA; - public static string CAMERA_NAME_PREFIX = "g3dcam_"; - - [Tooltip( - "This value can be used to scale the real world values used to calibrate the extension. For example if your scene is 10 times larger than the real world, you can set this value to 10. Do NOT change this while the game is already running!" - )] - [Min(0.000001f)] - public float sceneScaleFactor = 1.0f; - - [Tooltip( - "If set to true, the views will be flipped horizontally. This is necessary for holoboxes." - )] - public bool mirrorViews = false; - - #region Performance - [Tooltip( - "Set a percentage value to render only that percentage of the width and height per view. E.g. a reduction of 50% will reduce the rendered size by a factor of 4. Adapt Render Resolution To Views takes precedence." - )] - [Range(1, 100)] - public int renderResolutionScale = 100; + /// + /// IMPORTANT: This script must not be attached to a camera already using a G3D camera script. + /// + [RequireComponent(typeof(Camera))] + [DisallowMultipleComponent] + public class G3DCamera : MonoBehaviour + { + [Tooltip("Drop the calibration file for the display you want to use here.")] + public TextAsset calibrationFile; + + [Tooltip( + "This path has to be set to the directory where the folder containing the calibration files for your monitor are located. The folder has to have the same name as your camera model." + )] + public string calibrationPathOverwrite = ""; + + #region 3D Effect settings + public G3DCameraMode mode = G3DCameraMode.DIORAMA; + public static string CAMERA_NAME_PREFIX = "g3dcam_"; + + [Tooltip( + "This value can be used to scale the real world values used to calibrate the extension. For example if your scene is 10 times larger than the real world, you can set this value to 10. Do NOT change this while the game is already running!" + )] + [Min(0.000001f)] + public float sceneScaleFactor = 1.0f; + + [Tooltip( + "If set to true, the views will be flipped horizontally. This is necessary for holoboxes." + )] + public bool mirrorViews = false; + + #region Performance + [Tooltip( + "Set a percentage value to render only that percentage of the width and height per view. E.g. a reduction of 50% will reduce the rendered size by a factor of 4. Adapt Render Resolution To Views takes precedence." + )] + [Range(1, 100)] + public int renderResolutionScale = 100; #if G3D_URP - private bool generateViews = false; + private bool generateViews = false; #elif G3D_HDRP - [Tooltip("Only actually renders three views. The rest are generated. IF turned on anti ali")] - public bool generateViews = false; + [Tooltip( + "Only actually renders three views. The rest are generated. IF turned on anti ali" + )] + public bool generateViews = false; #endif - #endregion - - - [Tooltip( - "Set the dolly zoom effekt. 1 correponds to no dolly zoom. 0 is all the way zoomed in to the focus plane. 3 is all the way zoomed out." - )] - [Range(0.001f, 3)] - public float dollyZoom = 1; - - [Tooltip( - "Scale the view offset up or down. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance. This can be used to adjust the view offset down for very large scenes." - )] - [Range(0.0f, 5.0f)] - public float viewOffsetScale = 1.0f; // scale the view offset to the focus distance. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance. - - /// - /// Shifts the individual views to the left or right by the specified number of views. - /// - [Tooltip("Shifts the individual views to the left or right by the specified number of views.")] - public int viewOffset = 0; - - [Tooltip( - "Scales the strength of the head tracking effect. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance." - )] - [Min(0.0f)] - public float headTrackingScale = 1.0f; // scale the head tracking effect - #endregion - - #region Advanced settings - [Tooltip( - "Smoothes the head position (Size of the filter kernel). No filtering is applied, if set to all zeros. DO NOT CHANGE THIS WHILE GAME IS ALREADY RUNNING!" - )] - public Vector3Int headPositionFilter = new Vector3Int(5, 5, 5); - public LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE; - - [Tooltip("If set to true, the head tracking library will print debug messages to the console.")] - public bool debugMessages = false; - - [Tooltip( - "If set to true, shows the individual views as a mosaic instead of the autostereo effect." - )] - public bool debugRendering; - - [Tooltip( - "If set to true, the gizmos for the focus distance (green) and eye separation (blue) will be shown." - )] - public bool showGizmos = true; - - [Tooltip("Scales the gizmos. Affectd by scene scale factor.")] - [Range(0.005f, 1.0f)] - public float gizmoSize = 0.2f; - - public bool invertViewsInDiorama = false; - - [Tooltip( - "Where the views start to yoyo in the index map. Index map contains the order of views." - )] - [Range(0.0f, 1.0f)] - public float indexMapYoyoStart = 0.0f; - - [Tooltip("Inverts the entire index map. Index map contains the order of views.")] - public bool invertIndexMap = false; - - [Tooltip("Inverts the indices in the index map. Index map contains the order of views.")] - public bool invertIndexMapIndices = false; - - public HeadtrackingConnection headtrackingConnection; - - #endregion - - #region Private variables - private PreviousValues previousValues; - private IndexMap indexMap = IndexMap.Instance; - - // distance between the two cameras for diorama mode (in meters). DO NOT USE FOR MULTIVIEW MODE! - private float viewSeparation = 0.065f; - - /// - /// The distance between the camera and the focus plane in meters. Default is 70 cm. - /// Is read from calibration file at startup. - /// DO NOT SET THIS PARAMETER DIRECTLY. Use the SetFocusDistance function instead. - /// - private float focusDistance = 0.7f; - - private const int MAX_CAMERAS = 16; //shaders dont have dynamic arrays and this is the max supported. change it here? change it in the shaders as well ... - private int internalCameraCount = 2; - private int oldRenderResolutionScale = 100; - - private static object shaderLock = new object(); - - private Camera mainCamera; - private List cameras = null; - private GameObject focusPlaneObject = null; - private GameObject cameraParent = null; - - private Material material; + #endregion + + + [Tooltip( + "Set the dolly zoom effekt. 1 correponds to no dolly zoom. 0 is all the way zoomed in to the focus plane. 3 is all the way zoomed out." + )] + [Range(0.001f, 3)] + public float dollyZoom = 1; + + [Tooltip( + "Scale the view offset up or down. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance. This can be used to adjust the view offset down for very large scenes." + )] + [Range(0.0f, 5.0f)] + public float viewOffsetScale = 1.0f; // scale the view offset to the focus distance. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance. + + /// + /// Shifts the individual views to the left or right by the specified number of views. + /// + [Tooltip( + "Shifts the individual views to the left or right by the specified number of views." + )] + public int viewOffset = 0; + + [Tooltip( + "Scales the strength of the head tracking effect. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance." + )] + [Min(0.0f)] + public float headTrackingScale = 1.0f; // scale the head tracking effect + #endregion + + #region Advanced settings + [Tooltip( + "Smoothes the head position (Size of the filter kernel). No filtering is applied, if set to all zeros. DO NOT CHANGE THIS WHILE GAME IS ALREADY RUNNING!" + )] + public Vector3Int headPositionFilter = new Vector3Int(5, 5, 5); + public LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE; + + [Tooltip( + "If set to true, the head tracking library will print debug messages to the console." + )] + public bool debugMessages = false; + + [Tooltip( + "If set to true, shows the individual views as a mosaic instead of the autostereo effect." + )] + public bool debugRendering; + + [Tooltip( + "If set to true, the gizmos for the focus distance (green) and eye separation (blue) will be shown." + )] + public bool showGizmos = true; + + [Tooltip("Scales the gizmos. Affectd by scene scale factor.")] + [Range(0.005f, 1.0f)] + public float gizmoSize = 0.2f; + + public bool invertViewsInDiorama = false; + + [Tooltip( + "Where the views start to yoyo in the index map. Index map contains the order of views." + )] + [Range(0.0f, 1.0f)] + public float indexMapYoyoStart = 0.0f; + + [Tooltip("Inverts the entire index map. Index map contains the order of views.")] + public bool invertIndexMap = false; + + [Tooltip("Inverts the indices in the index map. Index map contains the order of views.")] + public bool invertIndexMapIndices = false; + + public HeadtrackingConnection headtrackingConnection; + + #endregion + + #region Private variables + private PreviousValues previousValues; + private IndexMap indexMap = IndexMap.Instance; + + // distance between the two cameras for diorama mode (in meters). DO NOT USE FOR MULTIVIEW MODE! + private float viewSeparation = 0.065f; + + /// + /// The distance between the camera and the focus plane in meters. Default is 70 cm. + /// Is read from calibration file at startup. + /// DO NOT SET THIS PARAMETER DIRECTLY. Use the SetFocusDistance function instead. + /// + private float focusDistance = 0.7f; + + private const int MAX_CAMERAS = 16; //shaders dont have dynamic arrays and this is the max supported. change it here? change it in the shaders as well ... + private int internalCameraCount = 2; + private int oldRenderResolutionScale = 100; + + private static object shaderLock = new object(); + + private Camera mainCamera; + private List cameras = null; + private GameObject focusPlaneObject = null; + private GameObject cameraParent = null; + + private Material material; #if G3D_HDRP - private HDAdditionalCameraData.AntialiasingMode antialiasingMode = HDAdditionalCameraData - .AntialiasingMode - .None; + private HDAdditionalCameraData.AntialiasingMode antialiasingMode = HDAdditionalCameraData + .AntialiasingMode + .None; #endif #if G3D_URP - private G3D.RenderPipeline.URP.ScriptableRP customPass; - private AntialiasingMode antialiasingMode = AntialiasingMode.None; + private G3D.RenderPipeline.URP.ScriptableRP customPass; + private AntialiasingMode antialiasingMode = AntialiasingMode.None; #endif - private ShaderHandles shaderHandles; - private G3DShaderParameters shaderParameters; + private ShaderHandles shaderHandles; + private G3DShaderParameters shaderParameters; - private Vector2Int cachedWindowPosition; - private Vector2Int cachedWindowSize; + private Vector2Int cachedWindowPosition; + private Vector2Int cachedWindowSize; - // half of the width of field of view at start at focus distance - private float halfCameraWidthAtStart = 1.0f; + // half of the width of field of view at start at focus distance + private float halfCameraWidthAtStart = 1.0f; - /// - /// This value is calculated based on the calibration file - /// - private float baseFieldOfView = 16.0f; + /// + /// This value is calculated based on the calibration file + /// + private float baseFieldOfView = 16.0f; - private bool showTestFrame = false; + private bool showTestFrame = false; - /// - /// Focus distance scaled by scene scale factor. - /// - public float scaledFocusDistance - { - get { return focusDistance * sceneScaleFactor; } - } + /// + /// Focus distance scaled by scene scale factor. + /// + public float scaledFocusDistance + { + get { return focusDistance * sceneScaleFactor; } + } - public float scaledFocusDistanceAndDolly - { - get + public float scaledFocusDistanceAndDolly { - float dollyZoomFactor = scaledFocusDistance - scaledFocusDistance * dollyZoom; - float focusDistanceWithDollyZoom = scaledFocusDistance - dollyZoomFactor; - return focusDistanceWithDollyZoom; + get + { + float dollyZoomFactor = scaledFocusDistance - scaledFocusDistance * dollyZoom; + float focusDistanceWithDollyZoom = scaledFocusDistance - dollyZoomFactor; + return focusDistanceWithDollyZoom; + } } - } - private float scaledViewSeparation - { - get { return viewSeparation * sceneScaleFactor * viewOffsetScale; } - } + private float scaledViewSeparation + { + get { return viewSeparation * sceneScaleFactor * viewOffsetScale; } + } - private float scaledHalfCameraWidthAtStart - { - get { return halfCameraWidthAtStart * sceneScaleFactor; } - } + private float scaledHalfCameraWidthAtStart + { + get { return halfCameraWidthAtStart * sceneScaleFactor; } + } - #endregion + #endregion - private Material viewGenerationMaterial; + private Material viewGenerationMaterial; - private RenderTexture[] colorRenderTextures = null; - private int mainCamCullingMask = -1; - private float originalMainFOV; - private CameraClearFlags originalMainClearFlags; + private RenderTexture[] colorRenderTextures = null; + private int mainCamCullingMask = -1; + private float originalMainFOV; + private CameraClearFlags originalMainClearFlags; #if G3D_HDRP - private G3D.RenderPipeline.HDRP.CustomPassController customPassController; + private G3D.RenderPipeline.HDRP.CustomPassController customPassController; #endif - private bool mainCamInactiveLastFrame = false; + private bool mainCamInactiveLastFrame = false; - #region Initialization + #region Initialization - void Awake() - { - InitMainCamera(); - } + void Awake() + { + InitMainCamera(); + } - void Start() - { - previousValues.init(); + void Start() + { + previousValues.init(); - oldRenderResolutionScale = renderResolutionScale; - setupCameras(); + oldRenderResolutionScale = renderResolutionScale; + setupCameras(); - reinitializeShader(); + reinitializeShader(); #if G3D_HDRP - customPassController = - gameObject.AddComponent(); - viewGenerationMaterial = customPassController.init( - internalCameraCount, - cameras, - mainCamCullingMask, - ref material, - renderResolutionScale - ); + customPassController = + gameObject.AddComponent(); + viewGenerationMaterial = customPassController.init( + internalCameraCount, + cameras, + mainCamCullingMask, + ref material, + renderResolutionScale + ); #endif #if G3D_URP - customPass = new G3D.RenderPipeline.URP.ScriptableRP(material); - antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing; - mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None; + customPass = new G3D.RenderPipeline.URP.ScriptableRP(material); + antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing; + mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None; #endif - shaderHandles = new ShaderHandles(); - shaderHandles.init(); - - headtrackingConnection = new HeadtrackingConnection( - focusDistance, - headTrackingScale, - sceneScaleFactor, - calibrationPathOverwrite, - this, - debugMessages, - headPositionFilter, - latencyCorrectionMode - ); - if (mode == G3DCameraMode.DIORAMA) - { - headtrackingConnection.initLibrary(); - headtrackingConnection.startHeadTracking(); - } + shaderHandles = new ShaderHandles(); + shaderHandles.init(); + + headtrackingConnection = new HeadtrackingConnection( + focusDistance, + headTrackingScale, + sceneScaleFactor, + calibrationPathOverwrite, + this, + debugMessages, + headPositionFilter, + latencyCorrectionMode + ); + if (mode == G3DCameraMode.DIORAMA) + { + headtrackingConnection.initLibrary(); + headtrackingConnection.startHeadTracking(); + } - updateScreenViewportProperties(); + updateScreenViewportProperties(); - loadShaderParametersFromCalibrationFile(); - updateShaderParameters(); + loadShaderParametersFromCalibrationFile(); + updateShaderParameters(); - updateCameras(); - updateShaderRenderTextures(); + updateCameras(); + updateShaderRenderTextures(); - // This has to be done after the cameras are updated - cachedWindowPosition = new Vector2Int( - Screen.mainWindowPosition.x, - Screen.mainWindowPosition.y - ); - cachedWindowSize = new Vector2Int(Screen.width, Screen.height); + // This has to be done after the cameras are updated + cachedWindowPosition = new Vector2Int( + Screen.mainWindowPosition.x, + Screen.mainWindowPosition.y + ); + cachedWindowSize = new Vector2Int(Screen.width, Screen.height); - indexMap.UpdateIndexMap( - getCameraCountFromCalibrationFile(), - internalCameraCount, - indexMapYoyoStart, - invertIndexMap, - invertIndexMapIndices - ); - } + indexMap.UpdateIndexMap( + getCameraCountFromCalibrationFile(), + internalCameraCount, + indexMapYoyoStart, + invertIndexMap, + invertIndexMapIndices + ); + } - void OnApplicationQuit() - { - headtrackingConnection.deinitLibrary(); - } + void OnApplicationQuit() + { + headtrackingConnection.deinitLibrary(); + } - private void OnEnable() - { - InitMainCamera(); + private void OnEnable() + { + InitMainCamera(); - mainCamera.cullingMask = 0; //disable rendering of the main camera - mainCamera.clearFlags = CameraClearFlags.Color; + mainCamera.cullingMask = 0; //disable rendering of the main camera + mainCamera.clearFlags = CameraClearFlags.Color; #if G3D_URP - RenderPipelineManager.beginCameraRendering += OnBeginCamera; + RenderPipelineManager.beginCameraRendering += OnBeginCamera; #endif - } + } - public void OnDisable() - { - if (cameras != null && cameras.Count > 0) + public void OnDisable() { - // disable all cameras when the script is disabled - for (int i = 0; i < MAX_CAMERAS; i++) + if (cameras != null && cameras.Count > 0) { - cameras[i]?.gameObject.SetActive(false); + // disable all cameras when the script is disabled + for (int i = 0; i < MAX_CAMERAS; i++) + { + cameras[i]?.gameObject.SetActive(false); + } } - } - mainCamera.cullingMask = mainCamCullingMask; - mainCamera.fieldOfView = originalMainFOV; - mainCamera.clearFlags = originalMainClearFlags; + mainCamera.cullingMask = mainCamCullingMask; + mainCamera.fieldOfView = originalMainFOV; + mainCamera.clearFlags = originalMainClearFlags; #if G3D_URP - RenderPipelineManager.beginCameraRendering -= OnBeginCamera; + RenderPipelineManager.beginCameraRendering -= OnBeginCamera; #endif - } + } #if G3D_URP - private void OnBeginCamera(ScriptableRenderContext context, Camera cam) - { - // Use the EnqueuePass method to inject a custom render pass - cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass); - - if (mainCamera.GetUniversalAdditionalCameraData().renderPostProcessing) + private void OnBeginCamera(ScriptableRenderContext context, Camera cam) { - for (int i = 0; i < MAX_CAMERAS; i++) + // Use the EnqueuePass method to inject a custom render pass + cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass); + + if (mainCamera.GetUniversalAdditionalCameraData().renderPostProcessing) { - cameras[i].GetUniversalAdditionalCameraData().renderPostProcessing = true; + for (int i = 0; i < MAX_CAMERAS; i++) + { + cameras[i].GetUniversalAdditionalCameraData().renderPostProcessing = true; + } } } - } #endif - /// - /// OnValidate gets called every time the script is changed in the editor. - /// This is used to react to changes made to the parameters. - /// - void OnValidate() - { - if (enabled == false) - { - // do not run this code if the script is not enabled - return; - } - - // reset cameras if calibration file or scene scale factor changed - if ( - calibrationFile != previousValues.calibrationFile - || previousValues.sceneScaleFactor != sceneScaleFactor - ) + /// + /// OnValidate gets called every time the script is changed in the editor. + /// This is used to react to changes made to the parameters. + /// + void OnValidate() { - previousValues.calibrationFile = calibrationFile; - setupCameras(true); - } + if (enabled == false) + { + // do not run this code if the script is not enabled + return; + } - // update cached calibration file if it changed - if (calibrationFile != previousValues.calibrationFile) - { - previousValues.calibrationFile = calibrationFile; - } + // reset cameras if calibration file or scene scale factor changed + if ( + calibrationFile != previousValues.calibrationFile + || previousValues.sceneScaleFactor != sceneScaleFactor + ) + { + previousValues.calibrationFile = calibrationFile; + setupCameras(true); + } - // update cached scene scale factor if it changed - if (previousValues.sceneScaleFactor != sceneScaleFactor) - { - if (headtrackingConnection != null) + // update cached calibration file if it changed + if (calibrationFile != previousValues.calibrationFile) { - headtrackingConnection.sceneScaleFactor = sceneScaleFactor; + previousValues.calibrationFile = calibrationFile; } - previousValues.sceneScaleFactor = sceneScaleFactor; - } - if (previousValues.mode != mode) - { - previousValues.mode = mode; - if (mode == G3DCameraMode.MULTIVIEW) + // update cached scene scale factor if it changed + if (previousValues.sceneScaleFactor != sceneScaleFactor) { - CalibrationProvider calibration = CalibrationProvider.getFromString( - calibrationFile.text - ); - internalCameraCount = getCameraCountFromCalibrationFile(calibration); - if (generateViews) + if (headtrackingConnection != null) { - // TODO DO NOT HARD CODE THIS VALUE! - internalCameraCount = 16; + headtrackingConnection.sceneScaleFactor = sceneScaleFactor; } - loadMultiviewViewSeparationFromCalibration(calibration); + previousValues.sceneScaleFactor = sceneScaleFactor; } - else + + if (previousValues.mode != mode) { - viewSeparation = 0.065f; + previousValues.mode = mode; + if (mode == G3DCameraMode.MULTIVIEW) + { + CalibrationProvider calibration = CalibrationProvider.getFromString( + calibrationFile.text + ); + internalCameraCount = getCameraCountFromCalibrationFile(calibration); + if (generateViews) + { + // TODO DO NOT HARD CODE THIS VALUE! + internalCameraCount = 16; + } + loadMultiviewViewSeparationFromCalibration(calibration); + } + else + { + viewSeparation = 0.065f; + } + + updateCameraCountBasedOnMode(); } - updateCameraCountBasedOnMode(); + if ( + previousValues.indexMapYoyoStart != indexMapYoyoStart + || previousValues.invertIndexMap != invertIndexMap + || previousValues.invertIndexMapIndices != invertIndexMapIndices + ) + { + previousValues.indexMapYoyoStart = indexMapYoyoStart; + previousValues.invertIndexMap = invertIndexMap; + previousValues.invertIndexMapIndices = invertIndexMapIndices; + + indexMap.UpdateIndexMap( + getCameraCountFromCalibrationFile(), + internalCameraCount, + indexMapYoyoStart, + invertIndexMap, + invertIndexMapIndices + ); + } } - if ( - previousValues.indexMapYoyoStart != indexMapYoyoStart - || previousValues.invertIndexMap != invertIndexMap - || previousValues.invertIndexMapIndices != invertIndexMapIndices - ) + /// + /// Us this to run OnValidate after parameters changed from a script. + /// + public void Validate() { - previousValues.indexMapYoyoStart = indexMapYoyoStart; - previousValues.invertIndexMap = invertIndexMap; - previousValues.invertIndexMapIndices = invertIndexMapIndices; - - indexMap.UpdateIndexMap( - getCameraCountFromCalibrationFile(), - internalCameraCount, - indexMapYoyoStart, - invertIndexMap, - invertIndexMapIndices - ); + OnValidate(); } - } - /// - /// Us this to run OnValidate after parameters changed from a script. - /// - public void Validate() - { - OnValidate(); - } - - public void loadShaderParametersFromCalibrationFile() - { - if (calibrationFile == null) + public void loadShaderParametersFromCalibrationFile() { - Debug.LogError( - "No calibration file set. Please set a calibration file. Using default values." - ); - return; - } + if (calibrationFile == null) + { + Debug.LogError( + "No calibration file set. Please set a calibration file. Using default values." + ); + return; + } - lock (shaderLock) - { - CalibrationProvider calibrationProvider = CalibrationProvider.getFromString( - calibrationFile.text - ); - shaderParameters = calibrationProvider.getShaderParameters(); + lock (shaderLock) + { + CalibrationProvider calibrationProvider = CalibrationProvider.getFromString( + calibrationFile.text + ); + shaderParameters = calibrationProvider.getShaderParameters(); + } } - } - /// - /// Updates all camera parameters based on the calibration file (i.e. focus distance, fov, etc.). - /// Includes shader parameters (i.e. lense shear angle, camera count, etc.). - /// - /// Updates calibration file as well. - /// - public void setupCameras(TextAsset calibrationFile, bool calledFromValidate = false) - { - this.calibrationFile = calibrationFile; - setupCameras(calledFromValidate); - } - - /// - /// Sets up all camera parameters based on the calibration file (i.e. focus distance, fov, etc.). - /// Includes shader parameters (i.e. lense shear angle, camera count, etc.). - /// - /// Does not update calibration file. - /// - public void setupCameras(bool calledFromValidate = false) - { - if (mainCamera == null) - { - InitMainCamera(); - } - if (Application.isPlaying && !calledFromValidate) + /// + /// Updates all camera parameters based on the calibration file (i.e. focus distance, fov, etc.). + /// Includes shader parameters (i.e. lense shear angle, camera count, etc.). + /// + /// Updates calibration file as well. + /// + public void setupCameras(TextAsset calibrationFile, bool calledFromValidate = false) { - // only run this code if not in editor mode (this function (setupCameras()) is called from OnValidate as well -> from editor ui) - initCamerasAndParents(); + this.calibrationFile = calibrationFile; + setupCameras(calledFromValidate); } - if (calibrationFile == null) + + /// + /// Sets up all camera parameters based on the calibration file (i.e. focus distance, fov, etc.). + /// Includes shader parameters (i.e. lense shear angle, camera count, etc.). + /// + /// Does not update calibration file. + /// + public void setupCameras(bool calledFromValidate = false) { - Debug.LogError( - "No calibration file set. Please set a calibration file. Using default values." - ); - mainCamera.fieldOfView = 16.0f; - SetFocusDistance(0.7f); - viewSeparation = 0.065f; - if (mode == G3DCameraMode.MULTIVIEW) + if (mainCamera == null) { - viewSeparation = 0.031f; + InitMainCamera(); } - if (focusPlaneObject != null) + if (Application.isPlaying && !calledFromValidate) { - focusPlaneObject.transform.localPosition = new Vector3(0, 0, scaledFocusDistance); + // only run this code if not in editor mode (this function (setupCameras()) is called from OnValidate as well -> from editor ui) + initCamerasAndParents(); + } + if (calibrationFile == null) + { + Debug.LogError( + "No calibration file set. Please set a calibration file. Using default values." + ); + mainCamera.fieldOfView = 16.0f; + SetFocusDistance(0.7f); + viewSeparation = 0.065f; + if (mode == G3DCameraMode.MULTIVIEW) + { + viewSeparation = 0.031f; + } + if (focusPlaneObject != null) + { + focusPlaneObject.transform.localPosition = new Vector3( + 0, + 0, + scaledFocusDistance + ); + } + return; } - return; - } - // load values from calibration file - CalibrationProvider calibration = CalibrationProvider.getFromString(calibrationFile.text); - int BasicWorkingDistanceMM = calibration.getInt("BasicWorkingDistanceMM"); - float PhysicalSizeInch = calibration.getFloat("PhysicalSizeInch"); - int NativeViewcount = calibration.getInt("NativeViewcount"); - int HorizontalResolution = calibration.getInt("HorizontalResolution"); - int VerticalResolution = calibration.getInt("VerticalResolution"); - - // calculate intermediate values - float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f; - float physicalSizeInMeter = PhysicalSizeInch * 0.0254f; - float aspectRatio = (float)HorizontalResolution / (float)VerticalResolution; - float FOV = - 2 * Mathf.Atan(physicalSizeInMeter / 2.0f / BasicWorkingDistanceMeter) * Mathf.Rad2Deg; - - // set focus distance - SetFocusDistance((float)BasicWorkingDistanceMeter); - - // set camera fov - baseFieldOfView = Camera.HorizontalToVerticalFieldOfView(FOV, aspectRatio); - mainCamera.fieldOfView = baseFieldOfView; - // calculate eye separation/ view separation - if (mode == G3DCameraMode.MULTIVIEW) - { - loadMultiviewViewSeparationFromCalibration(calibration); - // TODO DO NOT HARD CODE THIS VALUE! - if (generateViews) + // load values from calibration file + CalibrationProvider calibration = CalibrationProvider.getFromString( + calibrationFile.text + ); + int BasicWorkingDistanceMM = calibration.getInt("BasicWorkingDistanceMM"); + float PhysicalSizeInch = calibration.getFloat("PhysicalSizeInch"); + int NativeViewcount = calibration.getInt("NativeViewcount"); + int HorizontalResolution = calibration.getInt("HorizontalResolution"); + int VerticalResolution = calibration.getInt("VerticalResolution"); + + // calculate intermediate values + float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f; + float physicalSizeInMeter = PhysicalSizeInch * 0.0254f; + float aspectRatio = (float)HorizontalResolution / (float)VerticalResolution; + float FOV = + 2 + * Mathf.Atan(physicalSizeInMeter / 2.0f / BasicWorkingDistanceMeter) + * Mathf.Rad2Deg; + + // set focus distance + SetFocusDistance((float)BasicWorkingDistanceMeter); + + // set camera fov + baseFieldOfView = Camera.HorizontalToVerticalFieldOfView(FOV, aspectRatio); + mainCamera.fieldOfView = baseFieldOfView; + // calculate eye separation/ view separation + if (mode == G3DCameraMode.MULTIVIEW) { - internalCameraCount = 16; // default value for multiview mode + loadMultiviewViewSeparationFromCalibration(calibration); + // TODO DO NOT HARD CODE THIS VALUE! + if (generateViews) + { + internalCameraCount = 16; // default value for multiview mode + } + else + { + internalCameraCount = NativeViewcount; + } } else { - internalCameraCount = NativeViewcount; + viewSeparation = 0.065f; } - } - else - { - viewSeparation = 0.065f; - } - updateCameraCountBasedOnMode(); + updateCameraCountBasedOnMode(); - // only run this code if not in editor mode (this function (setupCameras()) is called from OnValidate as well -> from editor ui) - if (Application.isPlaying) - { - // update focus plane distance - if (focusPlaneObject != null) - { - focusPlaneObject.transform.localPosition = new Vector3(0, 0, scaledFocusDistance); - } - if (cameraParent != null) + // only run this code if not in editor mode (this function (setupCameras()) is called from OnValidate as well -> from editor ui) + if (Application.isPlaying) { - cameraParent.transform.localPosition = new Vector3(0, 0, -scaledFocusDistance); + // update focus plane distance + if (focusPlaneObject != null) + { + focusPlaneObject.transform.localPosition = new Vector3( + 0, + 0, + scaledFocusDistance + ); + } + if (cameraParent != null) + { + cameraParent.transform.localPosition = new Vector3(0, 0, -scaledFocusDistance); + } } + + halfCameraWidthAtStart = + Mathf.Tan(mainCamera.fieldOfView * Mathf.Deg2Rad / 2) * focusDistance; + + loadShaderParametersFromCalibrationFile(); } - halfCameraWidthAtStart = - Mathf.Tan(mainCamera.fieldOfView * Mathf.Deg2Rad / 2) * focusDistance; + public void updateShaderRenderTextures() + { + if (material == null) + return; + if (cameras == null) + return; - loadShaderParametersFromCalibrationFile(); - } + //prevent any memory leaks + for (int i = 0; i < MAX_CAMERAS; i++) + cameras[i].targetTexture?.Release(); - public void updateShaderRenderTextures() - { - if (material == null) - return; - if (cameras == null) - return; + for (int i = 0; i < colorRenderTextures?.Length; i++) + { + if (colorRenderTextures[i] != null) + colorRenderTextures[i].Release(); + } - //prevent any memory leaks - for (int i = 0; i < MAX_CAMERAS; i++) - cameras[i].targetTexture?.Release(); + colorRenderTextures = new RenderTexture[internalCameraCount]; - for (int i = 0; i < colorRenderTextures?.Length; i++) - { - if (colorRenderTextures[i] != null) - colorRenderTextures[i].Release(); + if (generateViews) + { + addRenderTextureToCamera(colorRenderTextures, 0, 0, "_leftCamTex"); // left camera + addRenderTextureToCamera( + colorRenderTextures, + 1, + internalCameraCount / 2, + "_middleCamTex" + ); // middle camera + addRenderTextureToCamera( + colorRenderTextures, + 2, + internalCameraCount - 1, + "_rightCamTex" + ); // right camera + } + else + { + //set only those we need + for (int i = 0; i < internalCameraCount; i++) + { + addRenderTextureToCamera(colorRenderTextures, i, i); + } + } } - colorRenderTextures = new RenderTexture[internalCameraCount]; - - if (generateViews) + public void logCameraPositionsToFile() { - addRenderTextureToCamera(colorRenderTextures, 0, 0, "_leftCamTex"); // left camera - addRenderTextureToCamera( - colorRenderTextures, - 1, - internalCameraCount / 2, - "_middleCamTex" - ); // middle camera - addRenderTextureToCamera( - colorRenderTextures, - 2, - internalCameraCount - 1, - "_rightCamTex" - ); // right camera + headtrackingConnection.logCameraPositionsToFile(); } - else + + public void shiftViewToLeft() { - //set only those we need - for (int i = 0; i < internalCameraCount; i++) + if (mode == G3DCameraMode.MULTIVIEW) { - addRenderTextureToCamera(colorRenderTextures, i, i); + return; } + headtrackingConnection.shiftViewToLeft(); } - } - public void logCameraPositionsToFile() - { - headtrackingConnection.logCameraPositionsToFile(); - } - - public void shiftViewToLeft() - { - if (mode == G3DCameraMode.MULTIVIEW) + public void shiftViewToRight() { - return; + if (mode == G3DCameraMode.MULTIVIEW) + { + return; + } + headtrackingConnection.shiftViewToRight(); } - headtrackingConnection.shiftViewToLeft(); - } - public void shiftViewToRight() - { - if (mode == G3DCameraMode.MULTIVIEW) + public void toggleTestFrame() { - return; + showTestFrame = !showTestFrame; } - headtrackingConnection.shiftViewToRight(); - } - public void toggleTestFrame() - { - showTestFrame = !showTestFrame; - } - - public void toggleHeadTracking() - { - if (mode == G3DCameraMode.MULTIVIEW) + public void toggleHeadTracking() { - return; + if (mode == G3DCameraMode.MULTIVIEW) + { + return; + } + headtrackingConnection.toggleHeadTracking(); } - headtrackingConnection.toggleHeadTracking(); - } - public G3DShaderParameters GetShaderParameters() - { - lock (shaderLock) + public G3DShaderParameters GetShaderParameters() { - return shaderParameters; + lock (shaderLock) + { + return shaderParameters; + } } - } - public void setShaderParameters(G3DShaderParameters parameters) - { - lock (shaderLock) + public void setShaderParameters(G3DShaderParameters parameters) { - shaderParameters = parameters; + lock (shaderLock) + { + shaderParameters = parameters; + } } - } - - public void setOriginalFOV(float fov) - { - originalMainFOV = fov; - } - private void SetFocusDistance(float distance) - { - focusDistance = distance; - if (headtrackingConnection != null) + public void setOriginalFOV(float fov) { - headtrackingConnection.focusDistance = distance; + originalMainFOV = fov; } - } - private void InitMainCamera() - { - if (mainCamera != null) + private void SetFocusDistance(float distance) { - return; + focusDistance = distance; + if (headtrackingConnection != null) + { + headtrackingConnection.focusDistance = distance; + } } - mainCamera = GetComponent(); - mainCamCullingMask = mainCamera.cullingMask; - originalMainFOV = mainCamera.fieldOfView; - originalMainClearFlags = mainCamera.clearFlags; - } - private void loadMultiviewViewSeparationFromCalibration(CalibrationProvider calibration) - { - if (mode != G3DCameraMode.MULTIVIEW) + private void InitMainCamera() { - return; + if (mainCamera != null) + { + return; + } + mainCamera = GetComponent(); + mainCamCullingMask = mainCamera.cullingMask; + originalMainFOV = mainCamera.fieldOfView; + originalMainClearFlags = mainCamera.clearFlags; } - int BasicWorkingDistanceMM = calibration.getInt("BasicWorkingDistanceMM"); - int NativeViewcount = calibration.getInt("NativeViewcount"); - float ApertureAngle = 14.0f; - try + private void loadMultiviewViewSeparationFromCalibration(CalibrationProvider calibration) { - ApertureAngle = calibration.getFloat("ApertureAngle"); - } - catch (Exception e) - { - Debug.LogWarning(e.Message); - } + if (mode != G3DCameraMode.MULTIVIEW) + { + return; + } - float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f; - float halfZoneOpeningAngleRad = ApertureAngle * Mathf.Deg2Rad / 2.0f; - float halfWidthZoneAtbasicDistance = - Mathf.Tan(halfZoneOpeningAngleRad) * BasicWorkingDistanceMeter; + int BasicWorkingDistanceMM = calibration.getInt("BasicWorkingDistanceMM"); + int NativeViewcount = calibration.getInt("NativeViewcount"); + float ApertureAngle = 14.0f; + try + { + ApertureAngle = calibration.getFloat("ApertureAngle"); + } + catch (Exception e) + { + Debug.LogWarning(e.Message); + } - // calculate eye separation/ view separation - if (mode == G3DCameraMode.MULTIVIEW) - { - viewSeparation = halfWidthZoneAtbasicDistance * 2 / NativeViewcount; - } - } + float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f; + float halfZoneOpeningAngleRad = ApertureAngle * Mathf.Deg2Rad / 2.0f; + float halfWidthZoneAtbasicDistance = + Mathf.Tan(halfZoneOpeningAngleRad) * BasicWorkingDistanceMeter; - /// - /// Initializes the cameras and their parents. - /// if cameras are already initialized, this function only returns the focus plane. - /// - /// - /// The focus plane game object. - /// - private void initCamerasAndParents() - { - if (focusPlaneObject == null) - { - focusPlaneObject = new GameObject("focus plane center"); - focusPlaneObject.transform.parent = transform; - focusPlaneObject.transform.localPosition = new Vector3(0, 0, scaledFocusDistance); - focusPlaneObject.transform.localRotation = Quaternion.identity; + // calculate eye separation/ view separation + if (mode == G3DCameraMode.MULTIVIEW) + { + viewSeparation = halfWidthZoneAtbasicDistance * 2 / NativeViewcount; + } } - //initialize cameras - if (cameraParent == null) + /// + /// Initializes the cameras and their parents. + /// if cameras are already initialized, this function only returns the focus plane. + /// + /// + /// The focus plane game object. + /// + private void initCamerasAndParents() { - cameraParent = new GameObject("g3dcams"); - cameraParent.transform.parent = focusPlaneObject.transform; - cameraParent.transform.localPosition = new Vector3(0, 0, -scaledFocusDistance); - cameraParent.transform.localRotation = Quaternion.identity; - } + if (focusPlaneObject == null) + { + focusPlaneObject = new GameObject("focus plane center"); + focusPlaneObject.transform.parent = transform; + focusPlaneObject.transform.localPosition = new Vector3(0, 0, scaledFocusDistance); + focusPlaneObject.transform.localRotation = Quaternion.identity; + } - if (cameras == null) - { - cameras = new List(); - for (int i = 0; i < MAX_CAMERAS; i++) + //initialize cameras + if (cameraParent == null) { - cameras.Add(new GameObject(CAMERA_NAME_PREFIX + i).AddComponent()); - cameras[i].transform.SetParent(cameraParent.transform, true); - cameras[i].gameObject.SetActive(false); - cameras[i].transform.localRotation = Quaternion.identity; - cameras[i].clearFlags = originalMainClearFlags; - cameras[i].backgroundColor = mainCamera.backgroundColor; - cameras[i].targetDisplay = mainCamera.targetDisplay; - cameras[i].cullingMask = mainCamCullingMask; + cameraParent = new GameObject("g3dcams"); + cameraParent.transform.parent = focusPlaneObject.transform; + cameraParent.transform.localPosition = new Vector3(0, 0, -scaledFocusDistance); + cameraParent.transform.localRotation = Quaternion.identity; + } - cameras[i].depth = mainCamera.depth - 1; // make sure the main camera renders on top of the secondary cameras + if (cameras == null) + { + cameras = new List(); + for (int i = 0; i < MAX_CAMERAS; i++) + { + cameras.Add(new GameObject(CAMERA_NAME_PREFIX + i).AddComponent()); + cameras[i].transform.SetParent(cameraParent.transform, true); + cameras[i].gameObject.SetActive(false); + cameras[i].transform.localRotation = Quaternion.identity; + cameras[i].clearFlags = originalMainClearFlags; + cameras[i].backgroundColor = mainCamera.backgroundColor; + cameras[i].targetDisplay = mainCamera.targetDisplay; + cameras[i].cullingMask = mainCamCullingMask; + + cameras[i].depth = mainCamera.depth - 1; // make sure the main camera renders on top of the secondary cameras #if G3D_HDRP - cameras[i].gameObject.AddComponent(); + cameras[i].gameObject.AddComponent(); #endif + } } } - } - private int getCameraCountFromCalibrationFile() - { - if (calibrationFile == null) + private int getCameraCountFromCalibrationFile() { - Debug.LogError( - "No calibration file set. Please set a calibration file. Using default values." + if (calibrationFile == null) + { + Debug.LogError( + "No calibration file set. Please set a calibration file. Using default values." + ); + return 2; + } + + // TODO do not recreate the calibration provider every time + // This gets called every frame in UpdateCameraCountBasedOnMode + CalibrationProvider calibration = CalibrationProvider.getFromString( + calibrationFile.text ); - return 2; + return getCameraCountFromCalibrationFile(calibration); } - // TODO do not recreate the calibration provider every time - // This gets called every frame in UpdateCameraCountBasedOnMode - CalibrationProvider calibration = CalibrationProvider.getFromString(calibrationFile.text); - return getCameraCountFromCalibrationFile(calibration); - } + private int getCameraCountFromCalibrationFile(CalibrationProvider calibration) + { + int NativeViewcount = calibration.getInt("NativeViewcount"); + return NativeViewcount; + } - private int getCameraCountFromCalibrationFile(CalibrationProvider calibration) - { - int NativeViewcount = calibration.getInt("NativeViewcount"); - return NativeViewcount; - } + private Vector2Int getDisplayResolutionFromCalibrationFile() + { + if (calibrationFile == null) + { + Debug.LogError( + "No calibration file set. Please set a calibration file. Using default values." + ); + return new Vector2Int(1920, 1080); + } - private Vector2Int getDisplayResolutionFromCalibrationFile() - { - if (calibrationFile == null) - { - Debug.LogError( - "No calibration file set. Please set a calibration file. Using default values." + CalibrationProvider calibration = CalibrationProvider.getFromString( + calibrationFile.text ); - return new Vector2Int(1920, 1080); + int HorizontalResolution = calibration.getInt("HorizontalResolution"); + int VerticalResolution = calibration.getInt("VerticalResolution"); + return new Vector2Int(HorizontalResolution, VerticalResolution); } - CalibrationProvider calibration = CalibrationProvider.getFromString(calibrationFile.text); - int HorizontalResolution = calibration.getInt("HorizontalResolution"); - int VerticalResolution = calibration.getInt("VerticalResolution"); - return new Vector2Int(HorizontalResolution, VerticalResolution); - } - - private void reinitializeShader() - { - if (mode == G3DCameraMode.MULTIVIEW) + private void reinitializeShader() { - if (generateViews) + if (mode == G3DCameraMode.MULTIVIEW) { - material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); + if (generateViews) + { + material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); + } + else + { + material = new Material(Shader.Find("G3D/AutostereoMultiview")); + } } else { - material = new Material(Shader.Find("G3D/AutostereoMultiview")); + material = new Material(Shader.Find("G3D/Autostereo")); } } - else - { - material = new Material(Shader.Find("G3D/Autostereo")); - } - } - #endregion - - #region Updates - void Update() - { - bool windowMovedLastFrame = windowMoved(); - bool windowResizedLastFrame = windowResized(); + #endregion - if (mainCamera.enabled == false) + #region Updates + void Update() { - // enable all cameras if main camera is disabled - for (int i = 0; i < internalCameraCount; i++) + bool windowMovedLastFrame = windowMoved(); + bool windowResizedLastFrame = windowResized(); + + if (mainCamera.enabled == false) { - cameras[i].gameObject.SetActive(false); + // enable all cameras if main camera is disabled + for (int i = 0; i < internalCameraCount; i++) + { + cameras[i].gameObject.SetActive(false); + } + mainCamInactiveLastFrame = true; + return; } - mainCamInactiveLastFrame = true; - return; - } - bool recreatedRenderTextures = false; + bool recreatedRenderTextures = false; - if (mainCamInactiveLastFrame) - { - // recreate shader render textures when main camera was inactive last frame - recreatedRenderTextures = true; - } + if (mainCamInactiveLastFrame) + { + // recreate shader render textures when main camera was inactive last frame + recreatedRenderTextures = true; + } - mainCamInactiveLastFrame = false; + mainCamInactiveLastFrame = false; - // update the shader parameters (only in diorama mode) - if (mode == G3DCameraMode.DIORAMA) - { - headtrackingConnection.calculateShaderParameters(); - } + // update the shader parameters (only in diorama mode) + if (mode == G3DCameraMode.DIORAMA) + { + headtrackingConnection.calculateShaderParameters(); + } - bool cameraCountChanged = updateCameraCountBasedOnMode(); - updateCameras(); - updateShaderParameters(); + bool cameraCountChanged = updateCameraCountBasedOnMode(); + updateCameras(); + updateShaderParameters(); - if (windowResizedLastFrame || windowMovedLastFrame) - { - updateScreenViewportProperties(); - } + if (windowResizedLastFrame || windowMovedLastFrame) + { + updateScreenViewportProperties(); + } - if (windowResizedLastFrame) - { - recreatedRenderTextures = true; - } + if (windowResizedLastFrame) + { + recreatedRenderTextures = true; + } - if (cameraCountChanged || oldRenderResolutionScale != renderResolutionScale) - { - oldRenderResolutionScale = renderResolutionScale; - recreatedRenderTextures = true; - } + if (cameraCountChanged || oldRenderResolutionScale != renderResolutionScale) + { + oldRenderResolutionScale = renderResolutionScale; + recreatedRenderTextures = true; + } #if G3D_HDRP - customPassController.cameraCountChanged = cameraCountChanged; - customPassController.resolutionScaleChanged = - oldRenderResolutionScale != renderResolutionScale; - customPassController.debugRendering = debugRendering; + customPassController.cameraCountChanged = cameraCountChanged; + customPassController.resolutionScaleChanged = + oldRenderResolutionScale != renderResolutionScale; + customPassController.debugRendering = debugRendering; #endif - if (recreatedRenderTextures) - { - updateShaderRenderTextures(); + if (recreatedRenderTextures) + { + updateShaderRenderTextures(); + } } - } - private void updateScreenViewportProperties() - { - Vector2Int displayResolution = getDisplayResolutionFromCalibrationFile(); - if (mode == G3DCameraMode.MULTIVIEW) - { - shaderParameters.screenWidth = displayResolution.x; - shaderParameters.screenHeight = displayResolution.y; - shaderParameters.leftViewportPosition = Screen.mainWindowPosition.x; - shaderParameters.bottomViewportPosition = Screen.mainWindowPosition.y + Screen.height; - } - else + private void updateScreenViewportProperties() { - headtrackingConnection.updateScreenViewportProperties(displayResolution); - } + Vector2Int displayResolution = getDisplayResolutionFromCalibrationFile(); + if (mode == G3DCameraMode.MULTIVIEW) + { + shaderParameters.screenWidth = displayResolution.x; + shaderParameters.screenHeight = displayResolution.y; + shaderParameters.leftViewportPosition = Screen.mainWindowPosition.x; + shaderParameters.bottomViewportPosition = + Screen.mainWindowPosition.y + Screen.height; + } + else + { + headtrackingConnection.updateScreenViewportProperties(displayResolution); + } - // this parameter is used in the shader to invert the y axis - material?.SetInt(Shader.PropertyToID("viewportHeight"), Screen.height); - material?.SetInt(Shader.PropertyToID("viewportWidth"), Screen.width); - } + // this parameter is used in the shader to invert the y axis + material?.SetInt(Shader.PropertyToID("viewportHeight"), Screen.height); + material?.SetInt(Shader.PropertyToID("viewportWidth"), Screen.width); + } - private void updateShaderParameters() - { - lock (shaderLock) + private void updateShaderParameters() { - material?.SetInt( - shaderHandles.leftViewportPosition, - shaderParameters.leftViewportPosition - ); - material?.SetInt( - shaderHandles.bottomViewportPosition, - shaderParameters.bottomViewportPosition - ); - material?.SetInt(shaderHandles.screenWidth, shaderParameters.screenWidth); - material?.SetInt(shaderHandles.screenHeight, shaderParameters.screenHeight); - material?.SetInt(shaderHandles.nativeViewCount, shaderParameters.nativeViewCount); - material?.SetInt( - shaderHandles.angleRatioNumerator, - shaderParameters.angleRatioNumerator - ); - material?.SetInt( - shaderHandles.angleRatioDenominator, - shaderParameters.angleRatioDenominator - ); - material?.SetInt( - shaderHandles.leftLensOrientation, - shaderParameters.leftLensOrientation - ); - material?.SetInt(shaderHandles.mstart, shaderParameters.mstart); - - // test frame and stripe - material?.SetInt(shaderHandles.showTestFrame, showTestFrame ? 1 : 0); - material?.SetInt(shaderHandles.showTestStripe, shaderParameters.showTestStripe); - - material?.SetInt(shaderHandles.testGapWidth, shaderParameters.testGapWidth); - material?.SetInt(shaderHandles.track, shaderParameters.track); - material?.SetInt(shaderHandles.hqViewCount, shaderParameters.hqViewCount); - material?.SetInt(shaderHandles.hviews1, shaderParameters.hviews1); - material?.SetInt(shaderHandles.hviews2, shaderParameters.hviews2); - material?.SetInt(shaderHandles.blur, shaderParameters.blur); - material?.SetInt(shaderHandles.blackBorder, shaderParameters.blackBorder); - material?.SetInt(shaderHandles.blackSpace, shaderParameters.blackSpace); - material?.SetInt(shaderHandles.bls, shaderParameters.bls); - material?.SetInt(shaderHandles.ble, shaderParameters.ble); - material?.SetInt(shaderHandles.brs, shaderParameters.brs); - material?.SetInt(shaderHandles.bre, shaderParameters.bre); - material?.SetInt(shaderHandles.zCorrectionValue, shaderParameters.zCorrectionValue); - material?.SetInt(shaderHandles.zCompensationValue, shaderParameters.zCompensationValue); - material?.SetInt(shaderHandles.BGRPixelLayout, shaderParameters.BGRPixelLayout); - - material?.SetInt(Shader.PropertyToID("cameraCount"), internalCameraCount); - - material?.SetInt(Shader.PropertyToID("mirror"), mirrorViews ? 1 : 0); - - if (mode == G3DCameraMode.MULTIVIEW) + lock (shaderLock) { - material.SetInt(Shader.PropertyToID("indexMapLength"), indexMap.currentMap.Length); - material.SetFloatArray( - Shader.PropertyToID("index_map"), - indexMap.getPaddedIndexMapArray() + material?.SetInt( + shaderHandles.leftViewportPosition, + shaderParameters.leftViewportPosition + ); + material?.SetInt( + shaderHandles.bottomViewportPosition, + shaderParameters.bottomViewportPosition + ); + material?.SetInt(shaderHandles.screenWidth, shaderParameters.screenWidth); + material?.SetInt(shaderHandles.screenHeight, shaderParameters.screenHeight); + material?.SetInt(shaderHandles.nativeViewCount, shaderParameters.nativeViewCount); + material?.SetInt( + shaderHandles.angleRatioNumerator, + shaderParameters.angleRatioNumerator ); + material?.SetInt( + shaderHandles.angleRatioDenominator, + shaderParameters.angleRatioDenominator + ); + material?.SetInt( + shaderHandles.leftLensOrientation, + shaderParameters.leftLensOrientation + ); + material?.SetInt(shaderHandles.mstart, shaderParameters.mstart); + + // test frame and stripe + material?.SetInt(shaderHandles.showTestFrame, showTestFrame ? 1 : 0); + material?.SetInt(shaderHandles.showTestStripe, shaderParameters.showTestStripe); + + material?.SetInt(shaderHandles.testGapWidth, shaderParameters.testGapWidth); + material?.SetInt(shaderHandles.track, shaderParameters.track); + material?.SetInt(shaderHandles.hqViewCount, shaderParameters.hqViewCount); + material?.SetInt(shaderHandles.hviews1, shaderParameters.hviews1); + material?.SetInt(shaderHandles.hviews2, shaderParameters.hviews2); + material?.SetInt(shaderHandles.blur, shaderParameters.blur); + material?.SetInt(shaderHandles.blackBorder, shaderParameters.blackBorder); + material?.SetInt(shaderHandles.blackSpace, shaderParameters.blackSpace); + material?.SetInt(shaderHandles.bls, shaderParameters.bls); + material?.SetInt(shaderHandles.ble, shaderParameters.ble); + material?.SetInt(shaderHandles.brs, shaderParameters.brs); + material?.SetInt(shaderHandles.bre, shaderParameters.bre); + material?.SetInt(shaderHandles.zCorrectionValue, shaderParameters.zCorrectionValue); + material?.SetInt( + shaderHandles.zCompensationValue, + shaderParameters.zCompensationValue + ); + material?.SetInt(shaderHandles.BGRPixelLayout, shaderParameters.BGRPixelLayout); - material?.SetInt(Shader.PropertyToID("viewOffset"), viewOffset); - } - else - { - if (invertViewsInDiorama) + material?.SetInt(Shader.PropertyToID("cameraCount"), internalCameraCount); + + material?.SetInt(Shader.PropertyToID("mirror"), mirrorViews ? 1 : 0); + + if (mode == G3DCameraMode.MULTIVIEW) { - material.SetInt(Shader.PropertyToID("invertViews"), 1); + material.SetInt( + Shader.PropertyToID("indexMapLength"), + indexMap.currentMap.Length + ); + material.SetFloatArray( + Shader.PropertyToID("index_map"), + indexMap.getPaddedIndexMapArray() + ); + + material?.SetInt(Shader.PropertyToID("viewOffset"), viewOffset); } else { - material.SetInt(Shader.PropertyToID("invertViews"), 0); + if (invertViewsInDiorama) + { + material.SetInt(Shader.PropertyToID("invertViews"), 1); + } + else + { + material.SetInt(Shader.PropertyToID("invertViews"), 0); + } } - } - material?.SetInt(Shader.PropertyToID("mosaic_rows"), 4); - material?.SetInt(Shader.PropertyToID("mosaic_columns"), 4); + material?.SetInt(Shader.PropertyToID("mosaic_rows"), 4); + material?.SetInt(Shader.PropertyToID("mosaic_columns"), 4); #if G3D_HDRP - if (generateViews) - { - viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); - viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); - } + if (generateViews) + { + viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_x"), 4); + viewGenerationMaterial.SetInt(Shader.PropertyToID("grid_size_y"), 4); + } #endif + } } - } - - private void updateCameras() - { - Vector3 targetPosition = new Vector3(0, 0, -scaledFocusDistance); // position for the camera center (base position from which all other cameras are offset) - float targetViewSeparation = 0.0f; - // calculate the camera center position and eye separation if head tracking and the diorama effect are enabled - if (mode == G3DCameraMode.DIORAMA) - { - headtrackingConnection.handleHeadTrackingState( - ref targetPosition, - ref targetViewSeparation, - scaledViewSeparation, - scaledFocusDistance - ); - } - else if (mode == G3DCameraMode.MULTIVIEW) + private void updateCameras() { - targetViewSeparation = scaledViewSeparation; - } + Vector3 targetPosition = new Vector3(0, 0, -scaledFocusDistance); // position for the camera center (base position from which all other cameras are offset) + float targetViewSeparation = 0.0f; - cameraParent.transform.localPosition = targetPosition; + // calculate the camera center position and eye separation if head tracking and the diorama effect are enabled + if (mode == G3DCameraMode.DIORAMA) + { + headtrackingConnection.handleHeadTrackingState( + ref targetPosition, + ref targetViewSeparation, + scaledViewSeparation, + scaledFocusDistance + ); + } + else if (mode == G3DCameraMode.MULTIVIEW) + { + targetViewSeparation = scaledViewSeparation; + } - float horizontalOffset = targetPosition.x; - float verticalOffset = targetPosition.y; + cameraParent.transform.localPosition = targetPosition; - float currentFocusDistance = -cameraParent.transform.localPosition.z; - float dollyZoomOffset = currentFocusDistance - currentFocusDistance * dollyZoom; + float horizontalOffset = targetPosition.x; + float verticalOffset = targetPosition.y; - float focusDistanceWithDollyZoom = currentFocusDistance - dollyZoomOffset; - mainCamera.fieldOfView = - 2 - * Mathf.Atan(scaledHalfCameraWidthAtStart / focusDistanceWithDollyZoom) - * Mathf.Rad2Deg; + float currentFocusDistance = -cameraParent.transform.localPosition.z; + float dollyZoomOffset = currentFocusDistance - currentFocusDistance * dollyZoom; - // set the camera parent position to the focus distance - cameraParent.transform.localPosition = new Vector3( - horizontalOffset, - verticalOffset, - -currentFocusDistance - ); + float focusDistanceWithDollyZoom = currentFocusDistance - dollyZoomOffset; + mainCamera.fieldOfView = + 2 + * Mathf.Atan(scaledHalfCameraWidthAtStart / focusDistanceWithDollyZoom) + * Mathf.Rad2Deg; - //calculate camera positions and matrices - for (int i = 0; i < internalCameraCount; i++) - { - var camera = cameras[i]; - //copy any changes to the main camera - camera.fieldOfView = mainCamera.fieldOfView; - camera.farClipPlane = mainCamera.farClipPlane; - camera.nearClipPlane = mainCamera.nearClipPlane; - camera.projectionMatrix = mainCamera.projectionMatrix; - camera.transform.localRotation = cameraParent.transform.localRotation; - if (generateViews == false) + // set the camera parent position to the focus distance + cameraParent.transform.localPosition = new Vector3( + horizontalOffset, + verticalOffset, + -currentFocusDistance + ); + + //calculate camera positions and matrices + for (int i = 0; i < internalCameraCount; i++) { + var camera = cameras[i]; + //copy any changes to the main camera + camera.fieldOfView = mainCamera.fieldOfView; + camera.farClipPlane = mainCamera.farClipPlane; + camera.nearClipPlane = mainCamera.nearClipPlane; + camera.projectionMatrix = mainCamera.projectionMatrix; + camera.transform.localRotation = cameraParent.transform.localRotation; + if (generateViews == false) + { #if G3D_URP - camera.GetUniversalAdditionalCameraData().antialiasing = antialiasingMode; + camera.GetUniversalAdditionalCameraData().antialiasing = antialiasingMode; #endif #if G3D_HDRP - HDAdditionalCameraData hdAdditionalCameraData = - camera.gameObject.GetComponent(); - if (hdAdditionalCameraData != null) - { - HDAdditionalCameraData mainData = - mainCamera.GetComponent(); - hdAdditionalCameraData.antialiasing = antialiasingMode; - if ( - antialiasingMode - == HDAdditionalCameraData.AntialiasingMode.TemporalAntialiasing - ) + HDAdditionalCameraData hdAdditionalCameraData = + camera.gameObject.GetComponent(); + if (hdAdditionalCameraData != null) { - hdAdditionalCameraData.taaAntiFlicker = mainData.taaAntiFlicker; - hdAdditionalCameraData.taaAntiHistoryRinging = - mainData.taaAntiHistoryRinging; - hdAdditionalCameraData.taaBaseBlendFactor = mainData.taaBaseBlendFactor; - hdAdditionalCameraData.taaHistorySharpening = mainData.taaHistorySharpening; - hdAdditionalCameraData.taaJitterScale = mainData.taaJitterScale; - hdAdditionalCameraData.taaMotionVectorRejection = - mainData.taaMotionVectorRejection; - hdAdditionalCameraData.taaRingingReduction = mainData.taaRingingReduction; - hdAdditionalCameraData.taaSharpenMode = mainData.taaSharpenMode; - hdAdditionalCameraData.taaSharpenStrength = mainData.taaSharpenStrength; - hdAdditionalCameraData.TAAQuality = mainData.TAAQuality; + HDAdditionalCameraData mainData = + mainCamera.GetComponent(); + hdAdditionalCameraData.antialiasing = antialiasingMode; + if ( + antialiasingMode + == HDAdditionalCameraData.AntialiasingMode.TemporalAntialiasing + ) + { + hdAdditionalCameraData.taaAntiFlicker = mainData.taaAntiFlicker; + hdAdditionalCameraData.taaAntiHistoryRinging = + mainData.taaAntiHistoryRinging; + hdAdditionalCameraData.taaBaseBlendFactor = mainData.taaBaseBlendFactor; + hdAdditionalCameraData.taaHistorySharpening = + mainData.taaHistorySharpening; + hdAdditionalCameraData.taaJitterScale = mainData.taaJitterScale; + hdAdditionalCameraData.taaMotionVectorRejection = + mainData.taaMotionVectorRejection; + hdAdditionalCameraData.taaRingingReduction = + mainData.taaRingingReduction; + hdAdditionalCameraData.taaSharpenMode = mainData.taaSharpenMode; + hdAdditionalCameraData.taaSharpenStrength = mainData.taaSharpenStrength; + hdAdditionalCameraData.TAAQuality = mainData.TAAQuality; + } } - } #endif - } + } - float localCameraOffset = calculateCameraOffset( - i, - targetViewSeparation, - internalCameraCount - ); + float localCameraOffset = calculateCameraOffset( + i, + targetViewSeparation, + internalCameraCount + ); - // apply new projection matrix - Matrix4x4 projMatrix = calculateCameraProjectionMatrix( - localCameraOffset, - horizontalOffset, - verticalOffset, - focusDistanceWithDollyZoom, - camera.projectionMatrix - ); + // apply new projection matrix + Matrix4x4 projMatrix = calculateCameraProjectionMatrix( + localCameraOffset, + horizontalOffset, + verticalOffset, + focusDistanceWithDollyZoom, + camera.projectionMatrix + ); - camera.projectionMatrix = projMatrix; + camera.projectionMatrix = projMatrix; - camera.transform.localPosition = new Vector3(localCameraOffset, 0, 0); + camera.transform.localPosition = new Vector3(localCameraOffset, 0, 0); - // if generate views only enable the leftmost and rightmost camera - if (generateViews) - { - if (i == 0 || i == internalCameraCount / 2 || i == internalCameraCount - 1) + // if generate views only enable the leftmost and rightmost camera + if (generateViews) { - camera.gameObject.SetActive(true); + if (i == 0 || i == internalCameraCount / 2 || i == internalCameraCount - 1) + { + camera.gameObject.SetActive(true); + } + else + { + camera.gameObject.SetActive(false); + } } else { - camera.gameObject.SetActive(false); + // enable all cameras + camera.gameObject.SetActive(true); } } - else + + //disable all the other cameras, we are not using them with this cameracount + for (int i = internalCameraCount; i < MAX_CAMERAS; i++) { - // enable all cameras - camera.gameObject.SetActive(true); + cameras[i].gameObject.SetActive(false); } } - //disable all the other cameras, we are not using them with this cameracount - for (int i = internalCameraCount; i < MAX_CAMERAS; i++) - { - cameras[i].gameObject.SetActive(false); - } - } - - /// - /// Sets the camera count to two if we are in diorama mode. Sets it to the maximum amount of views the display is capable of if we are in multiview mode. - /// - /// true if camera count was changed. - private bool updateCameraCountBasedOnMode() - { - int previousCameraCount = internalCameraCount; - if (mode == G3DCameraMode.DIORAMA) - { - internalCameraCount = 2; - } - else if (mode == G3DCameraMode.MULTIVIEW) + /// + /// Sets the camera count to two if we are in diorama mode. Sets it to the maximum amount of views the display is capable of if we are in multiview mode. + /// + /// true if camera count was changed. + private bool updateCameraCountBasedOnMode() { - if (generateViews) + int previousCameraCount = internalCameraCount; + if (mode == G3DCameraMode.DIORAMA) { - // TODO DO NOT HARD CODE THIS VALUE! - internalCameraCount = 16; + internalCameraCount = 2; } + else if (mode == G3DCameraMode.MULTIVIEW) { - internalCameraCount = getCameraCountFromCalibrationFile(); + if (generateViews) + { + // TODO DO NOT HARD CODE THIS VALUE! + internalCameraCount = 16; + } + { + internalCameraCount = getCameraCountFromCalibrationFile(); + } + if (internalCameraCount > MAX_CAMERAS) + { + internalCameraCount = MAX_CAMERAS; + } } - if (internalCameraCount > MAX_CAMERAS) + + if (internalCameraCount != previousCameraCount) { - internalCameraCount = MAX_CAMERAS; + return true; } - } - if (internalCameraCount != previousCameraCount) + return false; + } + + /// + /// adds rendertextres for the cameras to the shader and sets them as target textures for the cameras. + /// + /// + /// + /// + /// only used if view generation is turned on. used to specify the texture name (left, right, middle) + private void addRenderTextureToCamera( + RenderTexture[] renderTextures, + int renderTextureIndex, + int cameraIndex, + string texNameInShader = "texture" + ) { - return true; - } - - return false; - } - - /// - /// adds rendertextres for the cameras to the shader and sets them as target textures for the cameras. - /// - /// - /// - /// - /// only used if view generation is turned on. used to specify the texture name (left, right, middle) - private void addRenderTextureToCamera( - RenderTexture[] renderTextures, - int renderTextureIndex, - int cameraIndex, - string texNameInShader = "texture" - ) - { - int width = Screen.width; - int height = Screen.height; + int width = Screen.width; + int height = Screen.height; - width = (int)(width * (renderResolutionScale / 100f)); - height = (int)(height * (renderResolutionScale / 100f)); + width = (int)(width * (renderResolutionScale / 100f)); + height = (int)(height * (renderResolutionScale / 100f)); - renderTextures[renderTextureIndex] = new RenderTexture(width, height, 0) - { - format = RenderTextureFormat.ARGB32, - depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, - }; - cameras[cameraIndex].targetTexture = renderTextures[renderTextureIndex]; - material.SetTexture( - "texture" + renderTextureIndex, - renderTextures[renderTextureIndex], - RenderTextureSubElement.Color - ); - - if (generateViews) - { - viewGenerationMaterial.SetTexture( - texNameInShader, + renderTextures[renderTextureIndex] = new RenderTexture(width, height, 0) + { + format = RenderTextureFormat.ARGB32, + depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm, + }; + cameras[cameraIndex].targetTexture = renderTextures[renderTextureIndex]; + material.SetTexture( + "texture" + renderTextureIndex, renderTextures[renderTextureIndex], RenderTextureSubElement.Color ); + + if (generateViews) + { + viewGenerationMaterial.SetTexture( + texNameInShader, + renderTextures[renderTextureIndex], + RenderTextureSubElement.Color + ); + } } - } - private bool windowResized() - { - var window_dim = new Vector2Int(Screen.width, Screen.height); - if (cachedWindowSize != window_dim) + private bool windowResized() { - cachedWindowSize = window_dim; - return true; + var window_dim = new Vector2Int(Screen.width, Screen.height); + if (cachedWindowSize != window_dim) + { + cachedWindowSize = window_dim; + return true; + } + return false; } - return false; - } - private bool windowMoved() - { - var window_pos = new Vector2Int(Screen.mainWindowPosition.x, Screen.mainWindowPosition.y); - if (cachedWindowPosition != window_pos) + private bool windowMoved() { - cachedWindowPosition = window_pos; - return true; - } - return false; - } + var window_pos = new Vector2Int( + Screen.mainWindowPosition.x, + Screen.mainWindowPosition.y + ); + if (cachedWindowPosition != window_pos) + { + cachedWindowPosition = window_pos; + return true; + } + return false; + } + + /// + /// + /// + /// Offset (along the x axis) of this camera compared to zero position. + /// general offset (x axis) of the "zero position" compared to start position due to head tracking. + /// general offset (y axis) of the "zero position" compared to start position due to head tracking. + /// general offset (z axis) of the "zero position" compared to start position due to head tracking. + /// + /// + private Matrix4x4 calculateCameraProjectionMatrix( + float localCameraOffset, + float horizontalOffset, + float verticalOffset, + float focusDistance, + Matrix4x4 mainCamProjectionMatrix + ) + { + // horizontal obliqueness + float horizontalObl = -(localCameraOffset + horizontalOffset) / focusDistance; + float vertObl = -verticalOffset / focusDistance; - /// - /// - /// - /// Offset (along the x axis) of this camera compared to zero position. - /// general offset (x axis) of the "zero position" compared to start position due to head tracking. - /// general offset (y axis) of the "zero position" compared to start position due to head tracking. - /// general offset (z axis) of the "zero position" compared to start position due to head tracking. - /// - /// - private Matrix4x4 calculateCameraProjectionMatrix( - float localCameraOffset, - float horizontalOffset, - float verticalOffset, - float focusDistance, - Matrix4x4 mainCamProjectionMatrix - ) - { - // horizontal obliqueness - float horizontalObl = -(localCameraOffset + horizontalOffset) / focusDistance; - float vertObl = -verticalOffset / focusDistance; - - // focus distance is in view space. Writing directly into projection matrix would require focus distance to be in projection space - Matrix4x4 shearMatrix = Matrix4x4.identity; - shearMatrix[0, 2] = horizontalObl; - shearMatrix[1, 2] = vertObl; - // apply new projection matrix - return mainCamProjectionMatrix * shearMatrix; - } + // focus distance is in view space. Writing directly into projection matrix would require focus distance to be in projection space + Matrix4x4 shearMatrix = Matrix4x4.identity; + shearMatrix[0, 2] = horizontalObl; + shearMatrix[1, 2] = vertObl; + // apply new projection matrix + return mainCamProjectionMatrix * shearMatrix; + } - // This function only does something when you use the SRP render pipeline. - // when using either URP or HRDP image combination is handled in the respective renderpasses. - // URP -> G3D.RenderPipeline.URP.ScriptableRP.cs - // HDRP -> G3D.RenderPipeline.HDRP.CustomPass.cs - void OnRenderImage(RenderTexture source, RenderTexture destination) - { - // This is where the material and shader are applied to the camera image. - //legacy support (no URP or HDRP) + // This function only does something when you use the SRP render pipeline. + // when using either URP or HRDP image combination is handled in the respective renderpasses. + // URP -> G3D.RenderPipeline.URP.ScriptableRP.cs + // HDRP -> G3D.RenderPipeline.HDRP.CustomPass.cs + void OnRenderImage(RenderTexture source, RenderTexture destination) + { + // This is where the material and shader are applied to the camera image. + //legacy support (no URP or HDRP) #if G3D_HDRP || URP #else - if (material == null) - Graphics.Blit(source, destination); - else - Graphics.Blit(source, destination, material); + if (material == null) + Graphics.Blit(source, destination); + else + Graphics.Blit(source, destination, material); #endif - } - #endregion - - #region Debugging -#if UNITY_EDITOR - void OnDrawGizmos() - { - if (!showGizmos) - { - return; } + #endregion - if (enabled == false) + #region Debugging +#if UNITY_EDITOR + void OnDrawGizmos() { - // do not run this code if the script is not enabled - return; - } + if (!showGizmos) + { + return; + } - if (mainCamera == null) - { - InitMainCamera(); - if (mainCamera == null) + if (enabled == false) { - Debug.LogError( - "No main camera found. Please add a camera to the G3DCamera object." - ); + // do not run this code if the script is not enabled return; } - } - float dollyZoomFactor = scaledFocusDistance - scaledFocusDistance * dollyZoom; + if (mainCamera == null) + { + InitMainCamera(); + if (mainCamera == null) + { + Debug.LogError( + "No main camera found. Please add a camera to the G3DCamera object." + ); + return; + } + } - float tmpHalfCameraWidthAtStart = - Mathf.Tan(baseFieldOfView * Mathf.Deg2Rad / 2) * scaledFocusDistance; + float dollyZoomFactor = scaledFocusDistance - scaledFocusDistance * dollyZoom; - float focusDistanceWithDollyZoom = scaledFocusDistance - dollyZoomFactor; - float tmpFieldOfView = - 2 * Mathf.Atan(tmpHalfCameraWidthAtStart / focusDistanceWithDollyZoom) * Mathf.Rad2Deg; - float fieldOfViewWithoutDolly = - 2 * Mathf.Atan(tmpHalfCameraWidthAtStart / scaledFocusDistance) * Mathf.Rad2Deg; + float tmpHalfCameraWidthAtStart = + Mathf.Tan(baseFieldOfView * Mathf.Deg2Rad / 2) * scaledFocusDistance; - Vector3 basePosition = new Vector3(0, 0, focusDistanceWithDollyZoom); + float focusDistanceWithDollyZoom = scaledFocusDistance - dollyZoomFactor; + float tmpFieldOfView = + 2 + * Mathf.Atan(tmpHalfCameraWidthAtStart / focusDistanceWithDollyZoom) + * Mathf.Rad2Deg; + float fieldOfViewWithoutDolly = + 2 * Mathf.Atan(tmpHalfCameraWidthAtStart / scaledFocusDistance) * Mathf.Rad2Deg; + + Vector3 basePosition = new Vector3(0, 0, focusDistanceWithDollyZoom); + + Vector3 position; + // draw eye separation + Gizmos.color = new Color(0, 0, 1, 0.75F); + Gizmos.matrix = transform.localToWorldMatrix; + // draw camera position spheres + for (int i = 0; i < internalCameraCount; i++) + { + float localCameraOffset = calculateCameraOffset( + i, + scaledViewSeparation, + internalCameraCount + ); + Vector3 camPos = basePosition - new Vector3(0, 0, focusDistanceWithDollyZoom); + camPos += new Vector3(1, 0, 0) * localCameraOffset; + Gizmos.DrawSphere(camPos, 0.2f * gizmoSize * sceneScaleFactor); + } - Vector3 position; - // draw eye separation - Gizmos.color = new Color(0, 0, 1, 0.75F); - Gizmos.matrix = transform.localToWorldMatrix; - // draw camera position spheres - for (int i = 0; i < internalCameraCount; i++) - { - float localCameraOffset = calculateCameraOffset( - i, - scaledViewSeparation, - internalCameraCount - ); - Vector3 camPos = basePosition - new Vector3(0, 0, focusDistanceWithDollyZoom); - camPos += new Vector3(1, 0, 0) * localCameraOffset; - Gizmos.DrawSphere(camPos, 0.2f * gizmoSize * sceneScaleFactor); - } + // draw camera frustums + Gizmos.color = new Color(0, 0, 1, 1); // set color to one wo improve visibility + for (int i = 0; i < internalCameraCount; i++) + { + float localCameraOffset = calculateCameraOffset( + i, + scaledViewSeparation, + internalCameraCount + ); + position = transform.position + transform.right * localCameraOffset; - // draw camera frustums - Gizmos.color = new Color(0, 0, 1, 1); // set color to one wo improve visibility - for (int i = 0; i < internalCameraCount; i++) - { - float localCameraOffset = calculateCameraOffset( - i, - scaledViewSeparation, - internalCameraCount - ); - position = transform.position + transform.right * localCameraOffset; + // apply new projection matrix + Matrix4x4 localProjectionMatrix = Matrix4x4.TRS( + position, + transform.rotation, + Vector3.one + ); + Matrix4x4 projMatrix = calculateCameraProjectionMatrix( + localCameraOffset, + 0, + 0, + focusDistanceWithDollyZoom, + localProjectionMatrix + ); - // apply new projection matrix - Matrix4x4 localProjectionMatrix = Matrix4x4.TRS( - position, - transform.rotation, - Vector3.one - ); - Matrix4x4 projMatrix = calculateCameraProjectionMatrix( - localCameraOffset, - 0, - 0, - focusDistanceWithDollyZoom, - localProjectionMatrix - ); + Gizmos.matrix = projMatrix; - Gizmos.matrix = projMatrix; + Gizmos.DrawFrustum( + Vector3.zero, + tmpFieldOfView, + mainCamera.farClipPlane, + mainCamera.nearClipPlane, + mainCamera.aspect + ); + } - Gizmos.DrawFrustum( - Vector3.zero, - tmpFieldOfView, - mainCamera.farClipPlane, - mainCamera.nearClipPlane, - mainCamera.aspect - ); + Gizmos.matrix = transform.localToWorldMatrix; + + // draw focus plane + Gizmos.color = new Color(0, 0, 1, 0.25F); + position = new Vector3(0, 0, 1) * focusDistanceWithDollyZoom; + float frustumWidth = + Mathf.Tan(fieldOfViewWithoutDolly * Mathf.Deg2Rad / 2) * scaledFocusDistance * 2; + float frustumHeight = + Mathf.Tan( + Camera.VerticalToHorizontalFieldOfView( + fieldOfViewWithoutDolly, + mainCamera.aspect + ) + * Mathf.Deg2Rad + / 2 + ) + * scaledFocusDistance + * 2; + Gizmos.DrawCube(position, new Vector3(frustumHeight, frustumWidth, 0.001f)); } - - Gizmos.matrix = transform.localToWorldMatrix; - - // draw focus plane - Gizmos.color = new Color(0, 0, 1, 0.25F); - position = new Vector3(0, 0, 1) * focusDistanceWithDollyZoom; - float frustumWidth = - Mathf.Tan(fieldOfViewWithoutDolly * Mathf.Deg2Rad / 2) * scaledFocusDistance * 2; - float frustumHeight = - Mathf.Tan( - Camera.VerticalToHorizontalFieldOfView(fieldOfViewWithoutDolly, mainCamera.aspect) - * Mathf.Deg2Rad - / 2 - ) - * scaledFocusDistance - * 2; - Gizmos.DrawCube(position, new Vector3(frustumHeight, frustumWidth, 0.001f)); - } #endif - #endregion + #endregion - private float calculateCameraOffset( - int currentCamera, - float targetEyeSeparation, - int tmpCameraCount - ) - { - int currentView = -tmpCameraCount / 2 + currentCamera; - if (tmpCameraCount % 2 == 0 && currentView >= 0) + private float calculateCameraOffset( + int currentCamera, + float targetEyeSeparation, + int tmpCameraCount + ) { - currentView += 1; - } + int currentView = -tmpCameraCount / 2 + currentCamera; + if (tmpCameraCount % 2 == 0 && currentView >= 0) + { + currentView += 1; + } - // it is used to scale the offset to the ends for a mosaic texture where the middle textures are missing - float offset = currentView * targetEyeSeparation; + // it is used to scale the offset to the ends for a mosaic texture where the middle textures are missing + float offset = currentView * targetEyeSeparation; - // when the camera count is even, one camera is placed half the eye separation to the right of the center - // same for the other to the left - // therefore we need to add the correction term to the offset to get the correct position - if (tmpCameraCount % 2 == 0) - { - // subtract half of the eye separation to get the correct offset - float correctionTerm = targetEyeSeparation / 2; - if (currentView > 0) + // when the camera count is even, one camera is placed half the eye separation to the right of the center + // same for the other to the left + // therefore we need to add the correction term to the offset to get the correct position + if (tmpCameraCount % 2 == 0) { - correctionTerm *= -1; + // subtract half of the eye separation to get the correct offset + float correctionTerm = targetEyeSeparation / 2; + if (currentView > 0) + { + correctionTerm *= -1; + } + offset = offset + correctionTerm; } - offset = offset + correctionTerm; - } - int flip = mirrorViews ? 1 : -1; + int flip = mirrorViews ? 1 : -1; - return offset * flip; + return offset * flip; + } } } diff --git a/G3DCameraMosaicMultiview.cs b/G3DCameraMosaicMultiview.cs index 6ec5753..032770b 100644 --- a/G3DCameraMosaicMultiview.cs +++ b/G3DCameraMosaicMultiview.cs @@ -14,564 +14,599 @@ #if G3D_URP using UnityEngine.Rendering.Universal; #endif - -public enum MosaicMode -{ - Image, - Video, - RenderTexture -} - -/// -/// Replaces the image the camera this script is attached to sees with the rendertexture. -/// The texture should contain a mosaic image with several views. -/// -/// IMPORTANT: This script must not be attached to a camera already using a G3D camera script. -/// -[RequireComponent(typeof(Camera))] -public class G3DCameraMosaicMultiview : MonoBehaviour +namespace G3D { - #region Calibration - [Tooltip("Drop the calibration file for the display you want to use here.")] - public TextAsset calibrationFile; - - [Min(1)] - public int mosaicRowCount = 3; - - [Min(1)] - public int mosaicColumnCount = 3; - - [Tooltip( - "If enabled, the mosaic dimensions will be extracted from the filename. E.g. video.mosaic.3x3\nONLY WORKS FOR IMAGE AND VIDEO MODES" - )] - public bool dimensionsFromFilename = false; - - [Tooltip( - "Does not check if the amount of HQ views specified in the calibration file fits the provided mosaic." - )] - public bool useHQViews = false; - - [Space(10)] - [Tooltip( - "Where the views start to yoyo in the index map. Index map contains the order of views." - )] - [Range(0.0f, 1.0f)] - public float indexMapYoyoStart = 0.0f; - - [Tooltip("Inverts the entire index map. Index map contains the order of views.")] - public bool invertIndexMap = false; - - [Tooltip("Inverts the indices in the index map. Index map contains the order of views.")] - public bool invertIndexMapIndices = false; - - [Space(10)] - #endregion + public enum MosaicMode + { + Image, + Video, + RenderTexture + } /// - /// Shifts the individual views to the left or right by the specified number of views. + /// Replaces the image the camera this script is attached to sees with the rendertexture. + /// The texture should contain a mosaic image with several views. + /// + /// IMPORTANT: This script must not be attached to a camera already using a G3D camera script. /// - [Tooltip("Shifts the individual views to the left or right by the specified number of views.")] - public int viewOffset = 0; - - public MosaicMode mosaicMode = MosaicMode.RenderTexture; - public RenderTexture renderTexture; - public Texture2D image; - - public VideoClip videoClip; - - #region 3D Effect settings - [Header("3D Effect settings")] - [Tooltip("If set to true, the views will be flipped horizontally.")] - public bool mirrorViews = false; - #endregion - - - #region Private variables - private PreviousValues previousValues = new PreviousValues(); - private IndexMap indexMap = IndexMap.Instance; - private Camera mainCamera; - private Material material; + [RequireComponent(typeof(Camera))] + public class G3DCameraMosaicMultiview : MonoBehaviour + { + #region Calibration + [Tooltip("Drop the calibration file for the display you want to use here.")] + public TextAsset calibrationFile; + + [Min(1)] + public int mosaicRowCount = 3; + + [Min(1)] + public int mosaicColumnCount = 3; + + [Tooltip( + "If enabled, the mosaic dimensions will be extracted from the filename. E.g. video.mosaic.3x3\nONLY WORKS FOR IMAGE AND VIDEO MODES" + )] + public bool dimensionsFromFilename = false; + + [Tooltip( + "Does not check if the amount of HQ views specified in the calibration file fits the provided mosaic." + )] + public bool useHQViews = false; + + [Space(10)] + [Tooltip( + "Where the views start to yoyo in the index map. Index map contains the order of views." + )] + [Range(0.0f, 1.0f)] + public float indexMapYoyoStart = 0.0f; + + [Tooltip("Inverts the entire index map. Index map contains the order of views.")] + public bool invertIndexMap = false; + + [Tooltip("Inverts the indices in the index map. Index map contains the order of views.")] + public bool invertIndexMapIndices = false; + + [Space(10)] + #endregion + + /// + /// Shifts the individual views to the left or right by the specified number of views. + /// + [Tooltip( + "Shifts the individual views to the left or right by the specified number of views." + )] + public int viewOffset = 0; + + public MosaicMode mosaicMode = MosaicMode.RenderTexture; + public RenderTexture renderTexture; + public Texture2D image; + + public VideoClip videoClip; + + #region 3D Effect settings + [Header("3D Effect settings")] + [Tooltip("If set to true, the views will be flipped horizontally.")] + public bool mirrorViews = false; + #endregion + + + #region Private variables + private PreviousValues previousValues = new PreviousValues(); + private IndexMap indexMap = IndexMap.Instance; + private Camera mainCamera; + private Material material; #if G3D_HDRP - private G3D.RenderPipeline.HDRP.CustomPass customPass; + private G3D.RenderPipeline.HDRP.CustomPass customPass; #endif #if G3D_URP - private G3D.RenderPipeline.URP.ScriptableRP customPass; + private G3D.RenderPipeline.URP.ScriptableRP customPass; #endif - private ShaderHandles shaderHandles; - private G3DShaderParameters shaderParameters; + private ShaderHandles shaderHandles; + private G3DShaderParameters shaderParameters; - private Vector2Int cachedWindowPosition; - private Vector2Int cachedWindowSize; + private Vector2Int cachedWindowPosition; + private Vector2Int cachedWindowSize; - public VideoPlayer internalVideoPlayer { get; private set; } + public VideoPlayer internalVideoPlayer { get; private set; } - #endregion + #endregion - #region Initialization - void Start() - { - mainCamera = GetComponent(); - mainCamera.cullingMask = 0; //disable rendering of the main camera - mainCamera.clearFlags = CameraClearFlags.Color; + #region Initialization + void Start() + { + mainCamera = GetComponent(); + mainCamera.cullingMask = 0; //disable rendering of the main camera + mainCamera.clearFlags = CameraClearFlags.Color; - //initialize cameras + //initialize cameras - shaderHandles = new ShaderHandles(); - shaderHandles.init(); + shaderHandles = new ShaderHandles(); + shaderHandles.init(); - // This has to be done after the cameras are updated - cachedWindowPosition = new Vector2Int( - Screen.mainWindowPosition.x, - Screen.mainWindowPosition.y - ); - cachedWindowSize = new Vector2Int(Screen.width, Screen.height); + // This has to be done after the cameras are updated + cachedWindowPosition = new Vector2Int( + Screen.mainWindowPosition.x, + Screen.mainWindowPosition.y + ); + cachedWindowSize = new Vector2Int(Screen.width, Screen.height); #if G3D_HDRP - // init fullscreen postprocessing for hd render pipeline - var customPassVolume = gameObject.AddComponent(); - customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; - customPassVolume.isGlobal = true; - // Make the volume invisible in the inspector - customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; - customPass = - customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.CustomPass)) - as G3D.RenderPipeline.HDRP.CustomPass; - customPass.fullscreenPassMaterial = material; - customPass.materialPassName = "G3DFullScreen3D"; + // init fullscreen postprocessing for hd render pipeline + var customPassVolume = gameObject.AddComponent(); + customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess; + customPassVolume.isGlobal = true; + // Make the volume invisible in the inspector + customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave; + customPass = + customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.CustomPass)) + as G3D.RenderPipeline.HDRP.CustomPass; + customPass.fullscreenPassMaterial = material; + customPass.materialPassName = "G3DFullScreen3D"; #endif #if G3D_URP - customPass = new G3D.RenderPipeline.URP.ScriptableRP(material); + customPass = new G3D.RenderPipeline.URP.ScriptableRP(material); #endif - // Do this last to ensure custom passes are already set up - CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromString( - calibrationFile.text - ); - shaderParameters = defaultCalibrationProvider.getShaderParameters(); - setupTextureMode(); - reinitializeShader(); + // Do this last to ensure custom passes are already set up + CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromString( + calibrationFile.text + ); + shaderParameters = defaultCalibrationProvider.getShaderParameters(); + setupTextureMode(); + reinitializeShader(); + + previousValues.init(); - previousValues.init(); + if (dimensionsFromFilename) + { + extractDimensionsFromFile(); + } - if (dimensionsFromFilename) - { - extractDimensionsFromFile(); + updateIndexMap(); } - updateIndexMap(); - } - #if G3D_URP - private void OnEnable() - { - RenderPipelineManager.beginCameraRendering += OnBeginCamera; - } + private void OnEnable() + { + RenderPipelineManager.beginCameraRendering += OnBeginCamera; + } - private void OnDisable() - { - RenderPipelineManager.beginCameraRendering -= OnBeginCamera; - } + private void OnDisable() + { + RenderPipelineManager.beginCameraRendering -= OnBeginCamera; + } - private void OnBeginCamera(ScriptableRenderContext context, Camera cam) - { - // Use the EnqueuePass method to inject a custom render pass - cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass); - } + private void OnBeginCamera(ScriptableRenderContext context, Camera cam) + { + // Use the EnqueuePass method to inject a custom render pass + cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass); + } #endif - #endregion - - #region Updates + #endregion - /// - /// OnValidate gets called every time the script is changed in the editor. - /// This is used to react to changes made to the parameters. - /// - void OnValidate() - { - if (isActiveAndEnabled == false) - { - // do not run this code if the script is not enabled - return; - } + #region Updates - if (calibrationFile != previousValues.calibrationFile) + /// + /// OnValidate gets called every time the script is changed in the editor. + /// This is used to react to changes made to the parameters. + /// + void OnValidate() { - previousValues.calibrationFile = calibrationFile; - updateShaderFromCalibrationFile(); + if (isActiveAndEnabled == false) + { + // do not run this code if the script is not enabled + return; + } + + if (calibrationFile != previousValues.calibrationFile) + { + previousValues.calibrationFile = calibrationFile; + updateShaderFromCalibrationFile(); + } + + if ( + previousValues.indexMapYoyoStart != indexMapYoyoStart + || previousValues.invertIndexMap != invertIndexMap + || previousValues.invertIndexMapIndices != invertIndexMapIndices + ) + { + previousValues.indexMapYoyoStart = indexMapYoyoStart; + previousValues.invertIndexMap = invertIndexMap; + previousValues.invertIndexMapIndices = invertIndexMapIndices; + + indexMap.UpdateIndexMap( + shaderParameters.nativeViewCount, + mosaicColumnCount * mosaicRowCount, + indexMapYoyoStart, + invertIndexMap, + invertIndexMapIndices + ); + } } - if ( - previousValues.indexMapYoyoStart != indexMapYoyoStart - || previousValues.invertIndexMap != invertIndexMap - || previousValues.invertIndexMapIndices != invertIndexMapIndices - ) + void Update() { - previousValues.indexMapYoyoStart = indexMapYoyoStart; - previousValues.invertIndexMap = invertIndexMap; - previousValues.invertIndexMapIndices = invertIndexMapIndices; + updateShaderParameters(); - indexMap.UpdateIndexMap( - shaderParameters.nativeViewCount, - mosaicColumnCount * mosaicRowCount, - indexMapYoyoStart, - invertIndexMap, - invertIndexMapIndices - ); + if (windowResized() || windowMoved()) + { + updateScreenViewportProperties(); + } } - } - void Update() - { - updateShaderParameters(); - - if (windowResized() || windowMoved()) + public void reinitializeShader() { - updateScreenViewportProperties(); - } - } + material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); + setCorrectMosaicTexture(); - public void reinitializeShader() - { - material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic")); - setCorrectMosaicTexture(); - - updateScreenViewportProperties(); - updateShaderParameters(); + updateScreenViewportProperties(); + updateShaderParameters(); #if G3D_HDRP - customPass.fullscreenPassMaterial = material; + customPass.fullscreenPassMaterial = material; #endif #if G3D_URP - customPass.updateMaterial(material); + customPass.updateMaterial(material); #endif - } - - public void updateShaderFromCalibrationFile(TextAsset calibrationFile) - { - if (calibrationFile == null || calibrationFile.text == "") - { - return; } - this.calibrationFile = calibrationFile; - updateShaderFromCalibrationFile(); - } - public void updateShaderFromCalibrationFile() - { - if (calibrationFile == null || calibrationFile.text == "") + public void updateShaderFromCalibrationFile(TextAsset calibrationFile) { - return; + if (calibrationFile == null || calibrationFile.text == "") + { + return; + } + this.calibrationFile = calibrationFile; + updateShaderFromCalibrationFile(); } - CalibrationProvider calibrationProvider = CalibrationProvider.getFromString( - calibrationFile.text - ); - shaderParameters = calibrationProvider.getShaderParameters(); - } - - /// - /// The provided file uri has to be a display calibration ini file. - /// - /// - public void UpdateShaderParametersFromURI(string uri) - { - if (uri == null || uri == "") + public void updateShaderFromCalibrationFile() { - return; - } + if (calibrationFile == null || calibrationFile.text == "") + { + return; + } - try - { - CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromURI( - uri, - (CalibrationProvider provider) => - { - shaderParameters = provider.getShaderParameters(); - updateShaderParameters(); - return 0; - } + CalibrationProvider calibrationProvider = CalibrationProvider.getFromString( + calibrationFile.text ); + shaderParameters = calibrationProvider.getShaderParameters(); } - catch (Exception e) - { - Debug.LogError("Failed to update shader parameters from uri: " + e.Message); - } - } - /// - /// The provided file path has to be a display calibration ini file. - /// - /// - public void UpdateShaderParametersFromFile(string filePath) - { - if (filePath == null || filePath == "" || filePath.EndsWith(".ini") == false) + /// + /// The provided file uri has to be a display calibration ini file. + /// + /// + public void UpdateShaderParametersFromURI(string uri) { - return; + if (uri == null || uri == "") + { + return; + } + + try + { + CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromURI( + uri, + (CalibrationProvider provider) => + { + shaderParameters = provider.getShaderParameters(); + updateShaderParameters(); + return 0; + } + ); + } + catch (Exception e) + { + Debug.LogError("Failed to update shader parameters from uri: " + e.Message); + } } - try - { - CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromConfigFile( - filePath - ); - shaderParameters = defaultCalibrationProvider.getShaderParameters(); - updateShaderParameters(); - } - catch (Exception e) + /// + /// The provided file path has to be a display calibration ini file. + /// + /// + public void UpdateShaderParametersFromFile(string filePath) { - Debug.LogError("Failed to update shader parameters from file: " + e.Message); + if (filePath == null || filePath == "" || filePath.EndsWith(".ini") == false) + { + return; + } + + try + { + CalibrationProvider defaultCalibrationProvider = + CalibrationProvider.getFromConfigFile(filePath); + shaderParameters = defaultCalibrationProvider.getShaderParameters(); + updateShaderParameters(); + } + catch (Exception e) + { + Debug.LogError("Failed to update shader parameters from file: " + e.Message); + } } - } - /// - /// The provided string has to be a display calibration ini file. - /// - /// - public void UpdateShaderParametersFromINIString(string iniFile) - { - if (iniFile == null || iniFile == "") + /// + /// The provided string has to be a display calibration ini file. + /// + /// + public void UpdateShaderParametersFromINIString(string iniFile) { - return; + if (iniFile == null || iniFile == "") + { + return; + } + + try + { + CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromString( + iniFile + ); + shaderParameters = defaultCalibrationProvider.getShaderParameters(); + updateShaderParameters(); + } + catch (Exception e) + { + Debug.LogError("Failed to update shader parameters from json: " + e.Message); + } } - try + private void updateIndexMap() { - CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromString( - iniFile + int availableViews = shaderParameters.nativeViewCount; + if (useHQViews) + { + availableViews = shaderParameters.hqViewCount; + } + + indexMap.UpdateIndexMap( + availableViews, + mosaicColumnCount * mosaicRowCount, + indexMapYoyoStart, + invertIndexMap, + invertIndexMapIndices ); - shaderParameters = defaultCalibrationProvider.getShaderParameters(); - updateShaderParameters(); - } - catch (Exception e) - { - Debug.LogError("Failed to update shader parameters from json: " + e.Message); } - } - private void updateIndexMap() - { - int availableViews = shaderParameters.nativeViewCount; - if (useHQViews) + private void extractDimensionsFromFile() { - availableViews = shaderParameters.hqViewCount; + string name = ""; + switch (mosaicMode) + { + case MosaicMode.Image: + if (image != null) + { + name = image.name; + } + break; + case MosaicMode.Video: + if (videoClip != null) + { + name = videoClip.name; + } + break; + case MosaicMode.RenderTexture: + // cannot extract dimensions from render texture + return; + } + dimensionsFromString(name, out mosaicRowCount, out mosaicColumnCount); } - indexMap.UpdateIndexMap( - availableViews, - mosaicColumnCount * mosaicRowCount, - indexMapYoyoStart, - invertIndexMap, - invertIndexMapIndices - ); - } - - private void extractDimensionsFromFile() - { - string name = ""; - switch (mosaicMode) + private void dimensionsFromString(string name, out int rows, out int columns) { - case MosaicMode.Image: - if (image != null) - { - name = image.name; - } - break; - case MosaicMode.Video: - if (videoClip != null) - { - name = videoClip.name; - } - break; - case MosaicMode.RenderTexture: - // cannot extract dimensions from render texture - return; - } - dimensionsFromString(name, out mosaicRowCount, out mosaicColumnCount); - } + rows = 1; + columns = 1; - private void dimensionsFromString(string name, out int rows, out int columns) - { - rows = 1; - columns = 1; + string[] parts = name.Split('.'); + if (parts.Length < 3 || parts[1] != "mosaic") + { + Debug.LogError("Invalid mosaic video file name format: " + name); + return; + } - string[] parts = name.Split('.'); - if (parts.Length < 3 || parts[1] != "mosaic") - { - Debug.LogError("Invalid mosaic video file name format: " + name); - return; + string rowsStr = parts[2]; + string[] tmp = rowsStr.Split('x'); + if (tmp.Length != 2) + { + Debug.LogError("Invalid mosaic video rows format: " + rowsStr); + return; + } + string columnsStr = tmp[0]; // e.g. 3x3 -> 3 + string rowsStrOnly = tmp[1]; // e.g. 3x3 -> 3 + if (!int.TryParse(columnsStr, out columns) || !int.TryParse(rowsStrOnly, out rows)) + { + Debug.LogError("Could not parse mosaic video rows and columns from: " + rowsStr); + return; + } } - string rowsStr = parts[2]; - string[] tmp = rowsStr.Split('x'); - if (tmp.Length != 2) - { - Debug.LogError("Invalid mosaic video rows format: " + rowsStr); - return; - } - string columnsStr = tmp[0]; // e.g. 3x3 -> 3 - string rowsStrOnly = tmp[1]; // e.g. 3x3 -> 3 - if (!int.TryParse(columnsStr, out columns) || !int.TryParse(rowsStrOnly, out rows)) + private void setupTextureMode() { - Debug.LogError("Could not parse mosaic video rows and columns from: " + rowsStr); - return; + switch (mosaicMode) + { + case MosaicMode.RenderTexture: + // nothing to do here, render texture is already assigned + break; + case MosaicMode.Video: + setupVideoPlayer(); + break; + case MosaicMode.Image: + // nothing to do here, image is already assigned + break; + } } - } - private void setupTextureMode() - { - switch (mosaicMode) + private void setupVideoPlayer() { - case MosaicMode.RenderTexture: - // nothing to do here, render texture is already assigned - break; - case MosaicMode.Video: - setupVideoPlayer(); - break; - case MosaicMode.Image: - // nothing to do here, image is already assigned - break; + if (internalVideoPlayer == null) + { + internalVideoPlayer = gameObject.AddComponent(); + internalVideoPlayer.playOnAwake = true; + internalVideoPlayer.isLooping = true; + } + internalVideoPlayer.renderMode = VideoRenderMode.RenderTexture; + if (renderTexture == null) + { + renderTexture = new RenderTexture(1920, 1080, 0); + } + internalVideoPlayer.targetTexture = renderTexture; + internalVideoPlayer.clip = videoClip; + internalVideoPlayer.Play(); } - } - private void setupVideoPlayer() - { - if (internalVideoPlayer == null) + private void setCorrectMosaicTexture() { - internalVideoPlayer = gameObject.AddComponent(); - internalVideoPlayer.playOnAwake = true; - internalVideoPlayer.isLooping = true; + switch (mosaicMode) + { + case MosaicMode.RenderTexture: + material.SetTexture( + "mosaictexture", + renderTexture, + RenderTextureSubElement.Color + ); + material.SetTexture( + "_colorMosaic", + renderTexture, + RenderTextureSubElement.Color + ); + break; + case MosaicMode.Video: + material.SetTexture( + "mosaictexture", + renderTexture, + RenderTextureSubElement.Color + ); + material.SetTexture( + "_colorMosaic", + renderTexture, + RenderTextureSubElement.Color + ); + break; + case MosaicMode.Image: + material.SetTexture("mosaictexture", image); + material.SetTexture("_colorMosaic", image); + break; + } } - internalVideoPlayer.renderMode = VideoRenderMode.RenderTexture; - if (renderTexture == null) - { - renderTexture = new RenderTexture(1920, 1080, 0); - } - internalVideoPlayer.targetTexture = renderTexture; - internalVideoPlayer.clip = videoClip; - internalVideoPlayer.Play(); - } - private void setCorrectMosaicTexture() - { - switch (mosaicMode) + private void updateScreenViewportProperties() { - case MosaicMode.RenderTexture: - material.SetTexture("mosaictexture", renderTexture, RenderTextureSubElement.Color); - material.SetTexture("_colorMosaic", renderTexture, RenderTextureSubElement.Color); - break; - case MosaicMode.Video: - material.SetTexture("mosaictexture", renderTexture, RenderTextureSubElement.Color); - material.SetTexture("_colorMosaic", renderTexture, RenderTextureSubElement.Color); - break; - case MosaicMode.Image: - material.SetTexture("mosaictexture", image); - material.SetTexture("_colorMosaic", image); - break; + try + { + shaderParameters.screenHeight = Screen.height; + shaderParameters.screenWidth = Screen.width; + shaderParameters.leftViewportPosition = Screen.mainWindowPosition.x; + shaderParameters.bottomViewportPosition = + Screen.mainWindowPosition.y + Screen.height; + } + catch (Exception e) + { + Debug.LogError("Failed to update screen viewport properties: " + e.Message); + } + + // this parameter is used in the shader to invert the y axis + material?.SetInt(Shader.PropertyToID("viewportHeight"), Screen.height); + material?.SetInt(Shader.PropertyToID("viewportWidth"), Screen.width); } - } - private void updateScreenViewportProperties() - { - try - { - shaderParameters.screenHeight = Screen.height; - shaderParameters.screenWidth = Screen.width; - shaderParameters.leftViewportPosition = Screen.mainWindowPosition.x; - shaderParameters.bottomViewportPosition = Screen.mainWindowPosition.y + Screen.height; - } - catch (Exception e) + private void updateShaderParameters() { - Debug.LogError("Failed to update screen viewport properties: " + e.Message); - } - - // this parameter is used in the shader to invert the y axis - material?.SetInt(Shader.PropertyToID("viewportHeight"), Screen.height); - material?.SetInt(Shader.PropertyToID("viewportWidth"), Screen.width); - } + material?.SetInt( + shaderHandles.leftViewportPosition, + shaderParameters.leftViewportPosition + ); + material?.SetInt( + shaderHandles.bottomViewportPosition, + shaderParameters.bottomViewportPosition + ); + material?.SetInt(shaderHandles.screenHeight, shaderParameters.screenHeight); + material?.SetInt(shaderHandles.screenWidth, shaderParameters.screenWidth); + material?.SetInt( + shaderHandles.angleRatioNumerator, + shaderParameters.angleRatioNumerator + ); + material?.SetInt( + shaderHandles.angleRatioDenominator, + shaderParameters.angleRatioDenominator + ); + material?.SetInt(shaderHandles.nativeViewCount, shaderParameters.nativeViewCount); + material?.SetInt( + shaderHandles.leftLensOrientation, + shaderParameters.leftLensOrientation + ); + material?.SetInt(shaderHandles.showTestFrame, 0); + material?.SetInt(shaderHandles.hqViewCount, shaderParameters.hqViewCount); + material?.SetInt(shaderHandles.BGRPixelLayout, shaderParameters.BGRPixelLayout); + material?.SetInt(shaderHandles.mstart, shaderParameters.mstart); - private void updateShaderParameters() - { - material?.SetInt(shaderHandles.leftViewportPosition, shaderParameters.leftViewportPosition); - material?.SetInt( - shaderHandles.bottomViewportPosition, - shaderParameters.bottomViewportPosition - ); - material?.SetInt(shaderHandles.screenHeight, shaderParameters.screenHeight); - material?.SetInt(shaderHandles.screenWidth, shaderParameters.screenWidth); - material?.SetInt(shaderHandles.angleRatioNumerator, shaderParameters.angleRatioNumerator); - material?.SetInt( - shaderHandles.angleRatioDenominator, - shaderParameters.angleRatioDenominator - ); - material?.SetInt(shaderHandles.nativeViewCount, shaderParameters.nativeViewCount); - material?.SetInt(shaderHandles.leftLensOrientation, shaderParameters.leftLensOrientation); - material?.SetInt(shaderHandles.showTestFrame, 0); - material?.SetInt(shaderHandles.hqViewCount, shaderParameters.hqViewCount); - material?.SetInt(shaderHandles.BGRPixelLayout, shaderParameters.BGRPixelLayout); - material?.SetInt(shaderHandles.mstart, shaderParameters.mstart); - - int cameraCount = mosaicColumnCount * mosaicRowCount; - int shaderMaxCount = shaderParameters.nativeViewCount; - if (cameraCount > shaderMaxCount) - { - cameraCount = shaderMaxCount; - } + int cameraCount = mosaicColumnCount * mosaicRowCount; + int shaderMaxCount = shaderParameters.nativeViewCount; + if (cameraCount > shaderMaxCount) + { + cameraCount = shaderMaxCount; + } - material?.SetInt(Shader.PropertyToID("cameraCount"), cameraCount); + material?.SetInt(Shader.PropertyToID("cameraCount"), cameraCount); - material?.SetInt(Shader.PropertyToID("mirror"), mirrorViews ? 1 : 0); + material?.SetInt(Shader.PropertyToID("mirror"), mirrorViews ? 1 : 0); - material?.SetInt(Shader.PropertyToID("mosaic_rows"), mosaicRowCount); - material?.SetInt(Shader.PropertyToID("mosaic_columns"), mosaicColumnCount); + material?.SetInt(Shader.PropertyToID("mosaic_rows"), mosaicRowCount); + material?.SetInt(Shader.PropertyToID("mosaic_columns"), mosaicColumnCount); - material?.SetInt(Shader.PropertyToID("viewOffset"), viewOffset); + material?.SetInt(Shader.PropertyToID("viewOffset"), viewOffset); - material.SetInt(Shader.PropertyToID("indexMapLength"), indexMap.currentMap.Length); - material.SetFloatArray(Shader.PropertyToID("index_map"), indexMap.getPaddedIndexMapArray()); + material.SetInt(Shader.PropertyToID("indexMapLength"), indexMap.currentMap.Length); + material.SetFloatArray( + Shader.PropertyToID("index_map"), + indexMap.getPaddedIndexMapArray() + ); - material?.SetInt(Shader.PropertyToID("use_hq_views"), useHQViews ? 1 : 0); - } + material?.SetInt(Shader.PropertyToID("use_hq_views"), useHQViews ? 1 : 0); + } - private bool windowResized() - { - var window_dim = new Vector2Int(Screen.width, Screen.height); - if (cachedWindowSize != window_dim) + private bool windowResized() { - cachedWindowSize = window_dim; - return true; + var window_dim = new Vector2Int(Screen.width, Screen.height); + if (cachedWindowSize != window_dim) + { + cachedWindowSize = window_dim; + return true; + } + return false; } - return false; - } - private bool windowMoved() - { - var window_pos = new Vector2Int(Screen.mainWindowPosition.x, Screen.mainWindowPosition.y); - if (cachedWindowPosition != window_pos) + private bool windowMoved() { - cachedWindowPosition = window_pos; - return true; + var window_pos = new Vector2Int( + Screen.mainWindowPosition.x, + Screen.mainWindowPosition.y + ); + if (cachedWindowPosition != window_pos) + { + cachedWindowPosition = window_pos; + return true; + } + return false; } - return false; - } - // This function only does something when you use the SRP render pipeline. - // when using either URP or HRDP image combination is handled in the respective renderpasses. - // URP -> G3D.RenderPipeline.URP.ScriptableRP.cs - // HDRP -> G3D.RenderPipeline.HDRP.CustomPass.cs - void OnRenderImage(RenderTexture source, RenderTexture destination) - { - // This is where the material and shader are applied to the camera image. - //legacy support (no URP or HDRP) + // This function only does something when you use the SRP render pipeline. + // when using either URP or HRDP image combination is handled in the respective renderpasses. + // URP -> G3D.RenderPipeline.URP.ScriptableRP.cs + // HDRP -> G3D.RenderPipeline.HDRP.CustomPass.cs + void OnRenderImage(RenderTexture source, RenderTexture destination) + { + // This is where the material and shader are applied to the camera image. + //legacy support (no URP or HDRP) #if G3D_HDRP || URP #else - if (material == null) - Graphics.Blit(source, destination); - else - Graphics.Blit(source, destination, material); + if (material == null) + Graphics.Blit(source, destination); + else + Graphics.Blit(source, destination, material); #endif + } + #endregion } - #endregion } diff --git a/Scripts/CalibrationProvider.cs b/Scripts/CalibrationProvider.cs index 4dc3c9d..3119916 100644 --- a/Scripts/CalibrationProvider.cs +++ b/Scripts/CalibrationProvider.cs @@ -7,273 +7,276 @@ using UnityEngine; using UnityEngine.Networking; -public class CalibrationProvider +namespace G3D { - private IniData iniData; - - private CalibrationProvider() { } - - /// - /// - /// - /// - /// int return parameter can be ignored - /// - public static CalibrationProvider getFromURI( - string uri, - Func callback - ) + public class CalibrationProvider { - CalibrationProvider provider = new CalibrationProvider(); - if (uri == null || uri.Length == 0) + private IniData iniData; + + private CalibrationProvider() { } + + /// + /// + /// + /// + /// int return parameter can be ignored + /// + public static CalibrationProvider getFromURI( + string uri, + Func callback + ) { - return provider; - } + CalibrationProvider provider = new CalibrationProvider(); + if (uri == null || uri.Length == 0) + { + return provider; + } - UnityWebRequest webRequest = UnityWebRequest.Get(uri); - UnityWebRequestAsyncOperation asyncOperation = webRequest.SendWebRequest(); + UnityWebRequest webRequest = UnityWebRequest.Get(uri); + UnityWebRequestAsyncOperation asyncOperation = webRequest.SendWebRequest(); - asyncOperation.completed += (op) => - { - if (!webRequest.isDone && webRequest.result != UnityWebRequest.Result.Success) + asyncOperation.completed += (op) => { - Debug.LogError(webRequest.error); - return; - } + if (!webRequest.isDone && webRequest.result != UnityWebRequest.Result.Success) + { + Debug.LogError(webRequest.error); + return; + } - string calibrationData = webRequest.downloadHandler.text; + string calibrationData = webRequest.downloadHandler.text; - IniDataParser parser = new IniDataParser(); - provider.iniData = parser.Parse(calibrationData); + IniDataParser parser = new IniDataParser(); + provider.iniData = parser.Parse(calibrationData); - callback(provider); - }; + callback(provider); + }; - return provider; - } + return provider; + } - public static CalibrationProvider getFromConfigFile(string calibrationFile) - { - CalibrationProvider provider = new CalibrationProvider(); - if (calibrationFile == null || !File.Exists(calibrationFile)) + public static CalibrationProvider getFromConfigFile(string calibrationFile) { + CalibrationProvider provider = new CalibrationProvider(); + if (calibrationFile == null || !File.Exists(calibrationFile)) + { + return provider; + } + FileIniDataParser parser = new FileIniDataParser(); + provider.iniData = parser.ReadFile(calibrationFile); return provider; } - FileIniDataParser parser = new FileIniDataParser(); - provider.iniData = parser.ReadFile(calibrationFile); - return provider; - } - public static CalibrationProvider getFromString(string calibrationData) - { - CalibrationProvider provider = new CalibrationProvider(); - if (calibrationData == null || calibrationData.Length == 0) + public static CalibrationProvider getFromString(string calibrationData) { + CalibrationProvider provider = new CalibrationProvider(); + if (calibrationData == null || calibrationData.Length == 0) + { + return provider; + } + IniDataParser parser = new IniDataParser(); + provider.iniData = parser.Parse(calibrationData); return provider; } - IniDataParser parser = new IniDataParser(); - provider.iniData = parser.Parse(calibrationData); - return provider; - } - /// - /// - /// - /// - public G3DShaderParameters getShaderParameters() - { - G3DShaderParameters parameters = new G3DShaderParameters(); + /// + /// + /// + /// + public G3DShaderParameters getShaderParameters() + { + G3DShaderParameters parameters = new G3DShaderParameters(); - // display parameters + // display parameters #if UNITY_IOS - parameters.screenWidth = readOrDefault("HorizontalResolution", Screen.width); - parameters.screenHeight = readOrDefault("VerticalResolution", Screen.height); - parameters.leftViewportPosition = 0; //< The left position of the viewport in screen coordinates - parameters.bottomViewportPosition = 0; //< The bottom position of the viewport in screen coordinates + parameters.screenWidth = readOrDefault("HorizontalResolution", Screen.width); + parameters.screenHeight = readOrDefault("VerticalResolution", Screen.height); + parameters.leftViewportPosition = 0; //< The left position of the viewport in screen coordinates + parameters.bottomViewportPosition = 0; //< The bottom position of the viewport in screen coordinates #else - DisplayInfo mainDisplayInfo = Screen.mainWindowDisplayInfo; - parameters.screenWidth = readOrDefault("HorizontalResolution", mainDisplayInfo.width); - parameters.screenHeight = readOrDefault("VerticalResolution", mainDisplayInfo.height); - parameters.leftViewportPosition = Screen.mainWindowPosition.x; //< The left position of the viewport in screen coordinates - parameters.bottomViewportPosition = Screen.mainWindowPosition.y + Screen.height; //< The bottom position of the viewport in screen coordinates + DisplayInfo mainDisplayInfo = Screen.mainWindowDisplayInfo; + parameters.screenWidth = readOrDefault("HorizontalResolution", mainDisplayInfo.width); + parameters.screenHeight = readOrDefault("VerticalResolution", mainDisplayInfo.height); + parameters.leftViewportPosition = Screen.mainWindowPosition.x; //< The left position of the viewport in screen coordinates + parameters.bottomViewportPosition = Screen.mainWindowPosition.y + Screen.height; //< The bottom position of the viewport in screen coordinates #endif - // default values are those i got from the head tracking library when no camera was connected - parameters.nativeViewCount = getInt("NativeViewcount"); - parameters.angleRatioNumerator = getInt("AngleRatioNumerator"); - parameters.angleRatioDenominator = getInt("AngleRatioDenominator"); - parameters.leftLensOrientation = getInt("LeftLensOrientation"); - parameters.BGRPixelLayout = getBool("isBGR") ? 1 : 0; - parameters.blackBorder = getInt("BlackBorderDefault"); - parameters.blackSpace = getInt("BlackSpaceDefault"); - - parameters.showTestFrame = 0; - parameters.showTestStripe = 0; - parameters.testGapWidth = 0; - - parameters.blur = 0; // this is based on a guess - - parameters.mstart = 0; // set to zero for no viewOffset - parameters.track = 0; // set to zero for no tracking shift - - // lens parameters - // This is a total guess as to how this parameter is calculated. - parameters.hqViewCount = parameters.nativeViewCount * parameters.angleRatioDenominator; - parameters.hviews1 = parameters.hqViewCount - 1; - parameters.hviews2 = parameters.hqViewCount / 2; - - // bls and ble correspond to the start and end of the left eye view "window" - // brs and bre correspond to the start and end of the right eye view "window" - // my guess: the left eye takes half the views, and the right eye takes the other half - parameters.bls = 0; - parameters.ble = parameters.hviews2; - parameters.brs = parameters.hviews2; - parameters.bre = parameters.hqViewCount - 1; - - parameters.zCompensationValue = 0; // i got this value from the library when no camera was connected - parameters.zCorrectionValue = 13248; // i got this value from the library when no camera was connected - - return parameters; - } + // default values are those i got from the head tracking library when no camera was connected + parameters.nativeViewCount = getInt("NativeViewcount"); + parameters.angleRatioNumerator = getInt("AngleRatioNumerator"); + parameters.angleRatioDenominator = getInt("AngleRatioDenominator"); + parameters.leftLensOrientation = getInt("LeftLensOrientation"); + parameters.BGRPixelLayout = getBool("isBGR") ? 1 : 0; + parameters.blackBorder = getInt("BlackBorderDefault"); + parameters.blackSpace = getInt("BlackSpaceDefault"); + + parameters.showTestFrame = 0; + parameters.showTestStripe = 0; + parameters.testGapWidth = 0; + + parameters.blur = 0; // this is based on a guess + + parameters.mstart = 0; // set to zero for no viewOffset + parameters.track = 0; // set to zero for no tracking shift + + // lens parameters + // This is a total guess as to how this parameter is calculated. + parameters.hqViewCount = parameters.nativeViewCount * parameters.angleRatioDenominator; + parameters.hviews1 = parameters.hqViewCount - 1; + parameters.hviews2 = parameters.hqViewCount / 2; + + // bls and ble correspond to the start and end of the left eye view "window" + // brs and bre correspond to the start and end of the right eye view "window" + // my guess: the left eye takes half the views, and the right eye takes the other half + parameters.bls = 0; + parameters.ble = parameters.hviews2; + parameters.brs = parameters.hviews2; + parameters.bre = parameters.hqViewCount - 1; + + parameters.zCompensationValue = 0; // i got this value from the library when no camera was connected + parameters.zCorrectionValue = 13248; // i got this value from the library when no camera was connected + + return parameters; + } - /// - /// Returns the value of key from the ini file, or defaultValue if the key is not found or an error occurs. - /// - /// - /// - /// - private int readOrDefault(string key, int defaultValue) - { - if (iniData == null) + /// + /// Returns the value of key from the ini file, or defaultValue if the key is not found or an error occurs. + /// + /// + /// + /// + private int readOrDefault(string key, int defaultValue) { - return defaultValue; + if (iniData == null) + { + return defaultValue; + } + + try + { + string value = null; + iniData.TryGetKey("MonitorConfiguration." + key, out value); + + if (value == null) + { + return defaultValue; + } + int number; + if (int.TryParse(value, out number)) + { + return number; + } + + float floatNumber; + if (float.TryParse(value, out floatNumber)) + { + return (int)floatNumber; + } + + bool boolValue; + if (bool.TryParse(value, out boolValue)) + { + return boolValue ? 1 : 0; + } + + return defaultValue; + } + catch (System.Exception e) + { + Debug.LogWarning(e.Message); + return defaultValue; + } } - try + public int getInt(string key) { - string value = null; + if (iniData == null) + { + throw new System.Exception("iniData is null"); + } + + string value; iniData.TryGetKey("MonitorConfiguration." + key, out value); if (value == null) { - return defaultValue; + throw new System.Exception("Key not found: " + key); } int number; if (int.TryParse(value, out number)) { return number; } + throw new System.Exception("Error reading ini file. Value is not an int: " + value); + } - float floatNumber; - if (float.TryParse(value, out floatNumber)) + public float getFloat(string key) + { + if (iniData == null) { - return (int)floatNumber; + throw new System.Exception("iniData is null"); } - bool boolValue; - if (bool.TryParse(value, out boolValue)) + string value; + iniData.TryGetKey("MonitorConfiguration." + key, out value); + + if (value == null) { - return boolValue ? 1 : 0; + throw new System.Exception("Key not found: " + key); + } + float number; + if ( + float.TryParse( + value, + System.Globalization.NumberStyles.Float, + System.Globalization.CultureInfo.InvariantCulture, + out number + ) + ) + { + return number; } - return defaultValue; - } - catch (System.Exception e) - { - Debug.LogWarning(e.Message); - return defaultValue; - } - } - - public int getInt(string key) - { - if (iniData == null) - { - throw new System.Exception("iniData is null"); - } - - string value; - iniData.TryGetKey("MonitorConfiguration." + key, out value); - - if (value == null) - { - throw new System.Exception("Key not found: " + key); - } - int number; - if (int.TryParse(value, out number)) - { - return number; - } - throw new System.Exception("Error reading ini file. Value is not an int: " + value); - } - - public float getFloat(string key) - { - if (iniData == null) - { - throw new System.Exception("iniData is null"); + throw new System.Exception("Error reading ini file. Value is not a float: " + value); } - string value; - iniData.TryGetKey("MonitorConfiguration." + key, out value); - - if (value == null) + public string getString(string key) { - throw new System.Exception("Key not found: " + key); - } - float number; - if ( - float.TryParse( - value, - System.Globalization.NumberStyles.Float, - System.Globalization.CultureInfo.InvariantCulture, - out number - ) - ) - { - return number; - } - - throw new System.Exception("Error reading ini file. Value is not a float: " + value); - } + if (iniData == null) + { + throw new System.Exception("iniData is null"); + } - public string getString(string key) - { - if (iniData == null) - { - throw new System.Exception("iniData is null"); - } + string value; + iniData.TryGetKey("MonitorConfiguration." + key, out value); - string value; - iniData.TryGetKey("MonitorConfiguration." + key, out value); + if (value == null) + { + throw new System.Exception("Key not found: " + key); + } - if (value == null) - { - throw new System.Exception("Key not found: " + key); + return value; } - return value; - } - - public bool getBool(string key) - { - if (iniData == null) + public bool getBool(string key) { - throw new System.Exception("iniData is null"); - } + if (iniData == null) + { + throw new System.Exception("iniData is null"); + } - string value = null; - iniData.TryGetKey("MonitorConfiguration." + key, out value); + string value = null; + iniData.TryGetKey("MonitorConfiguration." + key, out value); - if (value == null) - { - throw new System.Exception("Key not found: " + key); - } - bool boolValue; - if (bool.TryParse(value, out boolValue)) - { - return boolValue; + if (value == null) + { + throw new System.Exception("Key not found: " + key); + } + bool boolValue; + if (bool.TryParse(value, out boolValue)) + { + return boolValue; + } + throw new System.Exception("Error reading ini file. Value is not a bool: " + value); } - throw new System.Exception("Error reading ini file. Value is not a bool: " + value); } } diff --git a/Scripts/HeadTracking/HeadTrackingPretender.cs b/Scripts/HeadTracking/HeadTrackingPretender.cs index b5a5acc..2954e3a 100644 --- a/Scripts/HeadTracking/HeadTrackingPretender.cs +++ b/Scripts/HeadTracking/HeadTrackingPretender.cs @@ -1,36 +1,39 @@ using UnityEngine; -/// -/// This class can be used to simulate head tracking if no head tracking device is available. -/// Simply attach it to a GameObject and move the GameObject around to simulate head tracking. -/// The game objects transform will be used to simulate the head position. -/// (In meter -> 0.7 z equals 70 centimiter from the head tracking camera) -/// -public class HeadTrackingPretender : MonoBehaviour +namespace G3D { - public G3DCamera g3dCamera; - public bool headDetected = true; + /// + /// This class can be used to simulate head tracking if no head tracking device is available. + /// Simply attach it to a GameObject and move the GameObject around to simulate head tracking. + /// The game objects transform will be used to simulate the head position. + /// (In meter -> 0.7 z equals 70 centimiter from the head tracking camera) + /// + public class HeadTrackingPretender : MonoBehaviour + { + public G3DCamera g3dCamera; + public bool headDetected = true; - public float initialOffsetZ = 0.7f; + public float initialOffsetZ = 0.7f; - // Start is called before the first frame update - void Start() { } + // Start is called before the first frame update + void Start() { } - // Update is called once per frame - void Update() - { - Vector3 headPosition = transform.localPosition; - headPosition = -headPosition; - headPosition = headPosition * 1000; - headPosition.z += initialOffsetZ * 1000; - ((ITNewHeadPositionCallback)g3dCamera.headtrackingConnection).NewHeadPositionCallback( - headDetected, - true, - (int)headPosition.x, - (int)headPosition.y, - headPosition.x, - headPosition.y, - headPosition.z - ); + // Update is called once per frame + void Update() + { + Vector3 headPosition = transform.localPosition; + headPosition = -headPosition; + headPosition = headPosition * 1000; + headPosition.z += initialOffsetZ * 1000; + ((ITNewHeadPositionCallback)g3dCamera.headtrackingConnection).NewHeadPositionCallback( + headDetected, + true, + (int)headPosition.x, + (int)headPosition.y, + headPosition.x, + headPosition.y, + headPosition.z + ); + } } } diff --git a/Scripts/HeadTracking/HeadtrackingConnection.cs b/Scripts/HeadTracking/HeadtrackingConnection.cs new file mode 100644 index 0000000..726d1ea --- /dev/null +++ b/Scripts/HeadTracking/HeadtrackingConnection.cs @@ -0,0 +1,737 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +namespace G3D +{ + public class HeadtrackingConnection + : ITNewHeadPositionCallback, + ITNewShaderParametersCallback, + ITNewErrorMessageCallback + { + [Tooltip("Time it takes till the reset animation starts in seconds.")] + public float headLostTimeoutInSec = 3.0f; + + [Tooltip("Reset animation duratuion in seconds.")] + public float transitionDuration = 0.5f; + + public Vector3Int headPositionFilter = new Vector3Int(5, 5, 5); + public LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE; + + public float focusDistance = 1.0f; + public float headTrackingScale = 1.0f; + public float sceneScaleFactor = 1.0f; + + private bool debugMessages; + + private Vector3 lastHeadPosition = new Vector3(0, 0, 0); + + private float headLostTimer = 0.0f; + private float transitionTime = 0.0f; + + private enum HeadTrackingState + { + TRACKING, + LOST, + TRANSITIONTOLOST, + TRANSITIONTOTRACKING, + LOSTGRACEPERIOD + } + + private HeadTrackingState prevHeadTrackingState = HeadTrackingState.LOST; + + private LibInterface libInterface; + private string calibrationPath; + + /// + /// This struct is used to store the current head position. + /// It is updated in a different thread, so always use getHeadPosition() to get the current head position. + /// NEVER use headPosition directly. + /// + private HeadPosition headPosition; + private HeadPosition filteredHeadPosition; + + private static object headPosLock = new object(); + private static object shaderLock = new object(); + + private Queue headPositionLog; + + private G3DCamera g3dCamera; + + public HeadtrackingConnection( + float focusDistance, + float headTrackingScale, + float sceneScaleFactor, + string calibrationPathOverwrite, + G3DCamera g3dCamera, + bool debugMessages = false, + Vector3Int headPositionFilter = new Vector3Int(), + LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE + ) + { + lastHeadPosition = new Vector3(0, 0, -focusDistance); + this.focusDistance = focusDistance; + this.headTrackingScale = headTrackingScale; + this.sceneScaleFactor = sceneScaleFactor; + + calibrationPath = System.Environment.GetFolderPath( + Environment.SpecialFolder.CommonDocuments + ); + calibrationPath = Path.Combine(calibrationPath, "3D Global", "calibrations"); + if (!string.IsNullOrEmpty(calibrationPathOverwrite)) + { + calibrationPath = calibrationPathOverwrite; + } + + this.debugMessages = debugMessages; + this.headPositionFilter = headPositionFilter; + this.latencyCorrectionMode = latencyCorrectionMode; + + this.g3dCamera = g3dCamera; + + headPositionLog = new Queue(10000); + } + + public void startHeadTracking() + { + try + { + libInterface.startHeadTracking(); + } + catch (Exception e) + { + Debug.LogError("Failed to start head tracking: " + e.Message); + } + } + + public void shiftViewToLeft() + { + try + { + libInterface.shiftViewToLeft(); + } + catch (Exception e) + { + Debug.LogError("Failed to shift view to left: " + e.Message); + } + } + + public void shiftViewToRight() + { + try + { + libInterface.shiftViewToRight(); + } + catch (Exception e) + { + Debug.LogError("Failed to shift view to right: " + e.Message); + } + } + + public void toggleHeadTracking() + { + if (libInterface == null || !libInterface.isInitialized()) + { + return; + } + + try + { + HeadTrackingStatus headtrackingConnection = libInterface.getHeadTrackingStatus(); + if (headtrackingConnection.hasTrackingDevice) + { + if (!headtrackingConnection.isTrackingActive) + { + libInterface.startHeadTracking(); + } + else + { + libInterface.stopHeadTracking(); + } + } + } + catch (Exception e) + { + Debug.LogError("Failed to toggle head tracking status: " + e.Message); + } + } + + /// + /// always use this method to get the current head position. + /// NEVER access headPosition directly, as it is updated in a different thread. + /// + /// + /// + public HeadPosition getHeadPosition() + { + HeadPosition currentHeadPosition; + lock (headPosLock) + { + if (usePositionFiltering()) + { + currentHeadPosition = filteredHeadPosition; + } + else + { + currentHeadPosition = headPosition; + } + } + return currentHeadPosition; + } + + /** + * Calculate the new camera center position based on the head tracking. + * If head tracking is lost, or the head moves to far away from the tracking camera a grace periope is started. + * Afterwards the camera center will be animated back towards the default position. + */ + public void handleHeadTrackingState( + ref Vector3 targetPosition, + ref float targetViewSeparation, + float viewSeparation, + float focusDistance + ) + { + HeadPosition headPos = getHeadPosition(); + // get new state + HeadTrackingState newState = getNewTrackingState(prevHeadTrackingState, ref headPos); + + prevHeadTrackingState = newState; + + // handle lost state + if (newState == HeadTrackingState.LOST) + { + targetPosition = new Vector3(0, 0, -focusDistance); + targetViewSeparation = 0.0f; + } + // handle tracking state + else if (newState == HeadTrackingState.TRACKING) + { + Vector3 headPositionWorld = new Vector3( + (float)headPos.worldPosX, + (float)headPos.worldPosY, + (float)headPos.worldPosZ + ); + + targetPosition = headPositionWorld; + targetViewSeparation = viewSeparation; + lastHeadPosition = targetPosition; + } + // if lost, start grace period + else if (newState == HeadTrackingState.LOSTGRACEPERIOD) + { + // if we have waited for the timeout + if (headLostTimer > headLostTimeoutInSec) + { + newState = HeadTrackingState.TRANSITIONTOLOST; + headLostTimer = 0.0f; + transitionTime = 0.0f; + } + else + { + headLostTimer += Time.deltaTime; + targetPosition = lastHeadPosition; + targetViewSeparation = viewSeparation; + } + } + // handle transitions + else if ( + newState == HeadTrackingState.TRANSITIONTOLOST + || newState == HeadTrackingState.TRANSITIONTOTRACKING + ) + { + // init with values for transition to lost + Vector3 originPosition = lastHeadPosition; + Vector3 transitionTargetPosition = new Vector3(0, 0, -focusDistance); + float transitionViewSeparation = 0.0f; + float originSeparation = viewSeparation; + + if (newState == HeadTrackingState.TRANSITIONTOTRACKING) + { + originPosition = new Vector3(0, 0, -focusDistance); + transitionViewSeparation = viewSeparation; + originSeparation = 0.0f; + + if (headPos.headDetected) + { + Vector3 headPositionWorld = new Vector3( + (float)headPos.worldPosX, + (float)headPos.worldPosY, + (float)headPos.worldPosZ + ); + transitionTargetPosition = headPositionWorld; + } + else + { + // if no head is detected use last known head position + transitionTargetPosition = lastHeadPosition; + } + } + + bool isEndReached = handleTransition( + originPosition, + transitionTargetPosition, + originSeparation, + transitionViewSeparation, + ref targetPosition, + ref targetViewSeparation + ); + if (isEndReached) + { + transitionTime = 0.0f; + // if we have reached the target position, we are no longer in transition + if (newState == HeadTrackingState.TRANSITIONTOLOST) + { + newState = HeadTrackingState.LOST; + } + else + { + newState = HeadTrackingState.TRACKING; + } + } + } + + // reset lost timer if we are not in grace period + if (newState != HeadTrackingState.LOSTGRACEPERIOD) + { + headLostTimer = 0.0f; + } + + prevHeadTrackingState = newState; + } + + public void initLibrary() + { + string applicationName = Application.productName; + if (string.IsNullOrEmpty(applicationName)) + { + applicationName = "Unity"; + } + var invalids = System.IO.Path.GetInvalidFileNameChars(); + applicationName = String + .Join("_", applicationName.Split(invalids, StringSplitOptions.RemoveEmptyEntries)) + .TrimEnd('.'); + applicationName = applicationName + "_G3D_Config.ini"; + + try + { + bool useHimaxD2XXDevices = true; + bool useHimaxRP2040Devices = true; + bool usePmdFlexxDevices = true; + + libInterface = LibInterface.Instance; + libInterface.init( + calibrationPath, + Application.persistentDataPath, + applicationName, + this, + this, + this, + debugMessages, + useHimaxD2XXDevices, + useHimaxRP2040Devices, + usePmdFlexxDevices + ); + } + catch (Exception e) + { + Debug.LogError("Failed to initialize library: " + e.Message); + return; + } + + // set initial values + // intialize head position at focus distance from focus plane + headPosition = new HeadPosition + { + headDetected = false, + imagePosIsValid = false, + imagePosX = 0, + imagePosY = 0, + worldPosX = 0.0, + worldPosY = 0.0, + worldPosZ = -focusDistance + }; + filteredHeadPosition = new HeadPosition + { + headDetected = false, + imagePosIsValid = false, + imagePosX = 0, + imagePosY = 0, + worldPosX = 0.0, + worldPosY = 0.0, + worldPosZ = -focusDistance + }; + + if (usePositionFiltering()) + { + try + { + libInterface.initializePositionFilter( + headPositionFilter.x, + headPositionFilter.y, + headPositionFilter.z + ); + } + catch (Exception e) + { + Debug.LogError("Failed to initialize position filter: " + e.Message); + } + } + } + + public void deinitLibrary() + { + if (libInterface == null || !libInterface.isInitialized()) + { + return; + } + + try + { + libInterface.stopHeadTracking(); + libInterface.unregisterHeadPositionChangedCallback(this); + libInterface.unregisterShaderParametersChangedCallback(this); + libInterface.unregisterMessageCallback(this); + libInterface.deinit(); + } + catch (Exception e) + { + Debug.Log(e); + } + } + + public void logCameraPositionsToFile() + { + StreamWriter writer = new StreamWriter( + Application.dataPath + "/HeadPositionLog.csv", + false + ); + writer.WriteLine( + "Camera update time; Camera X; Camera Y; Camera Z; Head detected; Image position valid; Filtered X; Filtered Y; Filtered Z" + ); + string[] headPoitionLogArray = headPositionLog.ToArray(); + for (int i = 0; i < headPoitionLogArray.Length; i++) + { + writer.WriteLine(headPoitionLogArray[i]); + } + writer.Close(); + } + + public void calculateShaderParameters() + { + libInterface.calculateShaderParameters(latencyCorrectionMode); + g3dCamera.setShaderParameters(libInterface.getCurrentShaderParameters()); + } + + public void updateScreenViewportProperties(Vector2Int displayResolution) + { + try + { + // This is the size of the entire monitor screen + libInterface.setScreenSize(displayResolution.x, displayResolution.y); + + // this refers to the window in which the 3D effect is rendered (including eg windows top window menu) + libInterface.setWindowSize(Screen.width, Screen.height); + libInterface.setWindowPosition( + Screen.mainWindowPosition.x, + Screen.mainWindowPosition.y + ); + + // This refers to the actual viewport in which the 3D effect is rendered + libInterface.setViewportSize(Screen.width, Screen.height); + libInterface.setViewportOffset(0, 0); + } + catch (Exception e) + { + Debug.LogError("Failed to update screen viewport properties: " + e.Message); + } + } + + /// + /// returns true if transition end is reached + /// + /// + /// + /// + /// + /// + /// is transition end reached + private bool handleTransition( + Vector3 originPosition, + Vector3 transitionTargetPosition, + float originSeparation, + float transitionViewSeparation, + ref Vector3 targetPosition, + ref float targetViewSeparation + ) + { + // interpolate values + float transitionPercentage = transitionTime / transitionDuration; + transitionTime += Time.deltaTime; + + // set to default position + Vector3 interpolatedPosition = Vector3.Lerp( + originPosition, + transitionTargetPosition, + transitionPercentage + ); + float interpolatedEyeSeparation = Mathf.Lerp( + originSeparation, + transitionViewSeparation, + transitionPercentage + ); + + // check if end is reached (with a small tolerance) + if (transitionPercentage + 0.01f < 1.0f) + { + // apply values + targetPosition = interpolatedPosition; + targetViewSeparation = interpolatedEyeSeparation; + return false; + } + else + { + return true; + } + } + + private HeadTrackingState getNewTrackingState( + HeadTrackingState currentHeadTrackingState, + ref HeadPosition headPosition + ) + { + HeadTrackingState newState; + + if (headPosition.headDetected) + { + // if head detected + if (currentHeadTrackingState == HeadTrackingState.TRACKING) + { + newState = HeadTrackingState.TRACKING; + } + else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD) + { + newState = HeadTrackingState.TRACKING; + } + else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING) + { + newState = HeadTrackingState.TRANSITIONTOTRACKING; + } + else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST) + { + newState = HeadTrackingState.TRANSITIONTOLOST; + } + else //(currentHeadTrackingState == HeadTrackingState.LOST) + { + newState = HeadTrackingState.TRANSITIONTOTRACKING; + } + } + else + { + // if head not detected + if (currentHeadTrackingState == HeadTrackingState.TRACKING) + { + newState = HeadTrackingState.LOSTGRACEPERIOD; + } + else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD) + { + newState = HeadTrackingState.LOSTGRACEPERIOD; + } + else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING) + { + newState = HeadTrackingState.TRANSITIONTOTRACKING; + } + else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST) + { + newState = HeadTrackingState.TRANSITIONTOLOST; + } + else //(currentHeadTrackingState == HeadTrackingState.LOST) + { + newState = HeadTrackingState.LOST; + } + } + return newState; + } + + private string headTrackingStateToString() + { + switch (prevHeadTrackingState) + { + case HeadTrackingState.TRACKING: + return "TRACKING"; + case HeadTrackingState.LOST: + return "LOST"; + case HeadTrackingState.LOSTGRACEPERIOD: + return "LOSTGRACEPERIOD"; + case HeadTrackingState.TRANSITIONTOLOST: + return "TRANSITIONTOLOST"; + case HeadTrackingState.TRANSITIONTOTRACKING: + return "TRANSITIONTOTRACKING"; + default: + return "UNKNOWN"; + } + } + + #region callback handling + void ITNewHeadPositionCallback.NewHeadPositionCallback( + bool headDetected, + bool imagePosIsValid, + int imagePosX, + int imagePosY, + double worldPosX, + double worldPosY, + double worldPosZ + ) + { + lock (headPosLock) + { + string logEntry = + DateTime.Now.ToString("HH:mm::ss.fff") + + ";" + + worldPosX + + ";" + + worldPosY + + ";" + + worldPosZ + + ";" + + headDetected + + ";" + + imagePosIsValid + + ";"; + + headPosition.headDetected = headDetected; + headPosition.imagePosIsValid = imagePosIsValid; + + int millimeterToMeter = 1000; + + Vector3 headPos = new Vector3( + (float)-worldPosX / millimeterToMeter, + (float)worldPosY / millimeterToMeter, + (float)-worldPosZ / millimeterToMeter + ); + + int scaleFactorInt = (int)sceneScaleFactor * (int)headTrackingScale; + float scaleFactor = sceneScaleFactor * headTrackingScale; + + headPosition.imagePosX = imagePosX / (int)millimeterToMeter * scaleFactorInt; + headPosition.imagePosY = imagePosY / (int)millimeterToMeter * scaleFactorInt; + headPosition.worldPosX = headPos.x * scaleFactor; + headPosition.worldPosY = headPos.y * scaleFactor; + headPosition.worldPosZ = headPos.z * sceneScaleFactor; + + if (usePositionFiltering()) + { + double filteredPositionX; + double filteredPositionY; + double filteredPositionZ; + + if (headDetected) + { + try + { + libInterface.applyPositionFilter( + worldPosX, + worldPosY, + worldPosZ, + out filteredPositionX, + out filteredPositionY, + out filteredPositionZ + ); + + filteredHeadPosition.worldPosX = + -filteredPositionX / millimeterToMeter * scaleFactor; + filteredHeadPosition.worldPosY = + filteredPositionY / millimeterToMeter * scaleFactor; + filteredHeadPosition.worldPosZ = + -filteredPositionZ / millimeterToMeter * sceneScaleFactor; + } + catch (Exception e) + { + Debug.LogError("Failed to apply position filter: " + e.Message); + } + } + + filteredHeadPosition.headDetected = headDetected; + filteredHeadPosition.imagePosIsValid = imagePosIsValid; + + logEntry += + filteredHeadPosition.worldPosX + + ";" + + filteredHeadPosition.worldPosY + + ";" + + filteredHeadPosition.worldPosZ + + ";"; + } + + headPositionLog.Enqueue(logEntry); + } + } + + void ITNewErrorMessageCallback.NewErrorMessageCallback( + EMessageSeverity severity, + string sender, + string caption, + string cause, + string remedy + ) + { + string messageText = formatErrorMessage(caption, cause, remedy); + switch (severity) + { + case EMessageSeverity.MS_EXCEPTION: + Debug.LogError(messageText); + break; + case EMessageSeverity.MS_ERROR: + Debug.LogError(messageText); + break; + case EMessageSeverity.MS_WARNING: + Debug.LogWarning(messageText); + break; + case EMessageSeverity.MS_INFO: + + Debug.Log(messageText); + break; + default: + Debug.Log(messageText); + break; + } + } + + /// + /// The shader parameters contain everything necessary for the shader to render the 3D effect. + /// These are updated every time a new head position is received. + /// They do not update the head position itself. + /// + /// + void ITNewShaderParametersCallback.NewShaderParametersCallback( + G3DShaderParameters shaderParameters + ) + { + g3dCamera.setShaderParameters(shaderParameters); + } + + private string formatErrorMessage(string caption, string cause, string remedy) + { + string messageText = caption + ": " + cause; + + if (string.IsNullOrEmpty(remedy) == false) + { + messageText = messageText + "\n" + remedy; + } + + return messageText; + } + #endregion + + /// + /// Returns false if all values of the position filter are set to zero. + /// + /// + private bool usePositionFiltering() + { + return headPositionFilter.x != 0 + || headPositionFilter.y != 0 + || headPositionFilter.z != 0; + } + } +} diff --git a/Scripts/HeadtrackingConnection.cs.meta b/Scripts/HeadTracking/HeadtrackingConnection.cs.meta similarity index 100% rename from Scripts/HeadtrackingConnection.cs.meta rename to Scripts/HeadTracking/HeadtrackingConnection.cs.meta diff --git a/Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs b/Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs index dbd2778..829d503 100644 --- a/Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs +++ b/Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs @@ -1,18 +1,21 @@ -public delegate void TNewErrorMessageCallback( - EMessageSeverity severity, - string sender, - string caption, - string cause, - string remedy -); - -public interface ITNewErrorMessageCallback +namespace G3D { - public void NewErrorMessageCallback( + public delegate void TNewErrorMessageCallback( EMessageSeverity severity, string sender, string caption, string cause, string remedy ); + + public interface ITNewErrorMessageCallback + { + public void NewErrorMessageCallback( + EMessageSeverity severity, + string sender, + string caption, + string cause, + string remedy + ); + } } diff --git a/Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs b/Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs index 5672362..8f7fb2b 100644 --- a/Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs +++ b/Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs @@ -1,16 +1,6 @@ -public delegate void TNewHeadPositionCallback( - bool headDetected, - bool imagePosIsValid, - int imagePosX, - int imagePosY, - double worldPosX, - double worldPosY, - double worldPosZ -); - -public interface ITNewHeadPositionCallback +namespace G3D { - public void NewHeadPositionCallback( + public delegate void TNewHeadPositionCallback( bool headDetected, bool imagePosIsValid, int imagePosX, @@ -19,4 +9,17 @@ public void NewHeadPositionCallback( double worldPosY, double worldPosZ ); + + public interface ITNewHeadPositionCallback + { + public void NewHeadPositionCallback( + bool headDetected, + bool imagePosIsValid, + int imagePosX, + int imagePosY, + double worldPosX, + double worldPosY, + double worldPosZ + ); + } } diff --git a/Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs b/Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs index c27c151..c8d267a 100644 --- a/Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs +++ b/Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs @@ -1,6 +1,9 @@ -public delegate void TNewShaderParametersCallback(G3DShaderParameters shaderParameters); - -public interface ITNewShaderParametersCallback +namespace G3D { - public void NewShaderParametersCallback(G3DShaderParameters shaderParameters); + public delegate void TNewShaderParametersCallback(G3DShaderParameters shaderParameters); + + public interface ITNewShaderParametersCallback + { + public void NewShaderParametersCallback(G3DShaderParameters shaderParameters); + } } diff --git a/Scripts/HeadTracking/LibInterface.cs b/Scripts/HeadTracking/LibInterface.cs index 22fa9ef..d6ac765 100644 --- a/Scripts/HeadTracking/LibInterface.cs +++ b/Scripts/HeadTracking/LibInterface.cs @@ -2,162 +2,231 @@ using System.Runtime.InteropServices; using UnityEngine; -[UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void TNewShaderParametersCallbackInternal( - in G3DShaderParameters shaderParameters, - IntPtr listener -); - -[UnmanagedFunctionPointer(CallingConvention.Cdecl)] -internal delegate void TNewHeadPositionCallbackInternal( - int headDetected, - int imagePosIsValid, - int imagePosX, - int imagePosY, - double worldPosX, - double worldPosY, - double worldPosZ, - IntPtr listener -); - -[UnmanagedFunctionPointer(CallingConvention.Cdecl)] -internal delegate void TNewErrorMessageCallbackInternal( - EMessageSeverity severity, - IntPtr sender, - IntPtr caption, - IntPtr cause, - IntPtr remedy, - IntPtr listener -); - -public enum EMessageSeverity +namespace G3D { - MS_INFO = 1, - MS_WARNING, - MS_ERROR, - MS_EXCEPTION -}; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void TNewShaderParametersCallbackInternal( + in G3DShaderParameters shaderParameters, + IntPtr listener + ); -public enum LatencyCorrectionMode -{ - LCM_SIMPLE = 0, //< Simple or No LatencyCorrection (position prediction) - without timestamp from render event / paintGL - LCM_EXTENDED = 1, //< Extended prediction - considering timestamp from render event / paintGL -} + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void TNewHeadPositionCallbackInternal( + int headDetected, + int imagePosIsValid, + int imagePosX, + int imagePosY, + double worldPosX, + double worldPosY, + double worldPosZ, + IntPtr listener + ); -public struct G3DShaderParameters -{ - // Viewport properties - public int leftViewportPosition; //< The left position of the viewport in screen coordinates - public int bottomViewportPosition; //< The bottom position of the viewport in screen coordinates - - // Monitor properties - public int screenWidth; //< The screen width in pixels - public int screenHeight; //< The screen height in pixels - - public int nativeViewCount; // OLD: viewcount - public int angleRatioNumerator; // OLD: zwinkel - public int angleRatioDenominator; // OLD: nwinkel - public int leftLensOrientation; // OLD: isleft - public int BGRPixelLayout; // OLD: isbgr - - public int mstart; // TODO: rename to viewOffset - public int showTestFrame; // OLD: test - public int showTestStripe; // OLD: stest - public int testGapWidth; // OLD: testgap - public int track; - public int hqViewCount; // OLD: hqview - public int hviews1; - public int hviews2; - public int blur; - public int blackBorder; // OLD: bborder - public int blackSpace; // OLD: bspace - public int bls; - public int ble; - public int brs; - public int bre; - - public int zCorrectionValue; // OLD: tvx - public int zCompensationValue; // OLD: zkom -}; - -public struct HeadTrackingStatus -{ - public bool hasTrackingDevice; - public bool isTrackingActive; -} + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void TNewErrorMessageCallbackInternal( + EMessageSeverity severity, + IntPtr sender, + IntPtr caption, + IntPtr cause, + IntPtr remedy, + IntPtr listener + ); -public sealed class LibInterface -{ - public bool logToConsole = true; - private TNewHeadPositionCallbackInternal newHeadPosCppCallback; - private TNewShaderParametersCallbackInternal newShaderparamsCppCallback; - TNewErrorMessageCallbackInternal newErrorMessagesCppCallback; + public enum EMessageSeverity + { + MS_INFO = 1, + MS_WARNING, + MS_ERROR, + MS_EXCEPTION + }; - private static readonly LibInterface internalInstance = new LibInterface(); + public enum LatencyCorrectionMode + { + LCM_SIMPLE = 0, //< Simple or No LatencyCorrection (position prediction) - without timestamp from render event / paintGL + LCM_EXTENDED = 1, //< Extended prediction - considering timestamp from render event / paintGL + } - // Explicit static constructor to tell C# compiler - // not to mark type as beforefieldinit - static LibInterface() { } + public struct G3DShaderParameters + { + // Viewport properties + public int leftViewportPosition; //< The left position of the viewport in screen coordinates + public int bottomViewportPosition; //< The bottom position of the viewport in screen coordinates + + // Monitor properties + public int screenWidth; //< The screen width in pixels + public int screenHeight; //< The screen height in pixels + + public int nativeViewCount; // OLD: viewcount + public int angleRatioNumerator; // OLD: zwinkel + public int angleRatioDenominator; // OLD: nwinkel + public int leftLensOrientation; // OLD: isleft + public int BGRPixelLayout; // OLD: isbgr + + public int mstart; // TODO: rename to viewOffset + public int showTestFrame; // OLD: test + public int showTestStripe; // OLD: stest + public int testGapWidth; // OLD: testgap + public int track; + public int hqViewCount; // OLD: hqview + public int hviews1; + public int hviews2; + public int blur; + public int blackBorder; // OLD: bborder + public int blackSpace; // OLD: bspace + public int bls; + public int ble; + public int brs; + public int bre; + + public int zCorrectionValue; // OLD: tvx + public int zCompensationValue; // OLD: zkom + }; + + public struct HeadTrackingStatus + { + public bool hasTrackingDevice; + public bool isTrackingActive; + } - private LibInterface() + public sealed class LibInterface { - newHeadPosCppCallback = new TNewHeadPositionCallbackInternal( - TranslateNewHeadPositionCallback - ); + public bool logToConsole = true; + private TNewHeadPositionCallbackInternal newHeadPosCppCallback; + private TNewShaderParametersCallbackInternal newShaderparamsCppCallback; + TNewErrorMessageCallbackInternal newErrorMessagesCppCallback; + + private static readonly LibInterface internalInstance = new LibInterface(); + + // Explicit static constructor to tell C# compiler + // not to mark type as beforefieldinit + static LibInterface() { } + + private LibInterface() + { + newHeadPosCppCallback = new TNewHeadPositionCallbackInternal( + TranslateNewHeadPositionCallback + ); + + newShaderparamsCppCallback = new TNewShaderParametersCallbackInternal( + TranslateShaderParametersCallback + ); + + newErrorMessagesCppCallback = new TNewErrorMessageCallbackInternal( + TranslateNewErrorMessageCallback + ); + } + + public static LibInterface Instance + { + get { return internalInstance; } + } + + private bool initialized = false; + + /// + /// Initializes the G3D head tracking library. If it is already initialized, this function only registers the callbacks. + /// If the library is already initialized, this function will not set the useHimaxD2XXDevice and usePmdFlexxDevice Flag, neither the logToConsole flag. + /// + /// This function has to be called before any other function of this class is called. + /// + /// This function does not start the head tracking. To start the head tracking, call startHeadTracking(). + /// + /// You do not have to call the destructor manually. It is called, when the last reference to the singeleton is removed. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void init( + string calibrationPath, + string configPath, + string configFileName, + in ITNewHeadPositionCallback newHeadInferfaceInstance, + in ITNewShaderParametersCallback shaderInterfaceInstance, + in ITNewErrorMessageCallback errorInterfaceInstance, + bool logToConsole = true, + bool useHimaxD2XXDevice = true, + bool useHimaxRP2040Device = true, + bool usePmdFlexxDevice = true + ) + { + // register callbacks in case the library is already initialized + // afterwards, return + if (initialized) + { + try + { + registerHeadPositionChangedCallback(newHeadInferfaceInstance); + } + catch (Exception e) + { + Debug.LogError(e); + } + try + { + registerShaderParametersChangedCallback(shaderInterfaceInstance); + } + catch (Exception e) + { + Debug.LogError(e); + } + try + { + registerMessageCallback(errorInterfaceInstance); + } + catch (Exception e) + { + Debug.LogError(e); + } + + Debug.Log("G3D head tracking library is already initialized."); + return; + } - newShaderparamsCppCallback = new TNewShaderParametersCallbackInternal( - TranslateShaderParametersCallback - ); + this.logToConsole = logToConsole; - newErrorMessagesCppCallback = new TNewErrorMessageCallbackInternal( - TranslateNewErrorMessageCallback - ); - } + if (calibrationPath == "" || configPath == "" || configFileName == "") + { + throw new System.Exception( + "G3D head tracking library: Configuration paths have to be set. Aborting initialization." + ); + } - public static LibInterface Instance - { - get { return internalInstance; } - } + try + { + initLibrary(); + } + catch (G3D_AlreadyInitializedException) + { + Debug.Log("G3D head tracking library has already been initialized."); + } + catch (Exception e) + { + throw e; + } - private bool initialized = false; + try + { + setCalibrationPath(calibrationPath); + setConfigPath(configPath); + setConfigFileName(configFileName); + } + catch (Exception e) + { + Debug.LogError(e); + deinitLibrary(); + throw new System.Exception( + "G3D head tracking library: an error occured when setting the calibration paths. Aborting initialization" + ); + } - /// - /// Initializes the G3D head tracking library. If it is already initialized, this function only registers the callbacks. - /// If the library is already initialized, this function will not set the useHimaxD2XXDevice and usePmdFlexxDevice Flag, neither the logToConsole flag. - /// - /// This function has to be called before any other function of this class is called. - /// - /// This function does not start the head tracking. To start the head tracking, call startHeadTracking(). - /// - /// You do not have to call the destructor manually. It is called, when the last reference to the singeleton is removed. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public void init( - string calibrationPath, - string configPath, - string configFileName, - in ITNewHeadPositionCallback newHeadInferfaceInstance, - in ITNewShaderParametersCallback shaderInterfaceInstance, - in ITNewErrorMessageCallback errorInterfaceInstance, - bool logToConsole = true, - bool useHimaxD2XXDevice = true, - bool useHimaxRP2040Device = true, - bool usePmdFlexxDevice = true - ) - { - // register callbacks in case the library is already initialized - // afterwards, return - if (initialized) - { + // register callbacks try { registerHeadPositionChangedCallback(newHeadInferfaceInstance); @@ -183,2002 +252,1956 @@ public void init( Debug.LogError(e); } - Debug.Log("G3D head tracking library is already initialized."); - return; - } + try + { + if (useHimaxD2XXDevice) + { + useHimaxD2XXDevices(); + } + } + catch (Exception e) + { + Debug.LogError(e); + } + try + { + if (useHimaxRP2040Device) + { + useHimaxRP2040Devices(); + } + } + catch (Exception e) + { + Debug.LogError(e); + } + try + { + if (usePmdFlexxDevice) + { + usePmdFlexxDevices(); + } + } + catch (Exception e) + { + Debug.LogError(e); + } - this.logToConsole = logToConsole; + try + { + initHeadTracking(); + } + catch (G3D_AlreadyInitializedException) + { + Debug.Log("G3D head tracking has already been initialized."); + } + catch (Exception e) + { + throw e; + } + Debug.Log("tracking device count " + getHeadTrackingDeviceCount()); - if (calibrationPath == "" || configPath == "" || configFileName == "") - { - throw new System.Exception( - "G3D head tracking library: Configuration paths have to be set. Aborting initialization." - ); + initialized = true; } - try - { - initLibrary(); - } - catch (G3D_AlreadyInitializedException) - { - Debug.Log("G3D head tracking library has already been initialized."); - } - catch (Exception e) + public void deinit() { - throw e; - } + // this is in its own try catch to continue deinit if this fails + try + { + stopHeadTracking(); + } + catch (Exception e) + { + // only log this if actually initialized. + // otherwise this was probably called from an uninitialized state and the exception reflects that. + if (initialized) + { + Debug.LogError(e.ToString()); + } + } - try - { - setCalibrationPath(calibrationPath); - setConfigPath(configPath); - setConfigFileName(configFileName); - } - catch (Exception e) - { - Debug.LogError(e); - deinitLibrary(); - throw new System.Exception( - "G3D head tracking library: an error occured when setting the calibration paths. Aborting initialization" - ); - } + // this is in its own try catch to continue deinit if this fails + try + { + deinitHeadTracking(); + } + catch (Exception e) + { + if (initialized) + { + Debug.LogError(e.ToString()); + } + } - // register callbacks - try - { - registerHeadPositionChangedCallback(newHeadInferfaceInstance); - } - catch (Exception e) - { - Debug.LogError(e); - } - try - { - registerShaderParametersChangedCallback(shaderInterfaceInstance); - } - catch (Exception e) - { - Debug.LogError(e); + // this is in its own try catch to continue deinit if this fails + try + { + deinitLibrary(); + } + catch (Exception e) + { + if (initialized) + { + Debug.LogError(e.ToString()); + } + } + + Debug.Log("destroy library"); + + initialized = false; } - try + + ~LibInterface() { - registerMessageCallback(errorInterfaceInstance); + deinit(); } - catch (Exception e) + + public bool isInitialized() { - Debug.LogError(e); + return initialized; } - try + private void initLibrary() { - if (useHimaxD2XXDevice) + int result = LibInterfaceCpp.initLibrary(); + if (logToConsole) { - useHimaxD2XXDevices(); + Debug.Log("G3D library initialization successfull."); } - } - catch (Exception e) - { - Debug.LogError(e); - } - try - { - if (useHimaxRP2040Device) + if (result == -100) { - useHimaxRP2040Devices(); + throw new G3D_AlreadyInitializedException("G3D library already initialized."); } - } - catch (Exception e) - { - Debug.LogError(e); - } - try - { - if (usePmdFlexxDevice) + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) { - usePmdFlexxDevices(); + throw new Exception( + "G3D library: an unknown error occurred when initializing the library." + ); } } - catch (Exception e) + + private void deinitLibrary() { - Debug.LogError(e); + int result = LibInterfaceCpp.deinitLibrary(); + if (logToConsole) + { + Debug.Log("G3D library deinitialization successfull."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when initializing the library." + ); + } } - try + public void setCalibrationPath(string calibrationPath) { - initHeadTracking(); + byte[] calibPathBytes = System.Text.Encoding.UTF8.GetBytes(calibrationPath + "\0"); + int result = LibInterfaceCpp.setCalibrationPath(calibPathBytes); + if (logToConsole) + { + Debug.Log("G3D library: setCalibrationPath success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: calibration path already initialized." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when setting the calibration path." + ); + } } - catch (G3D_AlreadyInitializedException) + + public void setConfigPath(string configPath) { - Debug.Log("G3D head tracking has already been initialized."); + byte[] configPathBytes = System.Text.Encoding.UTF8.GetBytes(configPath + "\0"); + int result = LibInterfaceCpp.setConfigPath(configPathBytes); + if (logToConsole) + { + Debug.Log("G3D library: setConfigPath success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: config path already initialized." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when setting the config path." + ); + } } - catch (Exception e) + + public void setConfigFileName(string configFileName) { - throw e; + byte[] configFileNameBytes = System.Text.Encoding.UTF8.GetBytes(configFileName + "\0"); + int result = LibInterfaceCpp.setConfigFileName(configFileNameBytes); + if (logToConsole) + { + Debug.Log("G3D library: setConfigFileName success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: config file name already initialized." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when setting the config file name." + ); + } } - Debug.Log("tracking device count " + getHeadTrackingDeviceCount()); - - initialized = true; - } - public void deinit() - { - // this is in its own try catch to continue deinit if this fails - try + /// + /// + /// + /// (has device, is tracking) + /// + /// + /// + /// + public bool currentHeadTrackingDeviceHasValidCalibration() { - stopHeadTracking(); - } - catch (Exception e) + bool isValid; + int result = LibInterfaceCpp.currentHeadTrackingDeviceHasValidCalibration(out isValid); + if (logToConsole) + { + Debug.Log("G3D library: currentHeadTrackingDeviceHasValidCalibration success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when trying toi check if the current devide has a valid calibration." + ); + } + + return isValid; + } + + // ------------------------------------------------ + + /// + /// headDetected: 0 = no head detected, 1 >= head detected + /// imagePosIsValid: 0 = invalid, 1 >= valid + /// + /// + /// + /// + /// + /// + /// + /// + /// + private void TranslateNewHeadPositionCallback( + int headDetected, + int imagePosIsValid, + int imagePosX, + int imagePosY, + double worldPosX, + double worldPosY, + double worldPosZ, + IntPtr listener + ) { - // only log this if actually initialized. - // otherwise this was probably called from an uninitialized state and the exception reflects that. - if (initialized) + try + { + // translate intptr to interface instance + // call interface instance callback + GCHandle gch = GCHandle.FromIntPtr(listener); + ITNewHeadPositionCallback interfaceInstance = (ITNewHeadPositionCallback)gch.Target; + + interfaceInstance.NewHeadPositionCallback( + headDetected != 0, + imagePosIsValid != 0, + imagePosX, + imagePosY, + worldPosX, + worldPosY, + worldPosZ + ); + } + catch (Exception e) { Debug.LogError(e.ToString()); } } - // this is in its own try catch to continue deinit if this fails - try - { - deinitHeadTracking(); - } - catch (Exception e) + public void registerHeadPositionChangedCallback( + in ITNewHeadPositionCallback inferfaceInstance + ) { - if (initialized) + GCHandle gch = GCHandle.Alloc(inferfaceInstance); + + int result = LibInterfaceCpp.registerHeadPositionChangedCallback( + GCHandle.ToIntPtr(gch), + newHeadPosCppCallback + ); + // TODO figure out if this results in memory leaks + // gch.Free(); + if (logToConsole) { - Debug.LogError(e.ToString()); + Debug.Log("G3D library: registerHeadPositionChangedCallback success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: this callback has already been registered." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when registering the head position changed callback." + ); } } - // this is in its own try catch to continue deinit if this fails - try + public void unregisterHeadPositionChangedCallback( + in ITNewHeadPositionCallback inferfaceInstance + ) { - deinitLibrary(); + GCHandle gch = GCHandle.Alloc(inferfaceInstance); + int result = LibInterfaceCpp.unregisterHeadPositionChangedCallback( + GCHandle.ToIntPtr(gch) + ); + gch.Free(); + + if (logToConsole) + { + Debug.Log("G3D library: unregisterHeadPositionChangedCallback success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: this callback has already been unregistered." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library: callback not registered."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when unregistering the head position changed callback." + ); + } } - catch (Exception e) + + private void TranslateShaderParametersCallback( + in G3DShaderParameters shaderParameters, + IntPtr listener + ) { - if (initialized) + try + { + // translate intptr to interface instance + // call interface instance callback + GCHandle gch = GCHandle.FromIntPtr(listener); + ITNewShaderParametersCallback interfaceInstance = (ITNewShaderParametersCallback) + gch.Target; + + interfaceInstance.NewShaderParametersCallback(shaderParameters); + } + catch (Exception e) { Debug.LogError(e.ToString()); } } - Debug.Log("destroy library"); + public void registerShaderParametersChangedCallback( + in ITNewShaderParametersCallback interfaceInstance + ) + { + GCHandle gch = GCHandle.Alloc(interfaceInstance); - initialized = false; - } + int result = LibInterfaceCpp.registerShaderParametersChangedCallback( + GCHandle.ToIntPtr(gch), + newShaderparamsCppCallback + ); + // TODO figure out if this results in memory leaks + // gch.Free(); + if (logToConsole) + { + Debug.Log("G3D library: registerShaderParametersChangedCallback success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: this callback has already been registered." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when registering the shader parameters changed callback." + ); + } + } - ~LibInterface() - { - deinit(); - } - - public bool isInitialized() - { - return initialized; - } - - private void initLibrary() - { - int result = LibInterfaceCpp.initLibrary(); - if (logToConsole) - { - Debug.Log("G3D library initialization successfull."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when initializing the library." - ); - } - } - - private void deinitLibrary() - { - int result = LibInterfaceCpp.deinitLibrary(); - if (logToConsole) - { - Debug.Log("G3D library deinitialization successfull."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when initializing the library." - ); - } - } - - public void setCalibrationPath(string calibrationPath) - { - byte[] calibPathBytes = System.Text.Encoding.UTF8.GetBytes(calibrationPath + "\0"); - int result = LibInterfaceCpp.setCalibrationPath(calibPathBytes); - if (logToConsole) - { - Debug.Log("G3D library: setCalibrationPath success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: calibration path already initialized." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when setting the calibration path." - ); - } - } - - public void setConfigPath(string configPath) - { - byte[] configPathBytes = System.Text.Encoding.UTF8.GetBytes(configPath + "\0"); - int result = LibInterfaceCpp.setConfigPath(configPathBytes); - if (logToConsole) - { - Debug.Log("G3D library: setConfigPath success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: config path already initialized." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when setting the config path." - ); - } - } - - public void setConfigFileName(string configFileName) - { - byte[] configFileNameBytes = System.Text.Encoding.UTF8.GetBytes(configFileName + "\0"); - int result = LibInterfaceCpp.setConfigFileName(configFileNameBytes); - if (logToConsole) - { - Debug.Log("G3D library: setConfigFileName success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: config file name already initialized." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when setting the config file name." - ); - } - } - - /// - /// - /// - /// (has device, is tracking) - /// - /// - /// - /// - public bool currentHeadTrackingDeviceHasValidCalibration() - { - bool isValid; - int result = LibInterfaceCpp.currentHeadTrackingDeviceHasValidCalibration(out isValid); - if (logToConsole) - { - Debug.Log("G3D library: currentHeadTrackingDeviceHasValidCalibration success"); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when trying toi check if the current devide has a valid calibration." - ); - } - - return isValid; - } - - // ------------------------------------------------ - - /// - /// headDetected: 0 = no head detected, 1 >= head detected - /// imagePosIsValid: 0 = invalid, 1 >= valid - /// - /// - /// - /// - /// - /// - /// - /// - /// - private void TranslateNewHeadPositionCallback( - int headDetected, - int imagePosIsValid, - int imagePosX, - int imagePosY, - double worldPosX, - double worldPosY, - double worldPosZ, - IntPtr listener - ) - { - try - { - // translate intptr to interface instance - // call interface instance callback - GCHandle gch = GCHandle.FromIntPtr(listener); - ITNewHeadPositionCallback interfaceInstance = (ITNewHeadPositionCallback)gch.Target; - - interfaceInstance.NewHeadPositionCallback( - headDetected != 0, - imagePosIsValid != 0, - imagePosX, - imagePosY, - worldPosX, - worldPosY, - worldPosZ - ); - } - catch (Exception e) - { - Debug.LogError(e.ToString()); - } - } - - public void registerHeadPositionChangedCallback(in ITNewHeadPositionCallback inferfaceInstance) - { - GCHandle gch = GCHandle.Alloc(inferfaceInstance); - - int result = LibInterfaceCpp.registerHeadPositionChangedCallback( - GCHandle.ToIntPtr(gch), - newHeadPosCppCallback - ); - // TODO figure out if this results in memory leaks - // gch.Free(); - if (logToConsole) - { - Debug.Log("G3D library: registerHeadPositionChangedCallback success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: this callback has already been registered." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when registering the head position changed callback." - ); - } - } - - public void unregisterHeadPositionChangedCallback( - in ITNewHeadPositionCallback inferfaceInstance - ) - { - GCHandle gch = GCHandle.Alloc(inferfaceInstance); - int result = LibInterfaceCpp.unregisterHeadPositionChangedCallback(GCHandle.ToIntPtr(gch)); - gch.Free(); - - if (logToConsole) - { - Debug.Log("G3D library: unregisterHeadPositionChangedCallback success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: this callback has already been unregistered." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library: callback not registered."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when unregistering the head position changed callback." - ); - } - } - - private void TranslateShaderParametersCallback( - in G3DShaderParameters shaderParameters, - IntPtr listener - ) - { - try - { - // translate intptr to interface instance - // call interface instance callback - GCHandle gch = GCHandle.FromIntPtr(listener); - ITNewShaderParametersCallback interfaceInstance = (ITNewShaderParametersCallback) - gch.Target; - - interfaceInstance.NewShaderParametersCallback(shaderParameters); - } - catch (Exception e) - { - Debug.LogError(e.ToString()); - } - } - - public void registerShaderParametersChangedCallback( - in ITNewShaderParametersCallback interfaceInstance - ) - { - GCHandle gch = GCHandle.Alloc(interfaceInstance); - - int result = LibInterfaceCpp.registerShaderParametersChangedCallback( - GCHandle.ToIntPtr(gch), - newShaderparamsCppCallback - ); - // TODO figure out if this results in memory leaks - // gch.Free(); - if (logToConsole) - { - Debug.Log("G3D library: registerShaderParametersChangedCallback success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: this callback has already been registered." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when registering the shader parameters changed callback." - ); - } - } - - public void unregisterShaderParametersChangedCallback( - in ITNewShaderParametersCallback interfaceInstance - ) - { - GCHandle gch = GCHandle.Alloc(interfaceInstance); - int result = LibInterfaceCpp.unregisterShaderParametersChangedCallback( - GCHandle.ToIntPtr(gch) - ); - gch.Free(); - - if (logToConsole) - { - Debug.Log("G3D library: unregisterShaderParametersChangedCallback success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: this callback has already been unregistered." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library: callback not registered."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when unregistering the shader parameters changed callback." - ); - } - } - - private void TranslateNewErrorMessageCallback( - EMessageSeverity severity, - IntPtr sender, - IntPtr caption, - IntPtr cause, - IntPtr remedy, - IntPtr listener - ) - { - try - { - // translate intptr to interface instance - // call interface instance callback - GCHandle gch = GCHandle.FromIntPtr(listener); - ITNewErrorMessageCallback interfaceInstance = (ITNewErrorMessageCallback)gch.Target; - - string senderString = Marshal.PtrToStringUTF8(sender); - string captionString = Marshal.PtrToStringUTF8(caption); - string causeString = Marshal.PtrToStringUTF8(cause); - string remedyString = Marshal.PtrToStringUTF8(remedy); - - interfaceInstance.NewErrorMessageCallback( - severity, - senderString, - captionString, - causeString, - remedyString - ); - } - catch (Exception e) - { - Debug.LogError(e.ToString()); - } - } - - public void registerMessageCallback(in ITNewErrorMessageCallback inferfaceInstance) - { - GCHandle gch = GCHandle.Alloc(inferfaceInstance); - - int result = LibInterfaceCpp.registerMessageCallback( - GCHandle.ToIntPtr(gch), - newErrorMessagesCppCallback - ); - // TODO figure out if this results in memory leaks - // gch.Free(); - if (logToConsole) - { - Debug.Log("G3D library: registerMessageCallback success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: this callback has already been registered." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when registering the message callback." - ); - } - } - - public void unregisterMessageCallback(in ITNewErrorMessageCallback inferfaceInstance) - { - GCHandle gch = GCHandle.Alloc(inferfaceInstance); - int result = LibInterfaceCpp.unregisterMessageCallback(GCHandle.ToIntPtr(gch)); - gch.Free(); - - if (logToConsole) - { - Debug.Log("G3D library: unregisterMessageCallback success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: this callback has already been unregistered." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library: callback not registered."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when unregistering the message callback." - ); - } - } - - // ------------------------------------------------ - - public void useHimaxD2XXDevices() - { - int result = LibInterfaceCpp.useHimaxD2XXDevices(); - if (logToConsole) - { - Debug.Log("G3D library: useHimaxD2XXDevices success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when using Himax D2XX devices." - ); - } - } - - public void useHimaxRP2040Devices() - { - int result = LibInterfaceCpp.useHimaxRP2040Devices(); - if (logToConsole) - { - Debug.Log("G3D library: useHimaxRP2040Devices success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when using Himax RP2040 devices." - ); - } - } - - public void usePmdFlexxDevices() - { - int result = LibInterfaceCpp.usePmdFlexxDevices(); - if (logToConsole) - { - Debug.Log("G3D library: usePmdFlexxDevices success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when using PMD Flexx devices." - ); - } - } - - public void initHeadTracking() - { - int result = LibInterfaceCpp.initHeadTracking(); - if (logToConsole) - { - Debug.Log("G3D library: initHeadTracking success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: head tracking already initialized." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library: head tracking not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when initializing head tracking." - ); - } - } - - public void deinitHeadTracking() - { - int result = LibInterfaceCpp.deinitHeadTracking(); - if (logToConsole) - { - Debug.Log("G3D library: deinitHeadTracking success."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: head tracking already initialized." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library: head tracking not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when deinitializing head tracking." - ); - } - } - - // ------------------------------------------------ - - public int getHeadTrackingDeviceCount() - { - int deviceCount; - int result = LibInterfaceCpp.getHeadTrackingDeviceCount(out deviceCount); - if (logToConsole) - { - Debug.Log( - "G3D library: getHeadTrackingDeviceCount success. Device count: " + deviceCount - ); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: head tracking device count already initialized." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when getting the head tracking device count." - ); - } - - return deviceCount; - } - - public string getHeadTrackingDeviceName(int deviceNumber) - { - IntPtr deviceNamePtr; - int result = LibInterfaceCpp.getHeadTrackingDeviceName(deviceNumber, out deviceNamePtr); - string deviceName = Marshal.PtrToStringUTF8(deviceNamePtr); - if (logToConsole) - { - Debug.Log( - "G3D library: getHeadTrackingDeviceName success. Device with index " - + deviceNumber - + "has name: " - + deviceName - ); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: head tracking device name already initialized." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: the provided deviceNumber is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when getting the head tracking device name." - ); - } - - return deviceName; - } - - public string getCurrentHeadTrackingDevice() - { - IntPtr device; - int result = LibInterfaceCpp.getCurrentHeadTrackingDevice(out device); - string deviceName = Marshal.PtrToStringUTF8(device); - if (logToConsole) - { - Debug.Log( - "G3D library: getCurrentHeadTrackingDevice success. Current device name: " - + deviceName - ); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: current head tracking device already initialized." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when getting the current head tracking device." - ); - } - - return deviceName; - } - - public void setCurrentHeadTrackingDevice(string device) - { - byte[] deviceNameBytes = System.Text.Encoding.UTF8.GetBytes(device); - int result = LibInterfaceCpp.setCurrentHeadTrackingDevice(deviceNameBytes); - if (logToConsole) - { - Debug.Log("G3D library: setCurrentHeadTrackingDevice success"); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library: current head tracking device already initialized." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when setting the current head tracking device." - ); - } - } - - /// - /// - /// - /// (horizontal resolution, vertical resolution) - /// - /// - /// - /// - public Tuple getHeadTrackingDeviceResolution() - { - int horizontalResolution; - int verticalResolution; - int result = LibInterfaceCpp.getHeadTrackingDeviceResolution( - out horizontalResolution, - out verticalResolution - ); - if (logToConsole) - { - Debug.Log( - "G3D library: getHeadTrackingDeviceResolution success. Horizontal resolution: " - + horizontalResolution - + ", vertical resolution: " - + verticalResolution - ); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when getting the head tracking device resolution." - ); - } - - return new Tuple(horizontalResolution, verticalResolution); - } - - // ------------------------------------------------ - - public int getFirstValidCalibrationMatrixCol() - { - int value; - int result = LibInterfaceCpp.getFirstValidCalibrationMatrixCol(out value); - if (logToConsole) - { - Debug.Log("G3D library: getFirstValidCalibrationMatrixCol success. Value: " + value); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when getting the first valid calibration matrix column." - ); - } - - return value; - } - - public int getLastValidCalibrationMatrixCol() - { - int value; - int result = LibInterfaceCpp.getLastValidCalibrationMatrixCol(out value); - if (logToConsole) - { - Debug.Log("G3D library: getLastValidCalibrationMatrixCol success. Value: " + value); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when getting the last valid calibration matrix column." - ); - } - - return value; - } - - public int getFirstValidCalibrationMatrixRow() - { - int value; - int result = LibInterfaceCpp.getFirstValidCalibrationMatrixRow(out value); - if (logToConsole) - { - Debug.Log("G3D library: getFirstValidCalibrationMatrixRow success. Value: " + value); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when getting the first valid calibration matrix row." - ); - } - - return value; - } - - public int getLastValidCalibrationMatrixRow() - { - int value; - int result = LibInterfaceCpp.getLastValidCalibrationMatrixRow(out value); - if (logToConsole) - { - Debug.Log("G3D library: getLastValidCalibrationMatrixRow success. Value: " + value); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when getting the last valid calibration matrix row." - ); - } - - return value; - } - - // ------------------------------------------------ - - public void startHeadTracking() - { - int result = LibInterfaceCpp.startHeadTracking(); - if (logToConsole) - { - Debug.Log("G3D library: head tracking started successfully."); - } - if (result == -100) + public void unregisterShaderParametersChangedCallback( + in ITNewShaderParametersCallback interfaceInstance + ) { - throw new G3D_AlreadyInitializedException("G3D library already started."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library: head tracking not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." + GCHandle gch = GCHandle.Alloc(interfaceInstance); + int result = LibInterfaceCpp.unregisterShaderParametersChangedCallback( + GCHandle.ToIntPtr(gch) ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when starting head tracking." - ); - } - } + gch.Free(); - public void stopHeadTracking() - { - int result = LibInterfaceCpp.stopHeadTracking(); - if (logToConsole) - { - Debug.Log("G3D library: head tracking stopped successfully."); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library: head tracking already stopped."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when stopping head tracking." - ); + if (logToConsole) + { + Debug.Log("G3D library: unregisterShaderParametersChangedCallback success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: this callback has already been unregistered." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library: callback not registered."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when unregistering the shader parameters changed callback." + ); + } } - } - /// - /// - /// - /// (has device, is tracking) - /// - /// - /// - /// - public HeadTrackingStatus getHeadTrackingStatus() - { - bool hasDevice; - bool isTracking; - int result = LibInterfaceCpp.getHeadTrackingStatus(out hasDevice, out isTracking); - if (logToConsole) - { - Debug.Log("G3D library: getHeadTrackingStatus success"); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) + private void TranslateNewErrorMessageCallback( + EMessageSeverity severity, + IntPtr sender, + IntPtr caption, + IntPtr cause, + IntPtr remedy, + IntPtr listener + ) { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); + try + { + // translate intptr to interface instance + // call interface instance callback + GCHandle gch = GCHandle.FromIntPtr(listener); + ITNewErrorMessageCallback interfaceInstance = (ITNewErrorMessageCallback)gch.Target; + + string senderString = Marshal.PtrToStringUTF8(sender); + string captionString = Marshal.PtrToStringUTF8(caption); + string causeString = Marshal.PtrToStringUTF8(cause); + string remedyString = Marshal.PtrToStringUTF8(remedy); + + interfaceInstance.NewErrorMessageCallback( + severity, + senderString, + captionString, + causeString, + remedyString + ); + } + catch (Exception e) + { + Debug.LogError(e.ToString()); + } } - if (result == -200) + + public void registerMessageCallback(in ITNewErrorMessageCallback inferfaceInstance) { - throw new Exception( - "G3D library: an unknown error occurred when getting the head tracking status." + GCHandle gch = GCHandle.Alloc(inferfaceInstance); + + int result = LibInterfaceCpp.registerMessageCallback( + GCHandle.ToIntPtr(gch), + newErrorMessagesCppCallback ); + // TODO figure out if this results in memory leaks + // gch.Free(); + if (logToConsole) + { + Debug.Log("G3D library: registerMessageCallback success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: this callback has already been registered." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when registering the message callback." + ); + } } - HeadTrackingStatus headTrackingStatus = new HeadTrackingStatus + public void unregisterMessageCallback(in ITNewErrorMessageCallback inferfaceInstance) { - hasTrackingDevice = hasDevice, - isTrackingActive = isTracking - }; + GCHandle gch = GCHandle.Alloc(inferfaceInstance); + int result = LibInterfaceCpp.unregisterMessageCallback(GCHandle.ToIntPtr(gch)); + gch.Free(); - return headTrackingStatus; - } + if (logToConsole) + { + Debug.Log("G3D library: unregisterMessageCallback success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: this callback has already been unregistered." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library: callback not registered."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when unregistering the message callback." + ); + } + } - // ------------------------------------------------ + // ------------------------------------------------ - public G3DShaderParameters getCurrentShaderParameters() - { - G3DShaderParameters parameters; - int result = LibInterfaceCpp.getCurrentShaderParameters(out parameters); - if (logToConsole) + public void useHimaxD2XXDevices() { - Debug.Log("G3D library: getCurrentShaderParameters success"); + int result = LibInterfaceCpp.useHimaxD2XXDevices(); + if (logToConsole) + { + Debug.Log("G3D library: useHimaxD2XXDevices success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when using Himax D2XX devices." + ); + } } - if (result == -100) + + public void useHimaxRP2040Devices() { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); + int result = LibInterfaceCpp.useHimaxRP2040Devices(); + if (logToConsole) + { + Debug.Log("G3D library: useHimaxRP2040Devices success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when using Himax RP2040 devices." + ); + } } - if (result == -101) + + public void usePmdFlexxDevices() { - throw new G3D_NotInitializedException("G3D library not initialized."); + int result = LibInterfaceCpp.usePmdFlexxDevices(); + if (logToConsole) + { + Debug.Log("G3D library: usePmdFlexxDevices success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when using PMD Flexx devices." + ); + } } - if (result == -102) + + public void initHeadTracking() { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); + int result = LibInterfaceCpp.initHeadTracking(); + if (logToConsole) + { + Debug.Log("G3D library: initHeadTracking success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: head tracking already initialized." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException( + "G3D library: head tracking not initialized." + ); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when initializing head tracking." + ); + } } - if (result == -200) + + public void deinitHeadTracking() { - throw new Exception( - "G3D library: an unknown error occurred when getting the current shader parameters." - ); + int result = LibInterfaceCpp.deinitHeadTracking(); + if (logToConsole) + { + Debug.Log("G3D library: deinitHeadTracking success."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: head tracking already initialized." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException( + "G3D library: head tracking not initialized." + ); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when deinitializing head tracking." + ); + } } - return parameters; - } + // ------------------------------------------------ - public void setWindowPosition(int left, int top) - { - int result = LibInterfaceCpp.setWindowPosition(left, top); - if (logToConsole) - { - Debug.Log("G3D library: setWindowPosition success"); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) + public int getHeadTrackingDeviceCount() { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when setting the window position." - ); - } - } + int deviceCount; + int result = LibInterfaceCpp.getHeadTrackingDeviceCount(out deviceCount); + if (logToConsole) + { + Debug.Log( + "G3D library: getHeadTrackingDeviceCount success. Device count: " + deviceCount + ); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: head tracking device count already initialized." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when getting the head tracking device count." + ); + } - public void setWindowSize(int width, int height) - { - int result = LibInterfaceCpp.setWindowSize(width, height); - if (logToConsole) - { - Debug.Log("G3D library: setWindowSize success"); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); + return deviceCount; } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library: an unknown error occurred when setting the window size." - ); - } - } - public void setViewportOffset(int left, int top) - { - int result = LibInterfaceCpp.setViewportOffset(left, top); - if (logToConsole) - { - Debug.Log("G3D library: setViewportOffset success"); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) - { - throw new G3D_NotInitializedException("G3D library not initialized."); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); - } - if (result == -200) + public string getHeadTrackingDeviceName(int deviceNumber) { - throw new Exception( - "G3D library: an unknown error occurred when setting the viewport offset." - ); - } - } + IntPtr deviceNamePtr; + int result = LibInterfaceCpp.getHeadTrackingDeviceName(deviceNumber, out deviceNamePtr); + string deviceName = Marshal.PtrToStringUTF8(deviceNamePtr); + if (logToConsole) + { + Debug.Log( + "G3D library: getHeadTrackingDeviceName success. Device with index " + + deviceNumber + + "has name: " + + deviceName + ); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: head tracking device name already initialized." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: the provided deviceNumber is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when getting the head tracking device name." + ); + } - public void setViewportSize(int width, int height) - { - int result = LibInterfaceCpp.setViewportSize(width, height); - if (logToConsole) - { - Debug.Log("G3D library: setViewportSize success"); + return deviceName; } - if (result == -100) - { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); - } - if (result == -101) + + public string getCurrentHeadTrackingDevice() { - throw new G3D_NotInitializedException("G3D library not initialized."); + IntPtr device; + int result = LibInterfaceCpp.getCurrentHeadTrackingDevice(out device); + string deviceName = Marshal.PtrToStringUTF8(device); + if (logToConsole) + { + Debug.Log( + "G3D library: getCurrentHeadTrackingDevice success. Current device name: " + + deviceName + ); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: current head tracking device already initialized." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when getting the current head tracking device." + ); + } + + return deviceName; } - if (result == -102) + + public void setCurrentHeadTrackingDevice(string device) { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); + byte[] deviceNameBytes = System.Text.Encoding.UTF8.GetBytes(device); + int result = LibInterfaceCpp.setCurrentHeadTrackingDevice(deviceNameBytes); + if (logToConsole) + { + Debug.Log("G3D library: setCurrentHeadTrackingDevice success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library: current head tracking device already initialized." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when setting the current head tracking device." + ); + } } - if (result == -200) + + /// + /// + /// + /// (horizontal resolution, vertical resolution) + /// + /// + /// + /// + public Tuple getHeadTrackingDeviceResolution() { - throw new Exception( - "G3D library: an unknown error occurred when setting the viewport size." + int horizontalResolution; + int verticalResolution; + int result = LibInterfaceCpp.getHeadTrackingDeviceResolution( + out horizontalResolution, + out verticalResolution ); - } - } + if (logToConsole) + { + Debug.Log( + "G3D library: getHeadTrackingDeviceResolution success. Horizontal resolution: " + + horizontalResolution + + ", vertical resolution: " + + verticalResolution + ); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when getting the head tracking device resolution." + ); + } - public void setScreenSize(int width, int height) - { - int result = LibInterfaceCpp.setScreenSize(width, height); - if (logToConsole) - { - Debug.Log("G3D library: setScreenSize success"); + return new Tuple(horizontalResolution, verticalResolution); } - if (result == -100) + + // ------------------------------------------------ + + public int getFirstValidCalibrationMatrixCol() { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); + int value; + int result = LibInterfaceCpp.getFirstValidCalibrationMatrixCol(out value); + if (logToConsole) + { + Debug.Log( + "G3D library: getFirstValidCalibrationMatrixCol success. Value: " + value + ); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when getting the first valid calibration matrix column." + ); + } + + return value; } - if (result == -101) + + public int getLastValidCalibrationMatrixCol() { - throw new G3D_NotInitializedException("G3D library not initialized."); + int value; + int result = LibInterfaceCpp.getLastValidCalibrationMatrixCol(out value); + if (logToConsole) + { + Debug.Log("G3D library: getLastValidCalibrationMatrixCol success. Value: " + value); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when getting the last valid calibration matrix column." + ); + } + + return value; } - if (result == -102) + + public int getFirstValidCalibrationMatrixRow() { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); + int value; + int result = LibInterfaceCpp.getFirstValidCalibrationMatrixRow(out value); + if (logToConsole) + { + Debug.Log( + "G3D library: getFirstValidCalibrationMatrixRow success. Value: " + value + ); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when getting the first valid calibration matrix row." + ); + } + + return value; } - if (result == -200) + + public int getLastValidCalibrationMatrixRow() { - throw new Exception( - "G3D library: an unknown error occurred when setting the screen dimensions." - ); + int value; + int result = LibInterfaceCpp.getLastValidCalibrationMatrixRow(out value); + if (logToConsole) + { + Debug.Log("G3D library: getLastValidCalibrationMatrixRow success. Value: " + value); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when getting the last valid calibration matrix row." + ); + } + + return value; } - } - // ------------------------------------------------ + // ------------------------------------------------ - public void shiftViewToLeft() - { - int result = LibInterfaceCpp.shiftViewToLeft(); - if (logToConsole) - { - Debug.Log("G3D library: shiftViewToLeft success"); - } - if (result == -100) + public void startHeadTracking() { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); + int result = LibInterfaceCpp.startHeadTracking(); + if (logToConsole) + { + Debug.Log("G3D library: head tracking started successfully."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already started."); + } + if (result == -101) + { + throw new G3D_NotInitializedException( + "G3D library: head tracking not initialized." + ); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when starting head tracking." + ); + } } - if (result == -101) + + public void stopHeadTracking() { - throw new G3D_NotInitializedException("G3D library not initialized."); + int result = LibInterfaceCpp.stopHeadTracking(); + if (logToConsole) + { + Debug.Log("G3D library: head tracking stopped successfully."); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException( + "G3D library: head tracking already stopped." + ); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when stopping head tracking." + ); + } } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); + + /// + /// + /// + /// (has device, is tracking) + /// + /// + /// + /// + public HeadTrackingStatus getHeadTrackingStatus() + { + bool hasDevice; + bool isTracking; + int result = LibInterfaceCpp.getHeadTrackingStatus(out hasDevice, out isTracking); + if (logToConsole) + { + Debug.Log("G3D library: getHeadTrackingStatus success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when getting the head tracking status." + ); + } + + HeadTrackingStatus headTrackingStatus = new HeadTrackingStatus + { + hasTrackingDevice = hasDevice, + isTrackingActive = isTracking + }; + + return headTrackingStatus; } - if (result == -200) + + // ------------------------------------------------ + + public G3DShaderParameters getCurrentShaderParameters() { - throw new Exception( - "G3D library: an unknown error occurred when shifting the view to the left." - ); + G3DShaderParameters parameters; + int result = LibInterfaceCpp.getCurrentShaderParameters(out parameters); + if (logToConsole) + { + Debug.Log("G3D library: getCurrentShaderParameters success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when getting the current shader parameters." + ); + } + + return parameters; } - } - public void shiftViewToRight() - { - int result = LibInterfaceCpp.shiftViewToRight(); - if (logToConsole) + public void setWindowPosition(int left, int top) { - Debug.Log("G3D library: shiftViewToRight success"); + int result = LibInterfaceCpp.setWindowPosition(left, top); + if (logToConsole) + { + Debug.Log("G3D library: setWindowPosition success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when setting the window position." + ); + } } - if (result == -100) + + public void setWindowSize(int width, int height) { - throw new G3D_AlreadyInitializedException("G3D library already initialized."); + int result = LibInterfaceCpp.setWindowSize(width, height); + if (logToConsole) + { + Debug.Log("G3D library: setWindowSize success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when setting the window size." + ); + } } - if (result == -101) + + public void setViewportOffset(int left, int top) { - throw new G3D_NotInitializedException("G3D library not initialized."); + int result = LibInterfaceCpp.setViewportOffset(left, top); + if (logToConsole) + { + Debug.Log("G3D library: setViewportOffset success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when setting the viewport offset." + ); + } } - if (result == -102) + + public void setViewportSize(int width, int height) { - throw new G3D_IndexOutOfRangeException( - "G3D library: a provided index is out of range." - ); + int result = LibInterfaceCpp.setViewportSize(width, height); + if (logToConsole) + { + Debug.Log("G3D library: setViewportSize success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when setting the viewport size." + ); + } } - if (result == -200) + + public void setScreenSize(int width, int height) { - throw new Exception( - "G3D library: an unknown error occurred when shifting the view to the right." - ); + int result = LibInterfaceCpp.setScreenSize(width, height); + if (logToConsole) + { + Debug.Log("G3D library: setScreenSize success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when setting the screen dimensions." + ); + } } - } - // ------------------------------------------------ + // ------------------------------------------------ - // extern G3D_UNIVERSAL_HEADTRACKING_LIBRARY_EXPORT int initializePositionFilter(const int lenX, const int lenY, const int lenZ); - public void initializePositionFilter(int lenX, int lenY, int lenZ) - { - int result = LibInterfaceCpp.initializePositionFilter(lenX, lenY, lenZ); - if (logToConsole) + public void shiftViewToLeft() { - Debug.Log( - "G3D library: initializePositionFilter success. Filter set to (" - + lenX - + "," - + lenY - + "," - + lenZ - + "," - + ")" - ); + int result = LibInterfaceCpp.shiftViewToLeft(); + if (logToConsole) + { + Debug.Log("G3D library: shiftViewToLeft success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when shifting the view to the left." + ); + } } - if (result == -100) + + public void shiftViewToRight() { - throw new G3D_AlreadyInitializedException( - "G3D library initializePositionFilter: position filter already initialized." - ); + int result = LibInterfaceCpp.shiftViewToRight(); + if (logToConsole) + { + Debug.Log("G3D library: shiftViewToRight success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException("G3D library already initialized."); + } + if (result == -101) + { + throw new G3D_NotInitializedException("G3D library not initialized."); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library: an unknown error occurred when shifting the view to the right." + ); + } } - if (result == -101) + + // ------------------------------------------------ + + // extern G3D_UNIVERSAL_HEADTRACKING_LIBRARY_EXPORT int initializePositionFilter(const int lenX, const int lenY, const int lenZ); + public void initializePositionFilter(int lenX, int lenY, int lenZ) { - throw new G3D_NotInitializedException( - "G3D library initializePositionFilter: not initialized." - ); + int result = LibInterfaceCpp.initializePositionFilter(lenX, lenY, lenZ); + if (logToConsole) + { + Debug.Log( + "G3D library: initializePositionFilter success. Filter set to (" + + lenX + + "," + + lenY + + "," + + lenZ + + "," + + ")" + ); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library initializePositionFilter: position filter already initialized." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException( + "G3D library initializePositionFilter: not initialized." + ); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library initializePositionFilter: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library initializePositionFilter: an unknown error occurred when shifting the view to the right." + ); + } } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library initializePositionFilter: a provided index is out of range." - ); + + // extern G3D_UNIVERSAL_HEADTRACKING_LIBRARY_EXPORT int applyPositionFilter(const double posX, const double posY, const double posZ, + // double* filteredX, double* filteredY, double* filteredZ); + public void applyPositionFilter( + double posX, + double posY, + double posZ, + out double filteredX, + out double filteredY, + out double filteredZ + ) + { + int result = LibInterfaceCpp.applyPositionFilter( + posX, + posY, + posZ, + out filteredX, + out filteredY, + out filteredZ + ); + if (logToConsole) + { + Debug.Log( + "G3D library: applyPositionFilter success. Filtered position: ()" + + filteredX + + ", " + + filteredY + + ", " + + filteredZ + + "); Unfiltered Position: (" + + posX + + ", " + + posY + + ", " + + posZ + + ")" + ); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library applyPositionFilter: library already initialized error." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException( + "G3D library applyPositionFilter: position filter not initialized." + ); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library applyPositionFilter: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library applyPositionFilter: an unknown error occurred when getting the head tracking device resolution." + ); + } } - if (result == -200) + + public void calculateShaderParameters(LatencyCorrectionMode latencyCorrectionMode) { - throw new Exception( - "G3D library initializePositionFilter: an unknown error occurred when shifting the view to the right." - ); + int result = LibInterfaceCpp.calculateShaderParameters(latencyCorrectionMode); + if (logToConsole) + { + Debug.Log("G3D library: calculateShaderParameters success"); + } + if (result == -100) + { + throw new G3D_AlreadyInitializedException( + "G3D library calculateShaderParameters: library already initialized error." + ); + } + if (result == -101) + { + throw new G3D_NotInitializedException( + "G3D library calculateShaderParameters: library not initialized." + ); + } + if (result == -102) + { + throw new G3D_IndexOutOfRangeException( + "G3D library calculateShaderParameters: a provided index is out of range." + ); + } + if (result == -200) + { + throw new Exception( + "G3D library calculateShaderParameters: an unknown error occurred when calculating the shader parameters." + ); + } } } - // extern G3D_UNIVERSAL_HEADTRACKING_LIBRARY_EXPORT int applyPositionFilter(const double posX, const double posY, const double posZ, - // double* filteredX, double* filteredY, double* filteredZ); - public void applyPositionFilter( - double posX, - double posY, - double posZ, - out double filteredX, - out double filteredY, - out double filteredZ - ) + /// + /// This class provides the raw C interface to the G3D Universal Head Tracking Library. + /// + internal static class LibInterfaceCpp { - int result = LibInterfaceCpp.applyPositionFilter( - posX, - posY, - posZ, - out filteredX, - out filteredY, - out filteredZ + // Error codes + // const int E_OK = 0; + // const int E_INITIALIZED_ALREADY = -100; + // const int E_NOT_INITIALIZED = -101; + // const int E_INDEX_OUT_OF_RANGE = -102; + // const int E_EXCEPTION_ERROR = -200; + + //function definitions + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?initLibrary@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int initLibrary(); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?deinitLibrary@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int deinitLibrary(); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?setCalibrationPath@G3D_HTL@@YAHPEBD@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int setCalibrationPath(byte[] path); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?setConfigPath@G3D_HTL@@YAHPEBD@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int setConfigPath(byte[] path); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?setConfigFileName@G3D_HTL@@YAHPEBD@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int setConfigFileName(byte[] path); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?currentHeadTrackingDeviceHasValidCalibration@G3D_HTL@@YAHPEA_N@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int currentHeadTrackingDeviceHasValidCalibration( + [MarshalAs(UnmanagedType.U1)] out bool isValid ); - if (logToConsole) - { - Debug.Log( - "G3D library: applyPositionFilter success. Filtered position: ()" - + filteredX - + ", " - + filteredY - + ", " - + filteredZ - + "); Unfiltered Position: (" - + posX - + ", " - + posY - + ", " - + posZ - + ")" - ); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library applyPositionFilter: library already initialized error." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException( - "G3D library applyPositionFilter: position filter not initialized." - ); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library applyPositionFilter: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library applyPositionFilter: an unknown error occurred when getting the head tracking device resolution." - ); - } - } - public void calculateShaderParameters(LatencyCorrectionMode latencyCorrectionMode) - { - int result = LibInterfaceCpp.calculateShaderParameters(latencyCorrectionMode); - if (logToConsole) - { - Debug.Log("G3D library: calculateShaderParameters success"); - } - if (result == -100) - { - throw new G3D_AlreadyInitializedException( - "G3D library calculateShaderParameters: library already initialized error." - ); - } - if (result == -101) - { - throw new G3D_NotInitializedException( - "G3D library calculateShaderParameters: library not initialized." - ); - } - if (result == -102) - { - throw new G3D_IndexOutOfRangeException( - "G3D library calculateShaderParameters: a provided index is out of range." - ); - } - if (result == -200) - { - throw new Exception( - "G3D library calculateShaderParameters: an unknown error occurred when calculating the shader parameters." - ); - } - } -} + // ------------------------------------------------ -/// -/// This class provides the raw C interface to the G3D Universal Head Tracking Library. -/// -internal static class LibInterfaceCpp -{ - // Error codes - // const int E_OK = 0; - // const int E_INITIALIZED_ALREADY = -100; - // const int E_NOT_INITIALIZED = -101; - // const int E_INDEX_OUT_OF_RANGE = -102; - // const int E_EXCEPTION_ERROR = -200; - - //function definitions - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?initLibrary@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int initLibrary(); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?deinitLibrary@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int deinitLibrary(); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?setCalibrationPath@G3D_HTL@@YAHPEBD@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int setCalibrationPath(byte[] path); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?setConfigPath@G3D_HTL@@YAHPEBD@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int setConfigPath(byte[] path); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?setConfigFileName@G3D_HTL@@YAHPEBD@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int setConfigFileName(byte[] path); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?currentHeadTrackingDeviceHasValidCalibration@G3D_HTL@@YAHPEA_N@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int currentHeadTrackingDeviceHasValidCalibration( - [MarshalAs(UnmanagedType.U1)] out bool isValid - ); + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?registerHeadPositionChangedCallback@G3D_HTL@@YAHPEAXP6AXHHHHNNN0@Z@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int registerHeadPositionChangedCallback( + IntPtr listener, + TNewHeadPositionCallbackInternal callback + ); - // ------------------------------------------------ + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?unregisterHeadPositionChangedCallback@G3D_HTL@@YAHPEAX@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int unregisterHeadPositionChangedCallback(IntPtr listener); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?registerShaderParametersChangedCallback@G3D_HTL@@YAHPEAXP6AXPEBUCG3DShaderParameters@@0@Z@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int registerShaderParametersChangedCallback( + IntPtr listener, + TNewShaderParametersCallbackInternal callback + ); - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?registerHeadPositionChangedCallback@G3D_HTL@@YAHPEAXP6AXHHHHNNN0@Z@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int registerHeadPositionChangedCallback( - IntPtr listener, - TNewHeadPositionCallbackInternal callback - ); + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?unregisterShaderParametersChangedCallback@G3D_HTL@@YAHPEAX@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int unregisterShaderParametersChangedCallback(IntPtr listener); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?registerMessageCallback@G3D_HTL@@YAHPEAXP6AXW4EMessageSeverity@@PEBD2220@Z@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int registerMessageCallback( + IntPtr listener, + TNewErrorMessageCallbackInternal callback + ); - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?unregisterHeadPositionChangedCallback@G3D_HTL@@YAHPEAX@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int unregisterHeadPositionChangedCallback(IntPtr listener); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?registerShaderParametersChangedCallback@G3D_HTL@@YAHPEAXP6AXPEBUCG3DShaderParameters@@0@Z@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int registerShaderParametersChangedCallback( - IntPtr listener, - TNewShaderParametersCallbackInternal callback - ); + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?unregisterMessageCallback@G3D_HTL@@YAHPEAX@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int unregisterMessageCallback(IntPtr listener); + + // ------------------------------------------------ + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?useHimaxD2XXDevices@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int useHimaxD2XXDevices(); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?useHimaxRP2040Devices@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int useHimaxRP2040Devices(); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?usePmdFlexxDevices@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int usePmdFlexxDevices(); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?initHeadTracking@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int initHeadTracking(); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?deinitHeadTracking@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int deinitHeadTracking(); + + // ------------------------------------------------ + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?getHeadTrackingDeviceCount@G3D_HTL@@YAHPEAH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int getHeadTrackingDeviceCount(out int deviceCount); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?getHeadTrackingDeviceName@G3D_HTL@@YAHHPEAPEAD@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int getHeadTrackingDeviceName(int deviceNumber, out IntPtr deviceName); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?getCurrentHeadTrackingDevice@G3D_HTL@@YAHPEAPEAD@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int getCurrentHeadTrackingDevice(out IntPtr deviceName); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?setCurrentHeadTrackingDevice@G3D_HTL@@YAHPEBD@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int setCurrentHeadTrackingDevice(in byte[] device); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?getHeadTrackingDeviceResolution@G3D_HTL@@YAHPEAH0@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int getHeadTrackingDeviceResolution( + out int horizontalResolution, + out int verticalResolution + ); - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?unregisterShaderParametersChangedCallback@G3D_HTL@@YAHPEAX@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int unregisterShaderParametersChangedCallback(IntPtr listener); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?registerMessageCallback@G3D_HTL@@YAHPEAXP6AXW4EMessageSeverity@@PEBD2220@Z@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int registerMessageCallback( - IntPtr listener, - TNewErrorMessageCallbackInternal callback - ); + // ------------------------------------------------ + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?getFirstValidCalibrationMatrixCol@G3D_HTL@@YAHPEAH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int getFirstValidCalibrationMatrixCol(out int value); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?getLastValidCalibrationMatrixCol@G3D_HTL@@YAHPEAH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int getLastValidCalibrationMatrixCol(out int value); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?getFirstValidCalibrationMatrixRow@G3D_HTL@@YAHPEAH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int getFirstValidCalibrationMatrixRow(out int value); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?getLastValidCalibrationMatrixRow@G3D_HTL@@YAHPEAH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int getLastValidCalibrationMatrixRow(out int value); + + // ------------------------------------------------ + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?startHeadTracking@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int startHeadTracking(); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?stopHeadTracking@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int stopHeadTracking(); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?getHeadTrackingStatus@G3D_HTL@@YAHPEA_N0@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int getHeadTrackingStatus( + [MarshalAs(UnmanagedType.U1)] out bool hasDevice, + [MarshalAs(UnmanagedType.U1)] out bool isTracking + ); - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?unregisterMessageCallback@G3D_HTL@@YAHPEAX@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int unregisterMessageCallback(IntPtr listener); - - // ------------------------------------------------ - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?useHimaxD2XXDevices@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int useHimaxD2XXDevices(); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?useHimaxRP2040Devices@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int useHimaxRP2040Devices(); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?usePmdFlexxDevices@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int usePmdFlexxDevices(); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?initHeadTracking@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int initHeadTracking(); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?deinitHeadTracking@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int deinitHeadTracking(); - - // ------------------------------------------------ - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?getHeadTrackingDeviceCount@G3D_HTL@@YAHPEAH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int getHeadTrackingDeviceCount(out int deviceCount); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?getHeadTrackingDeviceName@G3D_HTL@@YAHHPEAPEAD@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int getHeadTrackingDeviceName(int deviceNumber, out IntPtr deviceName); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?getCurrentHeadTrackingDevice@G3D_HTL@@YAHPEAPEAD@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int getCurrentHeadTrackingDevice(out IntPtr deviceName); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?setCurrentHeadTrackingDevice@G3D_HTL@@YAHPEBD@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int setCurrentHeadTrackingDevice(in byte[] device); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?getHeadTrackingDeviceResolution@G3D_HTL@@YAHPEAH0@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int getHeadTrackingDeviceResolution( - out int horizontalResolution, - out int verticalResolution - ); + // ------------------------------------------------ - // ------------------------------------------------ - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?getFirstValidCalibrationMatrixCol@G3D_HTL@@YAHPEAH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int getFirstValidCalibrationMatrixCol(out int value); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?getLastValidCalibrationMatrixCol@G3D_HTL@@YAHPEAH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int getLastValidCalibrationMatrixCol(out int value); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?getFirstValidCalibrationMatrixRow@G3D_HTL@@YAHPEAH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int getFirstValidCalibrationMatrixRow(out int value); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?getLastValidCalibrationMatrixRow@G3D_HTL@@YAHPEAH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int getLastValidCalibrationMatrixRow(out int value); - - // ------------------------------------------------ - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?startHeadTracking@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int startHeadTracking(); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?stopHeadTracking@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int stopHeadTracking(); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?getHeadTrackingStatus@G3D_HTL@@YAHPEA_N0@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int getHeadTrackingStatus( - [MarshalAs(UnmanagedType.U1)] out bool hasDevice, - [MarshalAs(UnmanagedType.U1)] out bool isTracking - ); + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?getCurrentShaderParameters@G3D_HTL@@YAHPEAUCG3DShaderParameters@@@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int getCurrentShaderParameters( + out G3DShaderParameters shaderParameters + ); - // ------------------------------------------------ - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?getCurrentShaderParameters@G3D_HTL@@YAHPEAUCG3DShaderParameters@@@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int getCurrentShaderParameters(out G3DShaderParameters shaderParameters); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?setWindowPosition@G3D_HTL@@YAHHH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int setWindowPosition(int left, int top); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?setWindowSize@G3D_HTL@@YAHHH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int setWindowSize(int width, int height); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?setViewportOffset@G3D_HTL@@YAHHH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int setViewportOffset(int left, int top); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?setViewportSize@G3D_HTL@@YAHHH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int setViewportSize(int width, int height); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?setScreenSize@G3D_HTL@@YAHHH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int setScreenSize(int width, int height); - - // ------------------------------------------------ - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?shiftViewToLeft@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int shiftViewToLeft(); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?shiftViewToRight@G3D_HTL@@YAHXZ", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int shiftViewToRight(); - - // ------------------------------------------------ - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?initializePositionFilter@G3D_HTL@@YAHHHH@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int initializePositionFilter(int lenX, int lenY, int lenZ); - - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?applyPositionFilter@G3D_HTL@@YAHNNNPEAN00@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int applyPositionFilter( - double posX, - double posY, - double posZ, - out double filteredX, - out double filteredY, - out double filteredZ - ); + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?setWindowPosition@G3D_HTL@@YAHHH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int setWindowPosition(int left, int top); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?setWindowSize@G3D_HTL@@YAHHH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int setWindowSize(int width, int height); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?setViewportOffset@G3D_HTL@@YAHHH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int setViewportOffset(int left, int top); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?setViewportSize@G3D_HTL@@YAHHH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int setViewportSize(int width, int height); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?setScreenSize@G3D_HTL@@YAHHH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int setScreenSize(int width, int height); + + // ------------------------------------------------ + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?shiftViewToLeft@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int shiftViewToLeft(); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?shiftViewToRight@G3D_HTL@@YAHXZ", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int shiftViewToRight(); + + // ------------------------------------------------ + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?initializePositionFilter@G3D_HTL@@YAHHHH@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int initializePositionFilter(int lenX, int lenY, int lenZ); + + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?applyPositionFilter@G3D_HTL@@YAHNNNPEAN00@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int applyPositionFilter( + double posX, + double posY, + double posZ, + out double filteredX, + out double filteredY, + out double filteredZ + ); - // ------------------------------------------------ + // ------------------------------------------------ - [DllImport( - "G3D_HeadTrackingLibrary_c.dll", - EntryPoint = "?calculateShaderParameters@G3D_HTL@@YAHW4LatencyCorrectionMode@1@@Z", - CallingConvention = CallingConvention.Cdecl - )] - public static extern int calculateShaderParameters(LatencyCorrectionMode latencyCorrectionMode); + [DllImport( + "G3D_HeadTrackingLibrary_c.dll", + EntryPoint = "?calculateShaderParameters@G3D_HTL@@YAHW4LatencyCorrectionMode@1@@Z", + CallingConvention = CallingConvention.Cdecl + )] + public static extern int calculateShaderParameters( + LatencyCorrectionMode latencyCorrectionMode + ); + } } diff --git a/Scripts/HeadtrackingConnection.cs b/Scripts/HeadtrackingConnection.cs deleted file mode 100644 index 16e994b..0000000 --- a/Scripts/HeadtrackingConnection.cs +++ /dev/null @@ -1,732 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using UnityEngine; - -public class HeadtrackingConnection - : ITNewHeadPositionCallback, - ITNewShaderParametersCallback, - ITNewErrorMessageCallback -{ - [Tooltip("Time it takes till the reset animation starts in seconds.")] - public float headLostTimeoutInSec = 3.0f; - - [Tooltip("Reset animation duratuion in seconds.")] - public float transitionDuration = 0.5f; - - public Vector3Int headPositionFilter = new Vector3Int(5, 5, 5); - public LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE; - - public float focusDistance = 1.0f; - public float headTrackingScale = 1.0f; - public float sceneScaleFactor = 1.0f; - - private bool debugMessages; - - private Vector3 lastHeadPosition = new Vector3(0, 0, 0); - - private float headLostTimer = 0.0f; - private float transitionTime = 0.0f; - - private enum HeadTrackingState - { - TRACKING, - LOST, - TRANSITIONTOLOST, - TRANSITIONTOTRACKING, - LOSTGRACEPERIOD - } - - private HeadTrackingState prevHeadTrackingState = HeadTrackingState.LOST; - - private LibInterface libInterface; - private string calibrationPath; - - /// - /// This struct is used to store the current head position. - /// It is updated in a different thread, so always use getHeadPosition() to get the current head position. - /// NEVER use headPosition directly. - /// - private HeadPosition headPosition; - private HeadPosition filteredHeadPosition; - - private static object headPosLock = new object(); - private static object shaderLock = new object(); - - private Queue headPositionLog; - - private G3DCamera g3dCamera; - - public HeadtrackingConnection( - float focusDistance, - float headTrackingScale, - float sceneScaleFactor, - string calibrationPathOverwrite, - G3DCamera g3dCamera, - bool debugMessages = false, - Vector3Int headPositionFilter = new Vector3Int(), - LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE - ) - { - lastHeadPosition = new Vector3(0, 0, -focusDistance); - this.focusDistance = focusDistance; - this.headTrackingScale = headTrackingScale; - this.sceneScaleFactor = sceneScaleFactor; - - calibrationPath = System.Environment.GetFolderPath( - Environment.SpecialFolder.CommonDocuments - ); - calibrationPath = Path.Combine(calibrationPath, "3D Global", "calibrations"); - if (!string.IsNullOrEmpty(calibrationPathOverwrite)) - { - calibrationPath = calibrationPathOverwrite; - } - - this.debugMessages = debugMessages; - this.headPositionFilter = headPositionFilter; - this.latencyCorrectionMode = latencyCorrectionMode; - - this.g3dCamera = g3dCamera; - - headPositionLog = new Queue(10000); - } - - public void startHeadTracking() - { - try - { - libInterface.startHeadTracking(); - } - catch (Exception e) - { - Debug.LogError("Failed to start head tracking: " + e.Message); - } - } - - public void shiftViewToLeft() - { - try - { - libInterface.shiftViewToLeft(); - } - catch (Exception e) - { - Debug.LogError("Failed to shift view to left: " + e.Message); - } - } - - public void shiftViewToRight() - { - try - { - libInterface.shiftViewToRight(); - } - catch (Exception e) - { - Debug.LogError("Failed to shift view to right: " + e.Message); - } - } - - public void toggleHeadTracking() - { - if (libInterface == null || !libInterface.isInitialized()) - { - return; - } - - try - { - HeadTrackingStatus headtrackingConnection = libInterface.getHeadTrackingStatus(); - if (headtrackingConnection.hasTrackingDevice) - { - if (!headtrackingConnection.isTrackingActive) - { - libInterface.startHeadTracking(); - } - else - { - libInterface.stopHeadTracking(); - } - } - } - catch (Exception e) - { - Debug.LogError("Failed to toggle head tracking status: " + e.Message); - } - } - - /// - /// always use this method to get the current head position. - /// NEVER access headPosition directly, as it is updated in a different thread. - /// - /// - /// - public HeadPosition getHeadPosition() - { - HeadPosition currentHeadPosition; - lock (headPosLock) - { - if (usePositionFiltering()) - { - currentHeadPosition = filteredHeadPosition; - } - else - { - currentHeadPosition = headPosition; - } - } - return currentHeadPosition; - } - - /** - * Calculate the new camera center position based on the head tracking. - * If head tracking is lost, or the head moves to far away from the tracking camera a grace periope is started. - * Afterwards the camera center will be animated back towards the default position. - */ - public void handleHeadTrackingState( - ref Vector3 targetPosition, - ref float targetViewSeparation, - float viewSeparation, - float focusDistance - ) - { - HeadPosition headPos = getHeadPosition(); - // get new state - HeadTrackingState newState = getNewTrackingState(prevHeadTrackingState, ref headPos); - - prevHeadTrackingState = newState; - - // handle lost state - if (newState == HeadTrackingState.LOST) - { - targetPosition = new Vector3(0, 0, -focusDistance); - targetViewSeparation = 0.0f; - } - // handle tracking state - else if (newState == HeadTrackingState.TRACKING) - { - Vector3 headPositionWorld = new Vector3( - (float)headPos.worldPosX, - (float)headPos.worldPosY, - (float)headPos.worldPosZ - ); - - targetPosition = headPositionWorld; - targetViewSeparation = viewSeparation; - lastHeadPosition = targetPosition; - } - // if lost, start grace period - else if (newState == HeadTrackingState.LOSTGRACEPERIOD) - { - // if we have waited for the timeout - if (headLostTimer > headLostTimeoutInSec) - { - newState = HeadTrackingState.TRANSITIONTOLOST; - headLostTimer = 0.0f; - transitionTime = 0.0f; - } - else - { - headLostTimer += Time.deltaTime; - targetPosition = lastHeadPosition; - targetViewSeparation = viewSeparation; - } - } - // handle transitions - else if ( - newState == HeadTrackingState.TRANSITIONTOLOST - || newState == HeadTrackingState.TRANSITIONTOTRACKING - ) - { - // init with values for transition to lost - Vector3 originPosition = lastHeadPosition; - Vector3 transitionTargetPosition = new Vector3(0, 0, -focusDistance); - float transitionViewSeparation = 0.0f; - float originSeparation = viewSeparation; - - if (newState == HeadTrackingState.TRANSITIONTOTRACKING) - { - originPosition = new Vector3(0, 0, -focusDistance); - transitionViewSeparation = viewSeparation; - originSeparation = 0.0f; - - if (headPos.headDetected) - { - Vector3 headPositionWorld = new Vector3( - (float)headPos.worldPosX, - (float)headPos.worldPosY, - (float)headPos.worldPosZ - ); - transitionTargetPosition = headPositionWorld; - } - else - { - // if no head is detected use last known head position - transitionTargetPosition = lastHeadPosition; - } - } - - bool isEndReached = handleTransition( - originPosition, - transitionTargetPosition, - originSeparation, - transitionViewSeparation, - ref targetPosition, - ref targetViewSeparation - ); - if (isEndReached) - { - transitionTime = 0.0f; - // if we have reached the target position, we are no longer in transition - if (newState == HeadTrackingState.TRANSITIONTOLOST) - { - newState = HeadTrackingState.LOST; - } - else - { - newState = HeadTrackingState.TRACKING; - } - } - } - - // reset lost timer if we are not in grace period - if (newState != HeadTrackingState.LOSTGRACEPERIOD) - { - headLostTimer = 0.0f; - } - - prevHeadTrackingState = newState; - } - - public void initLibrary() - { - string applicationName = Application.productName; - if (string.IsNullOrEmpty(applicationName)) - { - applicationName = "Unity"; - } - var invalids = System.IO.Path.GetInvalidFileNameChars(); - applicationName = String - .Join("_", applicationName.Split(invalids, StringSplitOptions.RemoveEmptyEntries)) - .TrimEnd('.'); - applicationName = applicationName + "_G3D_Config.ini"; - - try - { - bool useHimaxD2XXDevices = true; - bool useHimaxRP2040Devices = true; - bool usePmdFlexxDevices = true; - - libInterface = LibInterface.Instance; - libInterface.init( - calibrationPath, - Application.persistentDataPath, - applicationName, - this, - this, - this, - debugMessages, - useHimaxD2XXDevices, - useHimaxRP2040Devices, - usePmdFlexxDevices - ); - } - catch (Exception e) - { - Debug.LogError("Failed to initialize library: " + e.Message); - return; - } - - // set initial values - // intialize head position at focus distance from focus plane - headPosition = new HeadPosition - { - headDetected = false, - imagePosIsValid = false, - imagePosX = 0, - imagePosY = 0, - worldPosX = 0.0, - worldPosY = 0.0, - worldPosZ = -focusDistance - }; - filteredHeadPosition = new HeadPosition - { - headDetected = false, - imagePosIsValid = false, - imagePosX = 0, - imagePosY = 0, - worldPosX = 0.0, - worldPosY = 0.0, - worldPosZ = -focusDistance - }; - - if (usePositionFiltering()) - { - try - { - libInterface.initializePositionFilter( - headPositionFilter.x, - headPositionFilter.y, - headPositionFilter.z - ); - } - catch (Exception e) - { - Debug.LogError("Failed to initialize position filter: " + e.Message); - } - } - } - - public void deinitLibrary() - { - if (libInterface == null || !libInterface.isInitialized()) - { - return; - } - - try - { - libInterface.stopHeadTracking(); - libInterface.unregisterHeadPositionChangedCallback(this); - libInterface.unregisterShaderParametersChangedCallback(this); - libInterface.unregisterMessageCallback(this); - libInterface.deinit(); - } - catch (Exception e) - { - Debug.Log(e); - } - } - - public void logCameraPositionsToFile() - { - StreamWriter writer = new StreamWriter( - Application.dataPath + "/HeadPositionLog.csv", - false - ); - writer.WriteLine( - "Camera update time; Camera X; Camera Y; Camera Z; Head detected; Image position valid; Filtered X; Filtered Y; Filtered Z" - ); - string[] headPoitionLogArray = headPositionLog.ToArray(); - for (int i = 0; i < headPoitionLogArray.Length; i++) - { - writer.WriteLine(headPoitionLogArray[i]); - } - writer.Close(); - } - - public void calculateShaderParameters() - { - libInterface.calculateShaderParameters(latencyCorrectionMode); - g3dCamera.setShaderParameters(libInterface.getCurrentShaderParameters()); - } - - public void updateScreenViewportProperties(Vector2Int displayResolution) - { - try - { - // This is the size of the entire monitor screen - libInterface.setScreenSize(displayResolution.x, displayResolution.y); - - // this refers to the window in which the 3D effect is rendered (including eg windows top window menu) - libInterface.setWindowSize(Screen.width, Screen.height); - libInterface.setWindowPosition( - Screen.mainWindowPosition.x, - Screen.mainWindowPosition.y - ); - - // This refers to the actual viewport in which the 3D effect is rendered - libInterface.setViewportSize(Screen.width, Screen.height); - libInterface.setViewportOffset(0, 0); - } - catch (Exception e) - { - Debug.LogError("Failed to update screen viewport properties: " + e.Message); - } - } - - /// - /// returns true if transition end is reached - /// - /// - /// - /// - /// - /// - /// is transition end reached - private bool handleTransition( - Vector3 originPosition, - Vector3 transitionTargetPosition, - float originSeparation, - float transitionViewSeparation, - ref Vector3 targetPosition, - ref float targetViewSeparation - ) - { - // interpolate values - float transitionPercentage = transitionTime / transitionDuration; - transitionTime += Time.deltaTime; - - // set to default position - Vector3 interpolatedPosition = Vector3.Lerp( - originPosition, - transitionTargetPosition, - transitionPercentage - ); - float interpolatedEyeSeparation = Mathf.Lerp( - originSeparation, - transitionViewSeparation, - transitionPercentage - ); - - // check if end is reached (with a small tolerance) - if (transitionPercentage + 0.01f < 1.0f) - { - // apply values - targetPosition = interpolatedPosition; - targetViewSeparation = interpolatedEyeSeparation; - return false; - } - else - { - return true; - } - } - - private HeadTrackingState getNewTrackingState( - HeadTrackingState currentHeadTrackingState, - ref HeadPosition headPosition - ) - { - HeadTrackingState newState; - - if (headPosition.headDetected) - { - // if head detected - if (currentHeadTrackingState == HeadTrackingState.TRACKING) - { - newState = HeadTrackingState.TRACKING; - } - else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD) - { - newState = HeadTrackingState.TRACKING; - } - else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING) - { - newState = HeadTrackingState.TRANSITIONTOTRACKING; - } - else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST) - { - newState = HeadTrackingState.TRANSITIONTOLOST; - } - else //(currentHeadTrackingState == HeadTrackingState.LOST) - { - newState = HeadTrackingState.TRANSITIONTOTRACKING; - } - } - else - { - // if head not detected - if (currentHeadTrackingState == HeadTrackingState.TRACKING) - { - newState = HeadTrackingState.LOSTGRACEPERIOD; - } - else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD) - { - newState = HeadTrackingState.LOSTGRACEPERIOD; - } - else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING) - { - newState = HeadTrackingState.TRANSITIONTOTRACKING; - } - else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST) - { - newState = HeadTrackingState.TRANSITIONTOLOST; - } - else //(currentHeadTrackingState == HeadTrackingState.LOST) - { - newState = HeadTrackingState.LOST; - } - } - return newState; - } - - private string headTrackingStateToString() - { - switch (prevHeadTrackingState) - { - case HeadTrackingState.TRACKING: - return "TRACKING"; - case HeadTrackingState.LOST: - return "LOST"; - case HeadTrackingState.LOSTGRACEPERIOD: - return "LOSTGRACEPERIOD"; - case HeadTrackingState.TRANSITIONTOLOST: - return "TRANSITIONTOLOST"; - case HeadTrackingState.TRANSITIONTOTRACKING: - return "TRANSITIONTOTRACKING"; - default: - return "UNKNOWN"; - } - } - - #region callback handling - void ITNewHeadPositionCallback.NewHeadPositionCallback( - bool headDetected, - bool imagePosIsValid, - int imagePosX, - int imagePosY, - double worldPosX, - double worldPosY, - double worldPosZ - ) - { - lock (headPosLock) - { - string logEntry = - DateTime.Now.ToString("HH:mm::ss.fff") - + ";" - + worldPosX - + ";" - + worldPosY - + ";" - + worldPosZ - + ";" - + headDetected - + ";" - + imagePosIsValid - + ";"; - - headPosition.headDetected = headDetected; - headPosition.imagePosIsValid = imagePosIsValid; - - int millimeterToMeter = 1000; - - Vector3 headPos = new Vector3( - (float)-worldPosX / millimeterToMeter, - (float)worldPosY / millimeterToMeter, - (float)-worldPosZ / millimeterToMeter - ); - - int scaleFactorInt = (int)sceneScaleFactor * (int)headTrackingScale; - float scaleFactor = sceneScaleFactor * headTrackingScale; - - headPosition.imagePosX = imagePosX / (int)millimeterToMeter * scaleFactorInt; - headPosition.imagePosY = imagePosY / (int)millimeterToMeter * scaleFactorInt; - headPosition.worldPosX = headPos.x * scaleFactor; - headPosition.worldPosY = headPos.y * scaleFactor; - headPosition.worldPosZ = headPos.z * sceneScaleFactor; - - if (usePositionFiltering()) - { - double filteredPositionX; - double filteredPositionY; - double filteredPositionZ; - - if (headDetected) - { - try - { - libInterface.applyPositionFilter( - worldPosX, - worldPosY, - worldPosZ, - out filteredPositionX, - out filteredPositionY, - out filteredPositionZ - ); - - filteredHeadPosition.worldPosX = - -filteredPositionX / millimeterToMeter * scaleFactor; - filteredHeadPosition.worldPosY = - filteredPositionY / millimeterToMeter * scaleFactor; - filteredHeadPosition.worldPosZ = - -filteredPositionZ / millimeterToMeter * sceneScaleFactor; - } - catch (Exception e) - { - Debug.LogError("Failed to apply position filter: " + e.Message); - } - } - - filteredHeadPosition.headDetected = headDetected; - filteredHeadPosition.imagePosIsValid = imagePosIsValid; - - logEntry += - filteredHeadPosition.worldPosX - + ";" - + filteredHeadPosition.worldPosY - + ";" - + filteredHeadPosition.worldPosZ - + ";"; - } - - headPositionLog.Enqueue(logEntry); - } - } - - void ITNewErrorMessageCallback.NewErrorMessageCallback( - EMessageSeverity severity, - string sender, - string caption, - string cause, - string remedy - ) - { - string messageText = formatErrorMessage(caption, cause, remedy); - switch (severity) - { - case EMessageSeverity.MS_EXCEPTION: - Debug.LogError(messageText); - break; - case EMessageSeverity.MS_ERROR: - Debug.LogError(messageText); - break; - case EMessageSeverity.MS_WARNING: - Debug.LogWarning(messageText); - break; - case EMessageSeverity.MS_INFO: - - Debug.Log(messageText); - break; - default: - Debug.Log(messageText); - break; - } - } - - /// - /// The shader parameters contain everything necessary for the shader to render the 3D effect. - /// These are updated every time a new head position is received. - /// They do not update the head position itself. - /// - /// - void ITNewShaderParametersCallback.NewShaderParametersCallback( - G3DShaderParameters shaderParameters - ) - { - g3dCamera.setShaderParameters(shaderParameters); - } - - private string formatErrorMessage(string caption, string cause, string remedy) - { - string messageText = caption + ": " + cause; - - if (string.IsNullOrEmpty(remedy) == false) - { - messageText = messageText + "\n" + remedy; - } - - return messageText; - } - #endregion - - /// - /// Returns false if all values of the position filter are set to zero. - /// - /// - private bool usePositionFiltering() - { - return headPositionFilter.x != 0 || headPositionFilter.y != 0 || headPositionFilter.z != 0; - } -} diff --git a/Scripts/IndexMap.cs b/Scripts/IndexMap.cs index a6f8eaa..165bbcc 100644 --- a/Scripts/IndexMap.cs +++ b/Scripts/IndexMap.cs @@ -1,852 +1,915 @@ using System; using System.Collections.Generic; -public class IndexMap +namespace G3D { - private static readonly IndexMap internalInstance = new IndexMap(); - - // Explicit static constructor to tell C# compiler - // not to mark type as beforefieldinit - static IndexMap() + public class IndexMap { - internalInstance.generateDefaultMaps(); - } + private static readonly IndexMap internalInstance = new IndexMap(); - public static IndexMap Instance - { - get { return internalInstance; } - } + // Explicit static constructor to tell C# compiler + // not to mark type as beforefieldinit + static IndexMap() + { + internalInstance.generateDefaultMaps(); + } - private int BLANK_VIEW = 250; // corresponds to a black view - public Dictionary maps; + public static IndexMap Instance + { + get { return internalInstance; } + } - public int[] currentMap = new int[] { 0 }; + private int BLANK_VIEW = 250; // corresponds to a black view + public Dictionary maps; - public void generateDefaultMaps() - { - maps = new Dictionary(); - - string name = "A"; - int[] mapArray = new int[35]; - generate_indexmap(ref mapArray, mapArray.Length, 2, 0.0f, 1.0f, false); - maps[name] = mapArray; - - name = "B"; - mapArray = new int[35]; - generate_indexmap(ref mapArray, mapArray.Length, 6, 0.0f, 1.0f, false); - maps[name] = mapArray; - - name = "C"; - mapArray = new int[35]; - generate_indexmap(ref mapArray, mapArray.Length, 8, 0.0f, 1.0f, false); - maps[name] = mapArray; - - name = "D"; - mapArray = new int[35]; - generate_indexmap(ref mapArray, mapArray.Length, 9, 0.0f, 1.0f, false); - maps[name] = mapArray; - - name = "E"; - mapArray = new int[35]; - generate_indexmap(ref mapArray, mapArray.Length, 16, 0.0f, 1.0f, false); - maps[name] = mapArray; - - name = "S1D"; - maps.Add( - name, - new int[] - { - 250, - 250, - 250, - 250, - 250, - 250, - 250, - 250, - 250, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 250, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 250, - 250, - 250, - 250, - 250, - 250, - 250, - 250, - 250 - } - ); + public int[] currentMap = new int[] { 0 }; - name = "S2D"; - maps.Add( - name, - new int[] - { - 250, - 250, - 250, - 250, - 250, - 250, - 250, - 250, - 250, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 250, - 250, - 250, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 250, - 250, - 250, - 250, - 250, - 250, - 250, - 250, - 250 - } - ); + public void generateDefaultMaps() + { + maps = new Dictionary(); + + string name = "A"; + int[] mapArray = new int[35]; + generate_indexmap(ref mapArray, mapArray.Length, 2, 0.0f, 1.0f, false); + maps[name] = mapArray; + + name = "B"; + mapArray = new int[35]; + generate_indexmap(ref mapArray, mapArray.Length, 6, 0.0f, 1.0f, false); + maps[name] = mapArray; + + name = "C"; + mapArray = new int[35]; + generate_indexmap(ref mapArray, mapArray.Length, 8, 0.0f, 1.0f, false); + maps[name] = mapArray; + + name = "D"; + mapArray = new int[35]; + generate_indexmap(ref mapArray, mapArray.Length, 9, 0.0f, 1.0f, false); + maps[name] = mapArray; + + name = "E"; + mapArray = new int[35]; + generate_indexmap(ref mapArray, mapArray.Length, 16, 0.0f, 1.0f, false); + maps[name] = mapArray; + + name = "S1D"; + maps.Add( + name, + new int[] + { + 250, + 250, + 250, + 250, + 250, + 250, + 250, + 250, + 250, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 250, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 250, + 250, + 250, + 250, + 250, + 250, + 250, + 250, + 250 + } + ); - name = "S3D"; - maps.Add( - name, - new int[] - { - 250, - 250, - 250, - 250, - 250, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 250, - 250, - 250, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 250, - 250, - 250, - 250, - 250 - } - ); + name = "S2D"; + maps.Add( + name, + new int[] + { + 250, + 250, + 250, + 250, + 250, + 250, + 250, + 250, + 250, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 250, + 250, + 250, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 250, + 250, + 250, + 250, + 250, + 250, + 250, + 250, + 250 + } + ); - name = "S4D"; - maps.Add( - name, - new int[] - { - 250, - 250, - 250, - 250, - 250, - 250, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 250, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 250, - 250, - 250, - 250, - 250, - 250 - } - ); + name = "S3D"; + maps.Add( + name, + new int[] + { + 250, + 250, + 250, + 250, + 250, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 250, + 250, + 250, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 250, + 250, + 250, + 250, + 250 + } + ); - name = "S6D"; - maps.Add( - name, - new int[] - { - 250, - 250, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 250, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 250, - 250 - } - ); + name = "S4D"; + maps.Add( + name, + new int[] + { + 250, + 250, + 250, + 250, + 250, + 250, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 250, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 250, + 250, + 250, + 250, + 250, + 250 + } + ); - name = "05A"; - maps.Add(name, new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 }); + name = "S6D"; + maps.Add( + name, + new int[] + { + 250, + 250, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 250, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 250, + 250 + } + ); - name = "05C"; - maps.Add(name, new int[] { 0, 1, 2, 3, 4 }); + name = "05A"; + maps.Add(name, new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 }); - name = "05N"; - maps.Add(name, new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 }); + name = "05C"; + maps.Add(name, new int[] { 0, 1, 2, 3, 4 }); - name = "05O"; - maps.Add(name, new int[] { 0, 1, 2, 3, 4 }); + name = "05N"; + maps.Add(name, new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 }); - name = "05P"; - maps.Add(name, new int[] { 0, 1, 2, 3, 4 }); + name = "05O"; + maps.Add(name, new int[] { 0, 1, 2, 3, 4 }); - name = "05Q"; - maps.Add( - name, - new int[] { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4 } - ); + name = "05P"; + maps.Add(name, new int[] { 0, 1, 2, 3, 4 }); - name = "05R"; - maps.Add(name, new int[] { 0, 1, 2, 3, 4 }); + name = "05Q"; + maps.Add( + name, + new int[] + { + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4 + } + ); - name = "07A"; - maps.Add(name, new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6 }); + name = "05R"; + maps.Add(name, new int[] { 0, 1, 2, 3, 4 }); - name = "07D"; - maps.Add( - name, - new int[] - { - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5, - 5, - 6, - 6, - 6, - 6, - 6 - } - ); + name = "07A"; + maps.Add( + name, + new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6 } + ); - name = "08A"; - maps.Add( - name, - new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7 } - ); + name = "07D"; + maps.Add( + name, + new int[] + { + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 6, + 6, + 6, + 6, + 6 + } + ); - name = "08B"; - maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }); + name = "08A"; + maps.Add( + name, + new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7 } + ); - name = "08D"; - maps.Add( - name, - new int[] - { - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5, - 5, - 6, - 6, - 6, - 6, - 6, - 7, - 7, - 7, - 7, - 7 - } - ); + name = "08B"; + maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }); - name = "08O"; - maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }); + name = "08D"; + maps.Add( + name, + new int[] + { + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 6, + 6, + 6, + 6, + 6, + 7, + 7, + 7, + 7, + 7 + } + ); - name = "08Q"; - maps.Add( - name, - new int[] - { - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5, - 5, - 6, - 6, - 6, - 6, - 6, - 7, - 7, - 7, - 7, - 7 - } - ); + name = "08O"; + maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }); - name = "120views"; - maps.Add( - name, - new int[] - { - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 101, - 102, - 103, - 104, - 105, - 106, - 107, - 108, - 109, - 110, - 111, - 112, - 113, - 114, - 115, - 116, - 117, - 118, - 119 - } - ); + name = "08Q"; + maps.Add( + name, + new int[] + { + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 6, + 6, + 6, + 6, + 6, + 7, + 7, + 7, + 7, + 7 + } + ); + + name = "120views"; + maps.Add( + name, + new int[] + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119 + } + ); - name = "A8A"; - maps.Add( - name, - new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7 } - ); + name = "A8A"; + maps.Add( + name, + new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7 } + ); - name = "A8B"; - maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }); + name = "A8B"; + maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }); - name = "A8O"; - maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }); + name = "A8O"; + maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }); - name = "IPRO_CUS01"; - maps.Add( - name, - new int[] + name = "IPRO_CUS01"; + maps.Add( + name, + new int[] + { + 0, + 77, + 0, + 77, + 0, + 77, + 0, + 77, + 77, + 77, + 77, + 77, + 1, + 77, + 1, + 77, + 1, + 77, + 1, + 77, + 77, + 77, + 77, + 77 + } + ); // 77 == blank? if so: 77 = 250! + } + + private void generate_indexmap( + ref int[] indexmap, + int availableViews, + int contentViews, + float delimiterViews, + float yoyoStart, + bool invert, + bool invertIndices = false + ) + { + // sanitize inputs + yoyoStart = Math.Clamp(yoyoStart, 0.0f, 1.0f); + delimiterViews = Math.Clamp(delimiterViews, 0.0f, 1.0f); + + if (availableViews <= 1 || contentViews == 1) { - 0, - 77, - 0, - 77, - 0, - 77, - 0, - 77, - 77, - 77, - 77, - 77, - 1, - 77, - 1, - 77, - 1, - 77, - 1, - 77, - 77, - 77, - 77, - 77 + for (int j = 0; j < availableViews; j++) + indexmap[j] = 0; + return; } - ); // 77 == blank? if so: 77 = 250! - } - private void generate_indexmap( - ref int[] indexmap, - int availableViews, - int contentViews, - float delimiterViews, - float yoyoStart, - bool invert, - bool invertIndices = false - ) - { - // sanitize inputs - yoyoStart = Math.Clamp(yoyoStart, 0.0f, 1.0f); - delimiterViews = Math.Clamp(delimiterViews, 0.0f, 1.0f); + if (yoyoStart < 1.0f) + { + int nonInvertedCount = (int)(availableViews * yoyoStart); + int[] indexmapNonInvertedPart = new int[nonInvertedCount]; + int[] indexmapInvertedPart = new int[availableViews - nonInvertedCount]; + + generateAutomaticDistribution( + ref indexmapNonInvertedPart, + nonInvertedCount, + contentViews, + 0.0f, + false + ); + generateAutomaticDistribution( + ref indexmapInvertedPart, + availableViews - nonInvertedCount, + contentViews, + 0.0f, + true + ); + Array.Copy(indexmapNonInvertedPart, 0, indexmap, 0, nonInvertedCount); + Array.Copy( + indexmapInvertedPart, + 0, + indexmap, + nonInvertedCount, + availableViews - nonInvertedCount + ); + } + else + { + generateAutomaticDistribution( + ref indexmap, + availableViews, + contentViews, + delimiterViews, + invert + ); + } - if (availableViews <= 1 || contentViews == 1) - { - for (int j = 0; j < availableViews; j++) - indexmap[j] = 0; - return; + if (invert) + { + int x = 0; + int y = availableViews - 1; + int tmp; + while (x < y) + { + tmp = indexmap[x]; + indexmap[x] = indexmap[y]; + indexmap[y] = tmp; + x++; + y--; + } + } + + if (invertIndices) + { + for (int i = 0; i < indexmap.Length; i++) + { + if (indexmap[i] != BLANK_VIEW) + { + indexmap[i] = contentViews - 1 - indexmap[i]; + } + } + } } - if (yoyoStart < 1.0f) + private void generateAutomaticDistribution( + ref int[] indexmap, + int availableViews, + int contentViews, + float delimiterViews, + bool invert + ) { - int nonInvertedCount = (int)(availableViews * yoyoStart); - int[] indexmapNonInvertedPart = new int[nonInvertedCount]; - int[] indexmapInvertedPart = new int[availableViews - nonInvertedCount]; + if (indexmap.Length == 0) + return; - generateAutomaticDistribution( - ref indexmapNonInvertedPart, - nonInvertedCount, - contentViews, - 0.0f, - false - ); - generateAutomaticDistribution( - ref indexmapInvertedPart, - availableViews - nonInvertedCount, - contentViews, - 0.0f, - true - ); - Array.Copy(indexmapNonInvertedPart, 0, indexmap, 0, nonInvertedCount); - Array.Copy( - indexmapInvertedPart, - 0, - indexmap, - nonInvertedCount, - availableViews - nonInvertedCount - ); - } - else - { - generateAutomaticDistribution( - ref indexmap, - availableViews, - contentViews, - delimiterViews, - invert - ); - } + int i; + // automatic distribution + if (contentViews == 2) + { + i = generateTwoContentViews(ref indexmap, availableViews, delimiterViews); + } + else + { + i = generateMoreThanTwoContentViews( + ref indexmap, + availableViews, + contentViews, + delimiterViews + ); + } + for (int j = i; j < availableViews; j++) + indexmap[j] = 0; - if (invert) - { - int x = 0; - int y = availableViews - 1; - int tmp; - while (x < y) + if (invert) { - tmp = indexmap[x]; - indexmap[x] = indexmap[y]; - indexmap[y] = tmp; - x++; - y--; + int x = 0; + int y = availableViews - 1; + int tmp; + while (x < y) + { + tmp = indexmap[x]; + indexmap[x] = indexmap[y]; + indexmap[y] = tmp; + x++; + y--; + } } - } - if (invertIndices) - { - for (int i = 0; i < indexmap.Length; i++) + if (delimiterViews == 0.0f) { - if (indexmap[i] != BLANK_VIEW) + if (indexmap[0] == BLANK_VIEW) + indexmap[0] = 0; + for (int x = 1; x < availableViews; x++) { - indexmap[i] = contentViews - 1 - indexmap[i]; + if (indexmap[x] == BLANK_VIEW) + indexmap[x] = indexmap[x - 1]; } } } - } - - private void generateAutomaticDistribution( - ref int[] indexmap, - int availableViews, - int contentViews, - float delimiterViews, - bool invert - ) - { - if (indexmap.Length == 0) - return; - int i; - // automatic distribution - if (contentViews == 2) - { - i = generateTwoContentViews(ref indexmap, availableViews, delimiterViews); - } - else + private int generateTwoContentViews( + ref int[] indexmap, + int availableViews, + float delimiterViews + ) { - i = generateMoreThanTwoContentViews( - ref indexmap, - availableViews, - contentViews, - delimiterViews - ); + int i = 0; + float eyeAreaSpace = (1.0f - delimiterViews) / 2; + + int delimViews = (int)(availableViews * delimiterViews); + int eyeViews = (int)(availableViews * eyeAreaSpace); + int startDelimViews = (availableViews - delimViews - eyeViews * 2) / 2; + int endDelimViews = availableViews - delimViews - eyeViews * 2 - startDelimViews; + + for (; startDelimViews > 0; startDelimViews--) + indexmap[i++] = BLANK_VIEW; + for (int j = 0; j < eyeViews; j++) + indexmap[i++] = 1; + for (; delimViews > 0; delimViews--) + indexmap[i++] = BLANK_VIEW; + for (int j = 0; j < eyeViews; j++) + indexmap[i++] = 0; + for (; endDelimViews > 0; endDelimViews--) + indexmap[i++] = BLANK_VIEW; + + return i; } - for (int j = i; j < availableViews; j++) - indexmap[j] = 0; - if (invert) + private int generateMoreThanTwoContentViews( + ref int[] indexmap, + int availableViews, + int contentViews, + float delimiterViews + ) { - int x = 0; - int y = availableViews - 1; - int tmp; - while (x < y) + int i = 0; + int delimViewsStart = (int)(availableViews * delimiterViews / 2); + int delimViewsEnd = (int)(availableViews * delimiterViews / 2); + int spacePerView = (availableViews - delimViewsStart - delimViewsEnd) / contentViews; + int spaceLeftover = + availableViews - delimViewsStart - delimViewsEnd - spacePerView * contentViews; + + for (; delimViewsStart > 0; delimViewsStart--) + indexmap[i++] = BLANK_VIEW; + for (int viewIndex = 0; viewIndex < contentViews; viewIndex++) { - tmp = indexmap[x]; - indexmap[x] = indexmap[y]; - indexmap[y] = tmp; - x++; - y--; + int offset = 0; + if (spaceLeftover > 0) + { + offset = 1; + spaceLeftover--; + } + for (int currentSpace = spacePerView + offset; currentSpace > 0; currentSpace--) + { + indexmap[i++] = (int)(contentViews - 1 - viewIndex); + } } - } + for (; delimViewsEnd > 0; delimViewsEnd--) + indexmap[i++] = BLANK_VIEW; - if (delimiterViews == 0.0f) - { - if (indexmap[0] == BLANK_VIEW) - indexmap[0] = 0; - for (int x = 1; x < availableViews; x++) - { - if (indexmap[x] == BLANK_VIEW) - indexmap[x] = indexmap[x - 1]; - } + return i; } - } - private int generateTwoContentViews( - ref int[] indexmap, - int availableViews, - float delimiterViews - ) - { - int i = 0; - float eyeAreaSpace = (1.0f - delimiterViews) / 2; - - int delimViews = (int)(availableViews * delimiterViews); - int eyeViews = (int)(availableViews * eyeAreaSpace); - int startDelimViews = (availableViews - delimViews - eyeViews * 2) / 2; - int endDelimViews = availableViews - delimViews - eyeViews * 2 - startDelimViews; - - for (; startDelimViews > 0; startDelimViews--) - indexmap[i++] = BLANK_VIEW; - for (int j = 0; j < eyeViews; j++) - indexmap[i++] = 1; - for (; delimViews > 0; delimViews--) - indexmap[i++] = BLANK_VIEW; - for (int j = 0; j < eyeViews; j++) - indexmap[i++] = 0; - for (; endDelimViews > 0; endDelimViews--) - indexmap[i++] = BLANK_VIEW; - - return i; - } - - private int generateMoreThanTwoContentViews( - ref int[] indexmap, - int availableViews, - int contentViews, - float delimiterViews - ) - { - int i = 0; - int delimViewsStart = (int)(availableViews * delimiterViews / 2); - int delimViewsEnd = (int)(availableViews * delimiterViews / 2); - int spacePerView = (availableViews - delimViewsStart - delimViewsEnd) / contentViews; - int spaceLeftover = - availableViews - delimViewsStart - delimViewsEnd - spacePerView * contentViews; - - for (; delimViewsStart > 0; delimViewsStart--) - indexmap[i++] = BLANK_VIEW; - for (int viewIndex = 0; viewIndex < contentViews; viewIndex++) + /// + /// Updates the internal index map stored in "currentMap". + /// Also returns the generated index map. + /// Creates a new index map and adds it to the internal dictionary with the given key. + /// DANGEROUS: if the key already exists, the existing map will not be overwritten + /// if the parameter overwrite is false! + /// + /// key to the internal map. + /// number of available views on the display. has to be larger than 0 + /// number of views your content has. has to be larger than 0 + /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0 + /// if true, the final index map will be inverted + /// if true, the internal map in the internal dictionary will be overwritten + /// + public int[] UpdateIndexMapFromKey( + string key, + int availableViews, + int contentViews, + float yoyoStart, + bool invert, + bool invertIndices = false, + bool overwrite = false + ) { - int offset = 0; - if (spaceLeftover > 0) + if (maps.ContainsKey(key) && !overwrite) { - offset = 1; - spaceLeftover--; + // TODO: also check for parameters + // key found, return the loaded map + currentMap = maps[key]; } - for (int currentSpace = spacePerView + offset; currentSpace > 0; currentSpace--) + else { - indexmap[i++] = (int)(contentViews - 1 - viewIndex); + // key not found -> generate a map and return a pointer to that one + int[] mapArray = GetIndexMapTemp( + availableViews, + contentViews, + yoyoStart, + invert, + invertIndices + ); + maps[key] = mapArray; + currentMap = mapArray; } + return currentMap; } - for (; delimViewsEnd > 0; delimViewsEnd--) - indexmap[i++] = BLANK_VIEW; - return i; - } - - /// - /// Updates the internal index map stored in "currentMap". - /// Also returns the generated index map. - /// Creates a new index map and adds it to the internal dictionary with the given key. - /// DANGEROUS: if the key already exists, the existing map will not be overwritten - /// if the parameter overwrite is false! - /// - /// key to the internal map. - /// number of available views on the display. has to be larger than 0 - /// number of views your content has. has to be larger than 0 - /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0 - /// if true, the final index map will be inverted - /// if true, the internal map in the internal dictionary will be overwritten - /// - public int[] UpdateIndexMapFromKey( - string key, - int availableViews, - int contentViews, - float yoyoStart, - bool invert, - bool invertIndices = false, - bool overwrite = false - ) - { - if (maps.ContainsKey(key) && !overwrite) + /// + /// Updates the internal index map stored in "currentMap". + /// Also returns the generated index map. + /// + /// number of available views on the display. has to be larger than 0 + /// number of views your content has. has to be larger than 0 + /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0 + /// if true, the final index map will be inverted + /// if true, the internal map in the internal dictionary will be overwritten + /// + public int[] UpdateIndexMap( + int availableViews, + int contentViews, + float yoyoStart, + bool invert, + bool invertIndices = false + ) { - // TODO: also check for parameters - // key found, return the loaded map - currentMap = maps[key]; - } - else - { - // key not found -> generate a map and return a pointer to that one int[] mapArray = GetIndexMapTemp( availableViews, contentViews, @@ -854,100 +917,70 @@ public int[] UpdateIndexMapFromKey( invert, invertIndices ); - maps[key] = mapArray; currentMap = mapArray; + return currentMap; } - return currentMap; - } - - /// - /// Updates the internal index map stored in "currentMap". - /// Also returns the generated index map. - /// - /// number of available views on the display. has to be larger than 0 - /// number of views your content has. has to be larger than 0 - /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0 - /// if true, the final index map will be inverted - /// if true, the internal map in the internal dictionary will be overwritten - /// - public int[] UpdateIndexMap( - int availableViews, - int contentViews, - float yoyoStart, - bool invert, - bool invertIndices = false - ) - { - int[] mapArray = GetIndexMapTemp( - availableViews, - contentViews, - yoyoStart, - invert, - invertIndices - ); - currentMap = mapArray; - return currentMap; - } - - /// - /// Creates a new index map without storing it in the internal dictionary. - /// - /// number of available views on the display. has to be larger than 0 - /// number of views your content has. has to be larger than 0 - /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0 - /// if true, the final index map will be inverted - /// - public int[] GetIndexMapTemp( - int availableViews, - int contentViews, - float yoyoStart, - bool invert, - bool invertIndices = false - ) - { - int[] mapArray = new int[availableViews]; - generate_indexmap( - ref mapArray, - availableViews, - contentViews, - 0, - yoyoStart, - invert, - invertIndices - ); - return mapArray; - } - public float[] convertToFloatArray(in int[] intArray) - { - float[] floatArray = new float[intArray.Length]; - for (int i = 0; i < intArray.Length; i++) + /// + /// Creates a new index map without storing it in the internal dictionary. + /// + /// number of available views on the display. has to be larger than 0 + /// number of views your content has. has to be larger than 0 + /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0 + /// if true, the final index map will be inverted + /// + public int[] GetIndexMapTemp( + int availableViews, + int contentViews, + float yoyoStart, + bool invert, + bool invertIndices = false + ) { - floatArray[i] = intArray[i]; + int[] mapArray = new int[availableViews]; + generate_indexmap( + ref mapArray, + availableViews, + contentViews, + 0, + yoyoStart, + invert, + invertIndices + ); + return mapArray; } - return floatArray; - } - public float[] currentMapAsFloatArray() - { - return convertToFloatArray(currentMap); - } - - public float[] getPaddedIndexMapArray() - { - float[] paddedArray = new float[256]; - float[] indexMapArray = currentMapAsFloatArray(); - for (int i = 0; i < paddedArray.Length; i++) + public float[] convertToFloatArray(in int[] intArray) { - if (i < indexMapArray.Length) + float[] floatArray = new float[intArray.Length]; + for (int i = 0; i < intArray.Length; i++) { - paddedArray[i] = indexMapArray[i]; + floatArray[i] = intArray[i]; } - else + return floatArray; + } + + public float[] currentMapAsFloatArray() + { + return convertToFloatArray(currentMap); + } + + public float[] getPaddedIndexMapArray() + { + float[] paddedArray = new float[256]; + float[] indexMapArray = currentMapAsFloatArray(); + for (int i = 0; i < paddedArray.Length; i++) { - paddedArray[i] = 0.0f; + if (i < indexMapArray.Length) + { + paddedArray[i] = indexMapArray[i]; + } + else + { + paddedArray[i] = 0.0f; + } } + return paddedArray; } - return paddedArray; } } diff --git a/Scripts/InspectorG3DCamera.cs b/Scripts/InspectorG3DCamera.cs index 5bcb6ea..761be82 100644 --- a/Scripts/InspectorG3DCamera.cs +++ b/Scripts/InspectorG3DCamera.cs @@ -5,121 +5,124 @@ using UnityEngine; using UnityEngine.UIElements; -[CustomEditor(typeof(G3DCamera))] -public class InspectorG3DCamera : Editor +namespace G3D { - public VisualTreeAsset inspectorXML; + [CustomEditor(typeof(G3DCamera))] + public class InspectorG3DCamera : Editor + { + public VisualTreeAsset inspectorXML; - private PropertyField modeField; - private PropertyField generateViewsField; + private PropertyField modeField; + private PropertyField generateViewsField; - private PropertyField calibrationFileField; - private PropertyField headtrackingScaleField; + private PropertyField calibrationFileField; + private PropertyField headtrackingScaleField; - private PropertyField viewOffsetField; + private PropertyField viewOffsetField; - private Label calibFolderLabel; - private Label DioramaCalibFileInfo; + private Label calibFolderLabel; + private Label DioramaCalibFileInfo; - private static bool isAdvancedSettingsVisible = false; - private Foldout advancedSettingsFoldout; - private VisualElement viewGenerationContainer; + private static bool isAdvancedSettingsVisible = false; + private Foldout advancedSettingsFoldout; + private VisualElement viewGenerationContainer; - public override VisualElement CreateInspectorGUI() - { - G3DCamera camera = (G3DCamera)target; + public override VisualElement CreateInspectorGUI() + { + G3DCamera camera = (G3DCamera)target; - // Create a new VisualElement to be the root of our Inspector UI. - VisualElement mainInspector = new VisualElement(); + // Create a new VisualElement to be the root of our Inspector UI. + VisualElement mainInspector = new VisualElement(); - // Add a simple label. - mainInspector.Add(new Label("This is a custom Inspector")); + // Add a simple label. + mainInspector.Add(new Label("This is a custom Inspector")); - // Load the UXML file. - inspectorXML = AssetDatabase.LoadAssetAtPath( - "Packages/com.3dglobal.core/Resources/G3DCameraInspector.uxml" - ); + // Load the UXML file. + inspectorXML = AssetDatabase.LoadAssetAtPath( + "Packages/com.3dglobal.core/Resources/G3DCameraInspector.uxml" + ); - // Instantiate the UXML. - mainInspector = inspectorXML.Instantiate(); + // Instantiate the UXML. + mainInspector = inspectorXML.Instantiate(); - // Find the PropertyField in the Inspector XML. - modeField = mainInspector.Q("mode"); - modeField.RegisterValueChangeCallback( - (evt) => - { - G3DCameraMode newMode = (G3DCameraMode)evt.changedProperty.enumValueIndex; - if (newMode == G3DCameraMode.DIORAMA) + // Find the PropertyField in the Inspector XML. + modeField = mainInspector.Q("mode"); + modeField.RegisterValueChangeCallback( + (evt) => { - calibFolderLabel.style.display = DisplayStyle.Flex; - headtrackingScaleField.style.display = DisplayStyle.Flex; - DioramaCalibFileInfo.style.display = DisplayStyle.Flex; - viewOffsetField.style.display = DisplayStyle.None; + G3DCameraMode newMode = (G3DCameraMode)evt.changedProperty.enumValueIndex; + if (newMode == G3DCameraMode.DIORAMA) + { + calibFolderLabel.style.display = DisplayStyle.Flex; + headtrackingScaleField.style.display = DisplayStyle.Flex; + DioramaCalibFileInfo.style.display = DisplayStyle.Flex; + viewOffsetField.style.display = DisplayStyle.None; + } + else + { + calibFolderLabel.style.display = DisplayStyle.None; + headtrackingScaleField.style.display = DisplayStyle.None; + DioramaCalibFileInfo.style.display = DisplayStyle.None; + viewOffsetField.style.display = DisplayStyle.Flex; + } } - else + ); + + advancedSettingsFoldout = mainInspector.Q("AdvancedSettings"); + advancedSettingsFoldout.value = isAdvancedSettingsVisible; + advancedSettingsFoldout.RegisterValueChangedCallback( + (evt) => { - calibFolderLabel.style.display = DisplayStyle.None; - headtrackingScaleField.style.display = DisplayStyle.None; - DioramaCalibFileInfo.style.display = DisplayStyle.None; - viewOffsetField.style.display = DisplayStyle.Flex; + isAdvancedSettingsVisible = evt.newValue; } - } - ); + ); - advancedSettingsFoldout = mainInspector.Q("AdvancedSettings"); - advancedSettingsFoldout.value = isAdvancedSettingsVisible; - advancedSettingsFoldout.RegisterValueChangedCallback( - (evt) => - { - isAdvancedSettingsVisible = evt.newValue; - } - ); - - headtrackingScaleField = mainInspector.Q("headTrackingScale"); + headtrackingScaleField = mainInspector.Q("headTrackingScale"); - viewOffsetField = mainInspector.Q("viewOffset"); + viewOffsetField = mainInspector.Q("viewOffset"); - string calibrationPath = System.Environment.GetFolderPath( - Environment.SpecialFolder.CommonDocuments - ); - calibrationPath = System.IO.Path.Combine(calibrationPath, "3D Global", "calibrations"); - calibFolderLabel = mainInspector.Q