diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index cc594782019..9bbfac5c8dc 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added +- Added Pipeline for Importance Sampling (Marginals Generation) - Enable by default Cookie for Light Baking - Add warning if disabled and use Baking & Cookies - Ray tracing support for VR single-pass diff --git a/com.unity.render-pipelines.high-definition/Editor/Sky/HDRISky/HDRISkyEditor.cs b/com.unity.render-pipelines.high-definition/Editor/Sky/HDRISky/HDRISkyEditor.cs index 92e452cebe9..26f6213b652 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Sky/HDRISky/HDRISkyEditor.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Sky/HDRISky/HDRISkyEditor.cs @@ -3,6 +3,9 @@ using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.HighDefinition; +using System; +using System.Collections.Generic; + namespace UnityEditor.Rendering.HighDefinition { [CanEditMultipleObjects] @@ -28,8 +31,9 @@ class HDRISkyEditor SerializedDataParameter m_ShadowTint; RTHandle m_IntensityTexture; - Material m_IntegrateHDRISkyMaterial; // Compute the HDRI sky intensity in lux for the skybox Texture2D m_ReadBackTexture; + Material m_CubeToHemiLatLong; + public override bool hasAdvancedMode => true; public override void OnEnable() @@ -63,7 +67,9 @@ public override void OnEnable() m_IntensityTexture = RTHandles.Alloc(1, 1, colorFormat: GraphicsFormat.R32G32B32A32_SFloat); var hdrp = HDRenderPipeline.defaultAsset; if (hdrp != null) - m_IntegrateHDRISkyMaterial = CoreUtils.CreateEngineMaterial(hdrp.renderPipelineResources.shaders.integrateHdriSkyPS); + { + m_CubeToHemiLatLong = CoreUtils.CreateEngineMaterial(hdrp.renderPipelineResources.shaders.cubeToHemiPanoPS); + } m_ReadBackTexture = new Texture2D(1, 1, TextureFormat.RGBAFloat, false, false); } @@ -79,28 +85,50 @@ public override void OnDisable() public void GetUpperHemisphereLuxValue() { Cubemap hdri = m_hdriSky.value.objectReferenceValue as Cubemap; - - // null material can happen when no HDRP asset is present. - if (hdri == null || m_IntegrateHDRISkyMaterial == null) + if (hdri == null || m_CubeToHemiLatLong == null) return; - m_IntegrateHDRISkyMaterial.SetTexture(HDShaderIDs._Cubemap, hdri); - - Graphics.Blit(Texture2D.whiteTexture, m_IntensityTexture.rt, m_IntegrateHDRISkyMaterial); - - // Copy the rendertexture containing the lux value inside a Texture2D - RenderTexture.active = m_IntensityTexture.rt; - m_ReadBackTexture.ReadPixels(new Rect(0.0f, 0.0f, 1, 1), 0, 0); + RTHandle latLongMap = RTHandles.Alloc( 4*hdri.width, hdri.width, + colorFormat: GraphicsFormat.R32G32B32A32_SFloat, + enableRandomWrite: true); + m_CubeToHemiLatLong.SetTexture (HDShaderIDs._SrcCubeTexture, hdri); + m_CubeToHemiLatLong.SetInt (HDShaderIDs._CubeMipLvl, 0); + m_CubeToHemiLatLong.SetInt (HDShaderIDs._CubeArrayIndex, 0); + m_CubeToHemiLatLong.SetInt (HDShaderIDs._BuildPDF, 0); + m_CubeToHemiLatLong.SetInt (HDShaderIDs._PreMultiplyByJacobian, 1); + m_CubeToHemiLatLong.SetInt (HDShaderIDs._PreMultiplyByCosTheta, 1); + m_CubeToHemiLatLong.SetInt (HDShaderIDs._PreMultiplyBySolidAngle, 0); + m_CubeToHemiLatLong.SetVector (HDShaderIDs._Sizes, new Vector4( (float)latLongMap.rt.width, (float)latLongMap.rt.height, + 1.0f/((float)latLongMap.rt.width), 1.0f/((float)latLongMap.rt.height))); + Graphics.Blit(Texture2D.whiteTexture, latLongMap.rt, m_CubeToHemiLatLong, 0); + + RTHandle totalRows = GPUScan.ComputeOperation(latLongMap, null, GPUScan.Operation.Total, GPUScan.Direction.Horizontal, latLongMap.rt.graphicsFormat); + RTHandle totalCols = GPUScan.ComputeOperation(totalRows, null, GPUScan.Operation.Total, GPUScan.Direction.Vertical, latLongMap.rt.graphicsFormat); + + RenderTexture.active = totalCols.rt; + m_ReadBackTexture.ReadPixels(new Rect(0.0f, 0.0f, 1.0f, 1.0f), 0, 0); RenderTexture.active = null; - // And then the value inside this texture Color hdriIntensity = m_ReadBackTexture.GetPixel(0, 0); - m_UpperHemisphereLuxValue.value.floatValue = hdriIntensity.a; - float max = Mathf.Max(hdriIntensity.r, hdriIntensity.g, hdriIntensity.b); - if (max == 0.0f) - max = 1.0f; - m_UpperHemisphereLuxColor.value.vector3Value = new Vector3(hdriIntensity.r/max, hdriIntensity.g/max, hdriIntensity.b/max); - m_UpperHemisphereLuxColor.value.vector3Value *= 0.5f; // Arbitrary 25% to not have too dark or too bright shadow + + // (2.0f*PI)*(0.5f*PI) == (PI^2) + float coef = (Mathf.PI*Mathf.PI) + / // -------------------------------------------------------- + ((float)(latLongMap.rt.width*latLongMap.rt.height)); + float ref3 = (hdriIntensity.r + hdriIntensity.g + hdriIntensity.b)/3.0f; + float maxRef = Mathf.Max(hdriIntensity.r, hdriIntensity.g, hdriIntensity.b); + + m_UpperHemisphereLuxValue.value.floatValue = coef*ref3; + + totalCols.Release(); + totalRows.Release(); + latLongMap.Release(); + + if (maxRef == 0.0f) + maxRef = 1.0f; + + m_UpperHemisphereLuxColor.value.vector3Value = new Vector3(hdriIntensity.r/maxRef, hdriIntensity.g/maxRef, hdriIntensity.b/maxRef); + m_UpperHemisphereLuxColor.value.vector3Value *= 0.5f; // Arbitrary 50% to not have too dark or too bright shadow } public override void OnInspectorGUI() diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Arithmetics.compute b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Arithmetics.compute new file mode 100644 index 00000000000..ac89dcf2d56 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Arithmetics.compute @@ -0,0 +1,99 @@ +#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch + +#pragma kernel CSMainAdd Add KerName=CSMainAdd +#pragma kernel CSMainSub Sub KerName=CSMainSub +#pragma kernel CSMainMult Mult KerName=CSMainMult +#pragma kernel CSMainDiv Div KerName=CSMainDiv +#pragma kernel CSMainMAD MAD KerName=CSMainMAD +#pragma kernel CSMainMAD_RG MAD_RG KerName=CSMainMAD_RG +#pragma kernel CSMainMean Mean KerName=CSMainMean +#pragma kernel CSMainSelfAdd READ_WRITE Add KerName=CSMainSelfAdd +#pragma kernel CSMainSelfSub READ_WRITE Sub KerName=CSMainSelfSub +#pragma kernel CSMainSelfMult READ_WRITE Mult KerName=CSMainSelfMult +#pragma kernel CSMainSelfDiv READ_WRITE Div KerName=CSMainSelfDiv +#pragma kernel CSMainSelfMAD READ_WRITE MAD KerName=CSMainSelfMAD +#pragma kernel CSMainSelfMAD_RG READ_WRITE MAD_RG KerName=CSMainSelfMAD_RG +#pragma kernel CSMainSelfMean READ_WRITE Mean KerName=CSMainSelfMean + +#pragma kernel CSMainAddVal Val Add KerName=CSMainAddVal +#pragma kernel CSMainSubVal Val Sub KerName=CSMainSubVal +#pragma kernel CSMainMultVal Val Mult KerName=CSMainMultVal +#pragma kernel CSMainDivVal Val Div KerName=CSMainDivVal +#pragma kernel CSMainMADVal Val MAD KerName=CSMainMADVal +#pragma kernel CSMainMAD_RGVal Val MAD_RG KerName=CSMainMAD_RGVal +#pragma kernel CSMainMeanVal Val Mean KerName=CSMainMeanVal +#pragma kernel CSMainSelfAddVal Val READ_WRITE Add KerName=CSMainSelfAddVal +#pragma kernel CSMainSelfSubVal Val READ_WRITE Sub KerName=CSMainSelfSubVal +#pragma kernel CSMainSelfMultVal Val READ_WRITE Mult KerName=CSMainSelfMultVal +#pragma kernel CSMainSelfDivVal Val READ_WRITE Div KerName=CSMainSelfDivVal +#pragma kernel CSMainSelfMADVal Val READ_WRITE MAD KerName=CSMainSelfMADVal +#pragma kernel CSMainSelfMAD_RGVal Val READ_WRITE MAD_RG KerName=CSMainSelfMAD_RGVal +#pragma kernel CSMainSelfMeanVal Val READ_WRITE Mean KerName=CSMainSelfMeanVal + +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesFunctions.hlsl" + +#ifdef Val + float4 _InputVal; +#else +Texture2D _InputVal; +#endif + +#ifdef READ_WRITE + RWTexture2D _Output; + #define _Input _Output +#else + Texture2D _Input; + RWTexture2D _Output; +#endif + +uint4 _Sizes; // xy: InputSize; zw: OutputSize + +[numthreads(8, 8, 1)] +void KerName(uint3 id : SV_DispatchThreadID) +{ + if (all(id.xy < _Sizes.xy)) + { + float4 v = _Input[id.xy]; + +#ifdef Val + #if defined(MAD) + float4 param0 = _InputVal.r; + float4 param1 = _InputVal.g; + #elif defined(MAD_RG) + float4 param = float4(_InputVal.r, _InputVal.g, 0.0f, 0.0f); + #else + float4 param = _InputVal; + #endif +#else + #if defined(MAD) + float4 param0 = _InputVal[uint2(0, 0)]; + float4 param1 = _InputVal[uint2(1, 0)]; + #elif defined(MAD_RG) + float4 param = float4(_InputVal[uint2(0, 0)].r, _InputVal[uint2(0, 0)].g, 0.0f, 0.0f); + #else + float4 param = _InputVal[uint2(0, 0)]; + #endif +#endif + +#ifdef Add + _Output[id.xy] = v + param; +#elif defined(Sub) + _Output[id.xy] = v - param; +#elif defined(Mult) + _Output[id.xy] = v*param; +#elif defined(Div) + float4 a = param; + _Output[id.xy] = sign(a)*v/max(abs(a), 1e-4f); +#elif defined(MAD) + _Output[id.xy] = v*param0 + param1; +#elif defined(MAD_RG) + _Output[id.xy] = v*param.r + param.g; +#elif defined(Mean) + float mean = dot(v.xyz, float3((1.0f/3.0f).xxx)); + _Output[id.xy] = mean.xxxx; +#endif + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Arithmetics.compute.meta b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Arithmetics.compute.meta new file mode 100644 index 00000000000..eeb691e3374 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Arithmetics.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1810603f7bf46ba40add30b2ad156037 +ComputeShaderImporter: + externalObjects: {} + currentAPIMask: 262148 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/CubeToHemiPano.shader b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/CubeToHemiPano.shader new file mode 100644 index 00000000000..1cbb1bb6dae --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/CubeToHemiPano.shader @@ -0,0 +1,179 @@ +Shader "Hidden/CubeToHemiPano" { +Properties { + _SrcBlend ("", Float) = 1 + _DstBlend ("", Float) = 1 +} + +HLSLINCLUDE +#pragma editor_sync_compilation +#pragma target 4.5 +#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch + +#include "UnityCG.cginc" + +UNITY_DECLARE_TEXCUBE(_SrcCubeTexture); +UNITY_DECLARE_TEXCUBEARRAY(_SrcCubeTextureArray); + +uniform int _CubeMipLvl; +uniform int _CubeArrayIndex; +uniform int _BuildPDF; +uniform int _PreMultiplyByCosTheta; +uniform int _PreMultiplyBySolidAngle; +uniform int _PreMultiplyByJacobian; // Premultiply by the Det of Jacobian, to be "Integration Ready" +float4 _Sizes; // float4( outSize.xy, 1/outSize.xy ) + +struct v2f +{ + float4 vertex : SV_POSITION; + float2 texcoord : TEXCOORD0; +}; + +v2f vert(float4 vertex : POSITION, float2 texcoord : TEXCOORD0) +{ + v2f o; + o.vertex = UnityObjectToClipPos(vertex); + o.texcoord = texcoord.xy; + return o; +} + +float2 DirectionToSphericalTexCoordinate(float3 dir_in) // use this for the lookup +{ + float3 dir = normalize(dir_in); + // coordinate frame is (-Z,X) meaning negative Z is primary axis and X is secondary axis. + float recipPi = 1.0f/3.1415926535897932384626433832795f; + return float2( 1.0 - 0.5*recipPi*atan2( dir.x, -dir.z ), asin( dir.y )*recipPi + 0.5 ); +} + +float Rescale01(float x, float xmin, float xmax) +{ + return (x - xmin)/(xmax - xmin); +} + +float RescaleNormalized(float x, float newMin, float newMax) +{ + return x*(newMax - newMin) + newMin; +} + +float Rescale(float x, float xmin, float xmax, float newMin, float newMax) +{ + return RescaleNormalized(Rescale01(x, xmin, xmax), newMin, newMax); +} + +float3 SphericalTexCoordinateToDirection(float2 sphTexCoord) +{ + float pi = 3.1415926535897932384626433832795; + float theta = (1.0f - sphTexCoord.x)*(pi*2.0f); + float phi = RescaleNormalized(sphTexCoord.y, 0.0f, pi*0.5f); + + float csTh, siTh, csPh, siPh; + sincos(theta, siTh, csTh); + sincos(phi, siPh, csPh); + + // theta is 0 at negative Z (backwards). Coordinate frame is (-Z,X) meaning negative Z is primary axis and X is secondary axis. + return float3(siTh*csPh, siPh, -csTh*csPh); +} + +float3 GetDir(float2 texCoord) +{ + return SphericalTexCoordinateToDirection(texCoord.xy); +} + +float SampleToPDFMeasure(float3 value) +{ + return (value.r + value.g + value.b)*(1.0f/3.0f); +} + +float SampleToPDFMeasure(float4 value) +{ + return SampleToPDFMeasure(value.rgb); +} + +float GetScale(float angle) +{ + float scale = 1.0f; + float pi = 3.1415926535897932384626433832795f; + + if (_PreMultiplyByJacobian == 1) + { + scale *= sin(angle); // Spherical Jacobian + } + if (_PreMultiplyByCosTheta == 1) + { + scale *= cos(angle); + } + if (_PreMultiplyBySolidAngle == 1) + { + scale *= _Sizes.z*_Sizes.w; + scale *= pi*pi*0.25f; + } + + return scale; +} + +float4 frag(v2f i) : SV_Target +{ + float3 dir = GetDir(i.texcoord.xy); + + float3 output; + if (_BuildPDF == 1) + output = (float3)SampleToPDFMeasure(UNITY_SAMPLE_TEXCUBE_LOD(_SrcCubeTexture, dir, (float)_CubeMipLvl).rgb).xxx; + else + output = (float3)UNITY_SAMPLE_TEXCUBE_LOD(_SrcCubeTexture, dir, (float) _CubeMipLvl).rgb; + + float pi = 3.1415926535897932384626433832795f; + float angle = i.texcoord.y*pi*0.5f; + + output *= GetScale(angle); + + return float4(output.rgb, max(output.r, max(output.g, output.b))); +} + +float4 fragArray(v2f i) : SV_Target +{ + float3 dir = GetDir(i.texcoord.xy); + + float3 output; + if (_BuildPDF == 1) + output = SampleToPDFMeasure(UNITY_SAMPLE_TEXCUBEARRAY_LOD(_SrcCubeTextureArray, float4(dir, _CubeArrayIndex), (float)_CubeMipLvl).rgb).xxx; + else + output = UNITY_SAMPLE_TEXCUBEARRAY_LOD(_SrcCubeTextureArray, float4(dir, _CubeArrayIndex), (float)_CubeMipLvl).rgb; + + float pi = 3.1415926535897932384626433832795f; + float angle = i.texcoord.y*pi*0.5f; + + output *= GetScale(angle); + + return float4(output.rgb, max(output.r, max(output.g, output.b))); +} + +ENDHLSL + +SubShader { + Pass + { + ZWrite Off + ZTest Always + Cull Off + Blend Off + + HLSLPROGRAM + #pragma vertex vert + #pragma fragment frag + ENDHLSL + } + + Pass + { + ZWrite Off + ZTest Always + Cull Off + Blend Off + + HLSLPROGRAM + #pragma vertex vert + #pragma fragment fragArray + ENDHLSL + } +} +Fallback Off +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/CubeToHemiPano.shader.meta b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/CubeToHemiPano.shader.meta new file mode 100644 index 00000000000..6cf5c260632 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/CubeToHemiPano.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a13bb2e88d664074093e45f7827aa3ad +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/CubeToPano.shader b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/CubeToPano.shader index 8aca68776cf..dba62823edf 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/CubeToPano.shader +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/CubeToPano.shader @@ -3,32 +3,27 @@ Properties { _SrcBlend ("", Float) = 1 _DstBlend ("", Float) = 1 } -SubShader { - - - -Pass -{ - ZWrite Off - ZTest Always - Cull Off - Blend Off - - -CGPROGRAM +HLSLINCLUDE +#pragma editor_sync_compilation #pragma target 4.5 -#pragma vertex vert -#pragma fragment frag +#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch #include "UnityCG.cginc" -UNITY_DECLARE_TEXCUBE(_srcCubeTexture); - -uniform int _cubeMipLvl; +UNITY_DECLARE_TEXCUBE(_SrcCubeTexture); +UNITY_DECLARE_TEXCUBEARRAY(_SrcCubeTextureArray); +uniform int _CubeMipLvl; +uniform int _CubeArrayIndex; +uniform bool _BuildPDF; +uniform int _PreMultiplyByCosTheta; +uniform int _PreMultiplyBySolidAngle; +uniform int _PreMultiplyByJacobian; // Premultiply by the Det of Jacobian, to be "Integration Ready" +float4 _Sizes; // float4( outSize.xy, 1/outSize.xy ) -struct v2f { +struct v2f +{ float4 vertex : SV_POSITION; float2 texcoord : TEXCOORD0; }; @@ -41,15 +36,15 @@ v2f vert (float4 vertex : POSITION, float2 texcoord : TEXCOORD0) return o; } -half2 DirectionToSphericalTexCoordinate(half3 dir_in) // use this for the lookup +float2 DirectionToSphericalTexCoordinate(float3 dir_in) // use this for the lookup { - half3 dir = normalize(dir_in); + float3 dir = normalize(dir_in); // coordinate frame is (-Z,X) meaning negative Z is primary axis and X is secondary axis. float recipPi = 1.0/3.1415926535897932384626433832795; - return half2( 1.0-0.5*recipPi*atan2(dir.x, -dir.z), asin(dir.y)*recipPi+0.5 ); + return float2( 1.0-0.5*recipPi*atan2(dir.x, -dir.z), asin(dir.y)*recipPi + 0.5 ); } -half3 SphericalTexCoordinateToDirection(half2 sphTexCoord) +float3 SphericalTexCoordinateToDirection(float2 sphTexCoord) { float pi = 3.1415926535897932384626433832795; float theta = (1-sphTexCoord.x) * (pi*2); @@ -63,18 +58,111 @@ half3 SphericalTexCoordinateToDirection(half2 sphTexCoord) return float3(siTh*csPh, siPh, -csTh*csPh); } -half4 frag (v2f i) : SV_Target +float3 GetDir(float2 texCoord) +{ + return SphericalTexCoordinateToDirection(texCoord.xy); +} + +float SampleToPDFMeasure(float3 value) +{ + return (value.r + value.g + value.b)*(1.0f/3.0f); +} + +float SampleToPDFMeasure(float4 value) { - uint2 pixCoord = ((uint2) i.vertex.xy); + return SampleToPDFMeasure(value.rgb); +} + +float GetScale(float angle) +{ + float scale = 1.0f; + float pi = 3.1415926535897932384626433832795f; + + if (_PreMultiplyByJacobian == 1) + { + scale *= sin(angle); // Spherical Jacobian + } + if (_PreMultiplyByCosTheta == 1) + { + scale *= max(-cos(angle), 0.0f); + } + if (_PreMultiplyBySolidAngle == 1) + { + scale *= _Sizes.z*_Sizes.w; + scale *= pi*pi*0.5f; + } + + return scale; +} + +float4 frag(v2f i) : SV_Target +{ + uint2 pixCoord = (uint2)i.vertex.xy; + float3 dir = GetDir(i.texcoord.xy); + + float3 output; + if (_BuildPDF == 1) + output = SampleToPDFMeasure(UNITY_SAMPLE_TEXCUBE_LOD(_SrcCubeTexture, dir, (float)_CubeMipLvl).rgb).xxx; + else + output = UNITY_SAMPLE_TEXCUBE_LOD(_SrcCubeTexture, dir, (float) _CubeMipLvl).rgb; + + float scale = 1.0f; + float pi = 3.1415926535897932384626433832795f; + float angle = i.texcoord.y*pi; - half3 dir = SphericalTexCoordinateToDirection(i.texcoord.xy); + output *= GetScale(angle); - return (half4) UNITY_SAMPLE_TEXCUBE_LOD(_srcCubeTexture, dir, (float) _cubeMipLvl); + return float4(output.rgb, max(output.r, max(output.g, output.b))); } -ENDCG +float4 fragArray(v2f i) : SV_Target +{ + uint2 pixCoord = (uint2)i.vertex.xy; + float3 dir = GetDir(i.texcoord.xy); + + float3 output; + if (_BuildPDF == 1) + output = SampleToPDFMeasure(UNITY_SAMPLE_TEXCUBEARRAY_LOD(_SrcCubeTextureArray, float4(dir, _CubeArrayIndex), (float)_CubeMipLvl).rgb).xxx; + else + output = UNITY_SAMPLE_TEXCUBEARRAY_LOD(_SrcCubeTextureArray, float4(dir, _CubeArrayIndex), (float)_CubeMipLvl).rgb; + + float scale = 1.0f; + float pi = 3.1415926535897932384626433832795f; + float angle = (1.0f - i.texcoord.y)*pi; + + output *= GetScale(angle); + + return float4(output.rgb, max(output.r, max(output.g, output.b))); } +ENDHLSL + +SubShader { + Pass + { + ZWrite Off + ZTest Always + Cull Off + Blend Off + + HLSLPROGRAM + #pragma vertex vert + #pragma fragment frag + ENDHLSL + } + + Pass + { + ZWrite Off + ZTest Always + Cull Off + Blend Off + + HLSLPROGRAM + #pragma vertex vert + #pragma fragment fragArray + ENDHLSL + } } Fallback Off } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUArithmetic.cs b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUArithmetic.cs new file mode 100644 index 00000000000..39b337fd491 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUArithmetic.cs @@ -0,0 +1,234 @@ + +namespace UnityEngine.Rendering.HighDefinition +{ + class GPUArithmetic + { + /// + /// Allowed operation for GPUArithmetic + /// + public enum Operation + { + /// + /// Addition + /// + Add, + /// + /// Multiply + /// + Mult, + /// + /// Divide + /// + Div, + /// + /// RGB Mean + /// + Mean, + /// + /// MAD: Multiply and Addition (a*x + b) + /// + MAD, + /// + /// MAD_RB: Multiply and Addition (with each needed informations are stored on red & green channels: in.r*x + in.b) + /// + MAD_RG + } + + /// + /// Compute operation + /// + /// Output (Internally supported: +=, *=, /= ...) + /// Input (Internally supported: +=, *=, /= ...) + /// Parameters for add, mult, ... {paramsRT[uint2(0, 0)], paramsRT[uint2(1, 0)]}, or paramsRT[uint2(0, 0)].xy + /// Command Buffer (can be null for immediate context) + /// Supported {Add: output = input + param, Mult: output = input*param, Div: output = input/param, Mean: output = dot(input, float3(1.0f/3.0f).xxx), MAD: output = param[0]*input + param[1], MAD_RG: output = param[0].x*input + param[0].y} + static public void ComputeOperation(RTHandle output, RTHandle input, RTHandle paramsRT, CommandBuffer cmd, Operation operation) + { + Debug.Assert(input != null); + Debug.Assert(output != null); + Debug.Assert(operation == Operation.Mean || paramsRT != null); + Debug.Assert(output.rt.width == input.rt.width && output.rt.height == input.rt.height); + + string addon = ""; + bool self = false; + if (input == output) + { + self = true; + addon += "Self"; + } + + int width = input.rt.width; + int height = input.rt.height; + + var hdrp = HDRenderPipeline.defaultAsset; + ComputeShader arithmeticsCS = hdrp.renderPipelineResources.shaders.gpuArithmeticsCS; + + switch(operation) + { + case Operation.Add: + addon += "Add"; + break; + case Operation.Mult: + addon += "Mult"; + break; + case Operation.Div: + addon += "Div"; + break; + case Operation.Mean: + addon += "Mean"; + break; + case Operation.MAD: + addon += "MAD"; + break; + case Operation.MAD_RG: + addon += "MAD_RG"; + break; + } + + int numTilesX = (width + (8 - 1))/8; + int numTilesY = (height + (8 - 1))/8; + + int kernel = arithmeticsCS.FindKernel("CSMain" + addon); + if (cmd != null) + { + if (self) + { + cmd.SetComputeTextureParam(arithmeticsCS, kernel, HDShaderIDs._Output, input); + if (paramsRT != null) + cmd.SetComputeTextureParam(arithmeticsCS, kernel, HDShaderIDs._InputVal, paramsRT); + cmd.SetComputeIntParams (arithmeticsCS, HDShaderIDs._Sizes, + input.rt.width, input.rt.height, input.rt.width, input.rt.height); + cmd.DispatchCompute(arithmeticsCS, kernel, numTilesX, numTilesY, 1); + } + else + { + cmd.SetComputeTextureParam(arithmeticsCS, kernel, HDShaderIDs._Output, output); + cmd.SetComputeTextureParam(arithmeticsCS, kernel, HDShaderIDs._Input, input); + if (paramsRT != null) + cmd.SetComputeTextureParam(arithmeticsCS, kernel, HDShaderIDs._InputVal, paramsRT); + cmd.SetComputeIntParams (arithmeticsCS, HDShaderIDs._Sizes, + input.rt.width, input.rt.height, input.rt.width, input.rt.height); + cmd.DispatchCompute(arithmeticsCS, kernel, numTilesX, numTilesY, 1); + } + } + else + { + if (self) + { + arithmeticsCS.SetTexture(kernel, HDShaderIDs._Output, input); + if (paramsRT != null) + arithmeticsCS.SetTexture(kernel, HDShaderIDs._InputVal, paramsRT); + arithmeticsCS.SetInts (HDShaderIDs._Sizes, + input.rt.width, input.rt.height, input.rt.width, input.rt.height); + arithmeticsCS.Dispatch (kernel, numTilesX, numTilesY, 1); + } + else + { + arithmeticsCS.SetTexture(kernel, HDShaderIDs._Output, output); + arithmeticsCS.SetTexture(kernel, HDShaderIDs._Input, input); + if (paramsRT != null) + arithmeticsCS.SetTexture(kernel, HDShaderIDs._InputVal, paramsRT); + arithmeticsCS.SetInts (HDShaderIDs._Sizes, + input.rt.width, input.rt.height, input.rt.width, input.rt.height); + arithmeticsCS.Dispatch (kernel, numTilesX, numTilesY, 1); + } + } + } + + /// + /// Compute operation + /// + /// Output (Internally supported: +=, *=, /= ...) + /// Input (Internally supported: +=, *=, /= ...) + /// Parameters for add, mult, ... {paramsRT[uint2(0, 0)], paramsRT[uint2(1, 0)]}, or paramsRT[uint2(0, 0)].xy + /// Command Buffer (can be null for immediate context) + /// Supported {Add: output = input + param, Mult: output = input*param, Div: output = input/param, Mean: output = dot(input, float3(1.0f/3.0f).xxx), MAD: output = param[0]*input + param[1], MAD_RG: output = param[0].x*input + param[0].y} + static public void ComputeOperation(RTHandle output, RTHandle input, Vector4 param, CommandBuffer cmd, Operation operation) + { + Debug.Assert(input != null); + Debug.Assert(output != null); + Debug.Assert(output.rt.width == input.rt.width && output.rt.height == input.rt.height); + + string addon = ""; + bool self = false; + if (input == output) + { + self = true; + addon += "Self"; + } + + int width = input.rt.width; + int height = input.rt.height; + + var hdrp = HDRenderPipeline.defaultAsset; + ComputeShader arithmeticsCS = hdrp.renderPipelineResources.shaders.gpuArithmeticsCS; + + switch(operation) + { + case Operation.Add: + addon += "AddVal"; + break; + case Operation.Mult: + addon += "MultVal"; + break; + case Operation.Div: + addon += "DivVal"; + break; + case Operation.Mean: + addon += "MeanVal"; + break; + case Operation.MAD: + addon += "MADVal"; + break; + case Operation.MAD_RG: + addon += "MAD_RGVal"; + break; + } + + int numTilesX = (width + (8 - 1))/8; + int numTilesY = (height + (8 - 1))/8; + + int kernel = arithmeticsCS.FindKernel("CSMain" + addon); + if (cmd != null) + { + if (self) + { + cmd.SetComputeTextureParam(arithmeticsCS, kernel, HDShaderIDs._Output, input); + cmd.SetComputeVectorParam (arithmeticsCS, HDShaderIDs._InputVal, param); + cmd.SetComputeIntParams (arithmeticsCS, HDShaderIDs._Sizes, + input.rt.width, input.rt.height, input.rt.width, input.rt.height); + cmd.DispatchCompute(arithmeticsCS, kernel, numTilesX, numTilesY, 1); + } + else + { + cmd.SetComputeTextureParam(arithmeticsCS, kernel, HDShaderIDs._Output, output); + cmd.SetComputeTextureParam(arithmeticsCS, kernel, HDShaderIDs._Input, input); + cmd.SetComputeVectorParam (arithmeticsCS, HDShaderIDs._InputVal, param); + cmd.SetComputeIntParams (arithmeticsCS, HDShaderIDs._Sizes, + input.rt.width, input.rt.height, input.rt.width, input.rt.height); + cmd.DispatchCompute(arithmeticsCS, kernel, numTilesX, numTilesY, 1); + } + } + else + { + if (self) + { + arithmeticsCS.SetTexture(kernel, HDShaderIDs._Output, input); + arithmeticsCS.SetVector ( HDShaderIDs._InputVal, param); + arithmeticsCS.SetInts ( HDShaderIDs._Sizes, + input.rt.width, input.rt.height, input.rt.width, input.rt.height); + arithmeticsCS.Dispatch (kernel, numTilesX, numTilesY, 1); + } + else + { + arithmeticsCS.SetTexture(kernel, HDShaderIDs._Output, output); + arithmeticsCS.SetTexture(kernel, HDShaderIDs._Input, input); + arithmeticsCS.SetVector ( HDShaderIDs._InputVal, param); + arithmeticsCS.SetInts ( HDShaderIDs._Sizes, + input.rt.width, input.rt.height, input.rt.width, input.rt.height); + arithmeticsCS.Dispatch (kernel, numTilesX, numTilesY, 1); + } + } + } + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUArithmetic.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUArithmetic.cs.meta new file mode 100644 index 00000000000..b271e92afe1 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUArithmetic.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 40f9a0a3581527b4a83e3312763d2b4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.compute b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.compute new file mode 100644 index 00000000000..742c8ddd6b3 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.compute @@ -0,0 +1,88 @@ +#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch + +// Only cummulative Add +#pragma kernel CSMainFloat1AddH FLOAT1 ADD HORIZONTAL VType=float1 KerName=CSMainFloat1AddH +#pragma kernel CSMainFloat1AddV FLOAT1 ADD VERTICAL VType=float1 KerName=CSMainFloat1AddV +#pragma kernel CSMainFloat4AddH FLOAT4 ADD HORIZONTAL VType=float4 KerName=CSMainFloat4AddH +#pragma kernel CSMainFloat4AddV FLOAT4 ADD VERTICAL VType=float4 KerName=CSMainFloat4AddV + +#pragma kernel CSMainFloat2MinMaxFirstH FLOAT2 MINMAX HORIZONTAL VType=float2 KerName=CSMainFloat2MinMaxFirstH FIRST +#pragma kernel CSMainFloat2MinMaxFirstV FLOAT2 MINMAX VERTICAL VType=float2 KerName=CSMainFloat2MinMaxFirstV FIRST +#pragma kernel CSMainFloat2MinMaxH FLOAT2 MINMAX HORIZONTAL VType=float2 KerName=CSMainFloat2MinMaxH +#pragma kernel CSMainFloat2MinMaxV FLOAT2 MINMAX VERTICAL VType=float2 KerName=CSMainFloat2MinMaxV +// Note0: Can be extented for any cummulative operation: {Min, Max, MinMax, Mult, ...} + +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Sampling.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesFunctions.hlsl" + + Texture2D _Input; +RWTexture2D _Output; + +uint4 _Sizes; // xy: InputSize; zw: OutputSize +uint _Iteration; + +#define _InputSize _Sizes.xy +#define _OutputSize _Sizes.zw + +#ifdef VERTICAL + #define THREAD_PER_GROUP_X 1 + #define THREAD_PER_GROUP_Y 64 +#else // if HORIZONTAL + #define THREAD_PER_GROUP_X 64 + #define THREAD_PER_GROUP_Y 1 +#endif + +VType GetSample(uint2 id) +{ +#ifdef VERTICAL + return _Input[uint2(id.x , id.y - _Iteration)]; +#else + return _Input[uint2(id.x - _Iteration, id.y )]; +#endif +} + +VType Operation(VType a, VType b) +{ +#ifdef ADD + return a + b; +#elif defined(MINMAX) + return float2(min(a.x, b.x), max(a.y, b.y)); +#else + #error GPUScan: Operation not defined +#endif +} + +// :Inclusive Scan +// Ref: Hillis & Steele Parallel Scan Algorithm +[numthreads(THREAD_PER_GROUP_X, THREAD_PER_GROUP_Y, 1)] +void KerName(uint3 id : SV_DispatchThreadID) +{ + if (all(id.xy < _OutputSize)) + { + uint i; +#ifdef HORIZONTAL + uint k = id.x; +#else + uint k = id.y; +#endif + const uint off = uint(_Iteration) + 1; +#ifdef FIRST + VType inVal = _Input[id.xy].xx; + _Output[id.xy] = Operation(inVal.xx, GetSample(id.xy).xx); +#else + VType inVal = _Input[id.xy]; + if (k >= off) + { + _Output[id.xy] = Operation(inVal, GetSample(id.xy)); + } + else + { + _Output[id.xy] = inVal; + } +#endif + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.compute.meta b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.compute.meta new file mode 100644 index 00000000000..b5779957278 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f62a60fadb4701141b17a91bc42f5bbf +ComputeShaderImporter: + externalObjects: {} + currentAPIMask: 262148 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.cs b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.cs new file mode 100644 index 00000000000..da316fd3df3 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.cs @@ -0,0 +1,300 @@ +using UnityEngine.Experimental.Rendering; + +namespace UnityEngine.Rendering.HighDefinition +{ + // For Add (CommulativeAdd) + // Texture2D NxM -> Texture2D NxM + // For Total or MinMax + // Texture1D 1xN -> Texture2D 1x1 + // Texture1D Nx1 -> Texture2D 1x1 + // Texture2D NxM -> Texture2D 1xM + // Texture2D NxM -> Texture2D NxM + class GPUScan + { + /// + /// Allowed direction for GPUScan + /// + public enum Direction + { + /// + /// Vertical (Add: Vertical Inclusive Cumulative Add, Total/MinMax: Last column of a Add/MinMax) + /// + Vertical, + /// + /// Horizontal (Add: Horizontal Inclusive Cumulative Add, Total/MinMax: Last row of a Add/MinMax) + /// + Horizontal + } + + /// + /// Allowed operation for GPUScan + /// + public enum Operation + { + /// + /// Cumulative Add + /// + Add, + /// + /// Total, last element (rows/columns), of a cumulative Add + /// + Total, + /// + /// MinMax, last element (rows/columns), of a cumulative Add + /// Vertical: MinMax of each columns stored on the red (min) and the green (max) channels + /// Horizontal: MinMax of each rows stored on the red (min) and the green (max) channels + /// + MinMax + } + + internal static int m_TileSizes = 64; // Default value + + internal static GraphicsFormat GetFormat(int channelCount, bool isFullPrecision = false) + { + if (isFullPrecision) + { + if (channelCount == 1) + return GraphicsFormat.R32_SFloat; + else if (channelCount == 2) + return GraphicsFormat.R32G32_SFloat; + else if (channelCount == 4) + return GraphicsFormat.R32G32B32A32_SFloat; + else + return GraphicsFormat.None; + } + else + { + if (channelCount == 1) + return GraphicsFormat.R16_SFloat; + else if (channelCount == 2) + return GraphicsFormat.R16G16_SFloat; + else if (channelCount == 4) + return GraphicsFormat.R16G16B16A16_SFloat; + else + return GraphicsFormat.None; + } + } + + /// + /// Compute operation + /// + /// Texture used to performe operation + /// Commande Buffer: null supported for immediate context + /// Operation needed + /// Direction to performe the scan + /// Format used for the output, if not setted, use the same precision as the input + /// A RTHandle which contains the results of the performed scan. The lifetime of the RTHandle is managed by the user (cf. RTHandleDeleter.ScheduleRelease(...), myRTHandle.Release()). + static public RTHandle ComputeOperation(RTHandle input, CommandBuffer cmd, Operation operation, Direction direction, GraphicsFormat outFormat = GraphicsFormat.None) + { + if (input == null) + { + return null; + } + + int width = input.rt.width; + int height = input.rt.height; + + var hdrp = HDRenderPipeline.defaultAsset; + ComputeShader scanCS = hdrp.renderPipelineResources.shaders.gpuScanCS; + + bool isFullPrecision; + if (HDUtils.GetFormatMaxPrecisionBits(input.rt.graphicsFormat) == 32) + { + isFullPrecision = true; + } + else + { + isFullPrecision = false; + } + + GraphicsFormat format2 = GetFormat(2, isFullPrecision); + + GraphicsFormat format; + if (outFormat == GraphicsFormat.None) + { + if (operation == Operation.MinMax) + format = format2; + else + format = input.rt.graphicsFormat; + } + else + { + format = outFormat; + } + + RTHandle temp0 = RTHandles.Alloc(width, height, colorFormat: format, enableRandomWrite: true); + RTHandle temp1 = RTHandles.Alloc(width, height, colorFormat: format, enableRandomWrite: true); + RTHandle cdf = RTHandles.Alloc(width, height, colorFormat: format, enableRandomWrite: true); + RTHandleDeleter.ScheduleRelease(temp0); + RTHandleDeleter.ScheduleRelease(temp1); + + uint iteration; + string addon; + + int channelsCount = HDUtils.GetFormatChannelsCount(format); + if (channelsCount == 1) + { + addon = "1"; + } + else if (channelsCount == 2) + { + addon = "2"; + } + else + { + addon = "4"; + } + + Debug.Assert((operation == Operation.MinMax && (channelsCount == 1 || channelsCount == 2)) || + operation == Operation.Add || operation == Operation.Total); + + string preAddOn = ""; + switch(operation) + { + case Operation.Add: + case Operation.Total: + addon += "Add"; + break; + case Operation.MinMax: + preAddOn = "First"; + addon += "MinMax"; + break; + } + + string dirAddOn; + if (direction == Direction.Vertical) + { + dirAddOn = "V"; + iteration = (uint)Mathf.Log((float)height, 2.0f); + } + else + { + dirAddOn = "H"; + iteration = (uint)Mathf.Log((float)width, 2.0f); + } + + int numTilesX; + int numTilesY; + + int kernel = scanCS.FindKernel("CSMainFloat" + addon + preAddOn + dirAddOn); + if (cmd != null) + { + cmd.SetComputeTextureParam(scanCS, kernel, HDShaderIDs._Input, input); + cmd.SetComputeTextureParam(scanCS, kernel, HDShaderIDs._Output, temp0); + cmd.SetComputeIntParam (scanCS, HDShaderIDs._Iteration, 0); + cmd.SetComputeIntParams (scanCS, HDShaderIDs._Sizes, + input.rt.width, input.rt.height, temp0.rt.width, temp0.rt.height); + } + else + { + scanCS.SetTexture(kernel, HDShaderIDs._Input, input); + scanCS.SetTexture(kernel, HDShaderIDs._Output, temp0); + scanCS.SetInt (HDShaderIDs._Iteration, 0); + scanCS.SetInts (HDShaderIDs._Sizes, + input.rt.width, input.rt.height, temp0.rt.width, temp0.rt.height); + } + if (direction == Direction.Horizontal) + { + numTilesX = (temp0.rt.width + (m_TileSizes - 1))/m_TileSizes; + numTilesY = temp0.rt.height; + } + else + { + numTilesX = temp0.rt.width; + numTilesY = (temp0.rt.height + (m_TileSizes - 1))/m_TileSizes; + } + if (cmd != null) + cmd.DispatchCompute(scanCS, kernel, numTilesX, numTilesY, 1); + else + scanCS.Dispatch(kernel, numTilesX, numTilesY, 1); + + // Loop + kernel = scanCS.FindKernel("CSMainFloat" + addon + dirAddOn); + RTHandle ping = temp0; + RTHandle pong = temp1; + for (uint i = 1; i < iteration; ++i) + { + if (cmd != null) + { + cmd.SetComputeTextureParam(scanCS, kernel, HDShaderIDs._Input, ping); + cmd.SetComputeTextureParam(scanCS, kernel, HDShaderIDs._Output, pong); + cmd.SetComputeIntParam (scanCS, HDShaderIDs._Iteration, (int)Mathf.Pow(2.0f, (float)i)); + cmd.SetComputeIntParams (scanCS, HDShaderIDs._Sizes, + ping.rt.width, input.rt.height, pong.rt.width, pong.rt.height); + } + else + { + scanCS.SetTexture(kernel, HDShaderIDs._Input, ping); + scanCS.SetTexture(kernel, HDShaderIDs._Output, pong); + scanCS.SetInt (HDShaderIDs._Iteration, (int)Mathf.Pow(2.0f, (float)i)); + scanCS.SetInts (HDShaderIDs._Sizes, + ping.rt.width, input.rt.height, pong.rt.width, pong.rt.height); + } + if (direction == Direction.Horizontal) + { + numTilesX = (pong.rt.width + (m_TileSizes - 1))/m_TileSizes; + numTilesY = pong.rt.height; + } + else + { + numTilesX = pong.rt.width; + numTilesY = (pong.rt.height + (m_TileSizes - 1))/m_TileSizes; + } + if (cmd != null) + { + cmd.DispatchCompute(scanCS, kernel, numTilesX, numTilesY, 1); + } + else + { + scanCS.Dispatch(kernel, numTilesX, numTilesY, 1); + } + if (i == iteration - 1) + { + cdf = pong; + } + CoreUtils.Swap(ref ping, ref pong); + } + + if (operation == Operation.Add) + { + return cdf; + } + else if (operation == Operation.Total || operation == Operation.MinMax) + { + RTHandle output; + if (direction == Direction.Horizontal) + { + output = RTHandles.Alloc(1, height, colorFormat: format, enableRandomWrite: true); + RTHandleDeleter.ScheduleRelease(cdf); + if (cmd != null) + { + cmd.CopyTexture(cdf, 0, 0, width - 1, 0, 1, height, output, 0, 0, 0, 0); + } + else + { + Graphics.CopyTexture(cdf, 0, 0, width - 1, 0, 1, height, output, 0, 0, 0, 0); + } + } + else + { + output = RTHandles.Alloc(width, 1, colorFormat: format, enableRandomWrite: true); + RTHandleDeleter.ScheduleRelease(cdf); + if (cmd != null) + { + cmd.CopyTexture(cdf, 0, 0, 0, height - 1, width, 1, output, 0, 0, 0, 0); + } + else + { + Graphics.CopyTexture(cdf, 0, 0, 0, height - 1, width, 1, output, 0, 0, 0, 0); + } + } + + return output; + } + else + { + return null; + } + } + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.cs.meta new file mode 100644 index 00000000000..69b5698f06b --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/GPUScan.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e156ee028bfb767408b50255ab86196d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSample2D.compute b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSample2D.compute new file mode 100644 index 00000000000..773b6b0d753 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSample2D.compute @@ -0,0 +1,46 @@ +#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch + +#pragma kernel CSMainH HORIZONTAL KerName=CSMainH +#pragma kernel CSMainV VERTICAL KerName=CSMainV + +#pragma kernel CSMainHemiH HORIZONTAL HEMI KerName=CSMainHemiH +#pragma kernel CSMainHemiV VERTICAL HEMI KerName=CSMainHemiV + +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Sampling.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Random.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ImportanceSampling2D.hlsl" + + Texture2D _Marginal; + Texture2D _ConditionalMarginal; +RWTexture2D _Output; + +float4 _Sizes; + +#define _SizeInfos _Sizes.xyz +#define _SamplesCount ((uint)_Sizes.w) + +[numthreads(8, 1, 1)] +void KerName(uint3 id : SV_DispatchThreadID) +{ + if (all(id.x < _SamplesCount.x)) + { + float2 xi = Hammersley2dSeq(id.x, _SamplesCount); + + float2 latLongUV; + float3 sampleDir; + +#ifdef HEMI + float2 infos = ImportanceSamplingHemiLatLong(latLongUV, sampleDir, xi, _SizeInfos.xyz, _Marginal, s_linear_clamp_sampler, _ConditionalMarginal, s_linear_clamp_sampler); +#else + float2 infos = ImportanceSamplingLatLong (latLongUV, sampleDir, xi, _SizeInfos.xyz, _Marginal, s_linear_clamp_sampler, _ConditionalMarginal, s_linear_clamp_sampler); +#endif + + latLongUV = saturate(latLongUV); + + _Output[id.xy] = float4(latLongUV.xy, infos.xy); + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSample2D.compute.meta b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSample2D.compute.meta new file mode 100644 index 00000000000..57b13dc43af --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSample2D.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cce4fe192294cb940971970ebe490d82 +ComputeShaderImporter: + externalObjects: {} + currentAPIMask: 262148 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSamplingFromSamples.compute b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSamplingFromSamples.compute new file mode 100644 index 00000000000..ec0cbb3fd76 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSamplingFromSamples.compute @@ -0,0 +1,64 @@ +#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch + +#pragma kernel CSMain + +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Sampling.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + +TEXTURECUBE(_Cubemap); +SAMPLER(sampler_Cubemap); + +Texture2D _Samples; // Samples generated like with ImportanceSample2D.compute + // #Samples x 1 {RG: UV LatLong, B: Density [not normalized PDF]} +RWTexture2D _Output; + +Texture2D _Integral; // Integral to normalized the Density + +float4 _Sizes; // XY: SamplesSize, Z: SamplesCount + +#define OutputSize _Sizes.xy +#define SamplesCount _Sizes.z + +float3 SphericalTexCoordinateToDirection(float2 sphTexCoord) +{ + float pi = 3.1415926535897932384626433832795f; + float theta = (1.0f - sphTexCoord.x)*(2.0f*pi); + float phi = (sphTexCoord.y - 0.5f)*pi; + + float csTh, siTh, csPh, siPh; + sincos(theta, siTh, csTh); + sincos(phi, siPh, csPh); + + // theta is 0 at negative Z (backwards). Coordinate frame is (-Z,X) meaning negative Z is primary axis and X is secondary axis. + return float3(siTh*csPh, siPh, -csTh*csPh); +} + +[numthreads(8, 1, 1)] +void CSMain(uint3 id : SV_DispatchThreadID) +{ + if (all(id.xy < (uint2)OutputSize)) + { + uint2 pos = uint2(id.x, 0); + float integral = _Integral[uint2(0, 0)].r; + float4 input = _Samples[pos]; + float density = input.z; // PDF not normalized + + float2 latLongUV = input.xy; + float3 dir = SphericalTexCoordinateToDirection(latLongUV); + float sin0 = sin(latLongUV.y*PI); + + float3 signal = sin0*SAMPLE_TEXTURECUBE_LOD(_Cubemap, sampler_Cubemap, dir, 0).rgb; + + float pdf = density/integral; + + if (pdf == 0.0f) + signal = 0.0f; + else + signal /= pdf; + + _Output[pos] = float4(signal.rgb, max(signal.r, max(signal.g, signal.b))); + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSamplingFromSamples.compute.meta b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSamplingFromSamples.compute.meta new file mode 100644 index 00000000000..e692ee05d62 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/ImportanceSamplingFromSamples.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ad889ef5ffb61d64bb7fc5a3c4ff0b5e +ComputeShaderImporter: + externalObjects: {} + currentAPIMask: 262148 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/InverseCDF1D.compute b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/InverseCDF1D.compute new file mode 100644 index 00000000000..897dc7dc307 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/InverseCDF1D.compute @@ -0,0 +1,165 @@ +#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch + +#pragma kernel CSMainH HORIZONTAL +#pragma kernel CSMainV VERTICAL + +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Sampling.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesFunctions.hlsl" + + Texture2D _CDF; + Texture2D _PDF; +RWTexture2D _Output; + +uint4 _Sizes; // xy: InputSize; zw: OutputSize + +#define _InputSize _Sizes.xy +#define _OutputSize _Sizes.zw + +float Rescale01(float x, float min, float max) +{ + return x - min + / // ----------- + max - min; +} + +float Rescale(float x, float min, float max, float newMin, float newMax) +{ + return Rescale01(x, min, max)*(newMax - newMin) + newMin; +} + +[numthreads(8, 8, 1)] +#ifdef HORIZONTAL +void CSMainH(uint3 id : SV_DispatchThreadID) +#else +void CSMainV(uint3 id : SV_DispatchThreadID) +#endif +{ + #ifdef HORIZONTAL + if (id.x == 0) + { + float u1 = saturate(_CDF[id.xy].x); + _Output[id.xy] = float4(0.0f, + _PDF[id.xy].x, + _CDF[id.xy].x, + 1.0f); + } + else if (id.x == _OutputSize.x - 1) + { + float u1 = saturate(_CDF[id.xy].x); + _Output[id.xy] = float4(1.0f, + _PDF[id.xy].x, + _CDF[id.xy].x, + 1.0f); + } + #else + if (id.y == 0) + { + float u1 = saturate(_CDF[id.xy].x); + _Output[id.xy] = float4(0.0f, + _PDF[id.xy].x, + _CDF[id.xy].x, + 1.0f); + } + else if (id.y == _OutputSize.y - 1) + { + float u1 = saturate(_CDF[id.xy].x); + _Output[uint2(id.x, _OutputSize.x - 1)] = float4( 1.0f, + _PDF[id.xy].x, + _CDF[id.xy].x, + 1.0f); + } + #endif + else if (all(id.xy < _OutputSize)) + { + float u1 = saturate(_CDF[id.xy].x); + + int i; + + float2 scale = (_OutputSize.xy - float2(1.0f, 1.0f))/_OutputSize.xy; + float2 bias = 0.5f/_OutputSize.xy; + + #ifdef HORIZONTAL + float u0 = saturate(_CDF[uint2(id.x == 0 ? id.x : id.x - 1, id.y)].x); + float dp = 1.0f/_OutputSize.x; + float d = (u1 - u0); + if (d <= dp) + { + _Output[uint2(u1*_InputSize.x, id.y)] = float4( + saturate(float(id.x)*dp), + _PDF[uint2(u1*_InputSize.x, id.y)].x, + _CDF[uint2(u1*_InputSize.x, id.y)].x, + 1.0f); + } + else + { + const int maxIter = ceil(d*_InputSize.x); + + const float y0 = float(id.x - 1)*dp; + const float y1 = float(id.x)*dp; + const float x0 = float(u0*_InputSize.x); + const float x1 = float(u1*_InputSize.x); + + const float a = (y0 - y1) + / // --------- + (x0 - x1); + + const float b = (x0*y1 - x1*y0) + / // --------------- + (x0 - x1); + + for (i = 1; i <= maxIter; ++i) + { + uint xi = uint(u0*_InputSize.x + i); + _Output[uint2(xi, id.y)] = float4( + saturate(a*float(xi) + b), + _PDF[uint2(xi, id.y)].x, + _CDF[uint2(xi, id.y)].x, + 1.0f); + } + } + #else + float u0 = saturate(_CDF[uint2(id.x, id.y == 0 ? id.y : id.y - 1)].x); + float dp = 1.0f/_OutputSize.y; + float d = (u1 - u0); + if (d <= dp) + { + _Output[uint2(id.x, u1*_InputSize.y)] = float4( + saturate(float(id.y)*dp), + _PDF[uint2(id.x, u1*_InputSize.y)].x, + _CDF[uint2(id.x, u1*_InputSize.y)].x, + 1.0f); + } + else + { + const int maxIter = ceil(d*_InputSize.y); + + const float y0 = float(id.y - 1)*dp; + const float y1 = float(id.y)*dp; + const float x0 = float(u0*_InputSize.y); + const float x1 = float(u1*_InputSize.y); + + const float a = (y0 - y1) + / // ---------- + (x0 - x1); + + const float b = (x0*y1 - x1*y0) + / // --------------- + (x0 - x1); + + for (i = 1; i <= maxIter; ++i) + { + uint xi = uint(u0*_InputSize.y + i); + _Output[uint2(id.x, xi)] = float4( + saturate(a*float(xi) + b), + _PDF[uint2(id.x, xi)].x, + _CDF[uint2(xi, id.y)].x, + 1.0f); + } + } + #endif + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/InverseCDF1D.compute.meta b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/InverseCDF1D.compute.meta new file mode 100644 index 00000000000..cbadf3e71ae --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/InverseCDF1D.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ac177633d723a3842b3c95b1b410c7bb +ComputeShaderImporter: + externalObjects: {} + currentAPIMask: 262148 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Rescale01.compute b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Rescale01.compute new file mode 100644 index 00000000000..0587662a11f --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Rescale01.compute @@ -0,0 +1,56 @@ +#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch + +#pragma kernel CSMainH HORIZONTAL KerName=CSMainH +#pragma kernel CSMainV VERTICAL KerName=CSMainV +#pragma kernel CSMainS SINGLE KerName=CSMainS +#pragma kernel CSMainValue VALUE KerName=CSMainValue + +#pragma multi_compile _ READ_WRITE +#pragma multi_compile _ VALUE + +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Sampling.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl" + +#ifdef VALUE + float4 _MinMax; +#else + Texture2D _MinMax; +#endif + +#ifdef READ_WRITE +RWTexture2D _Output; +#define _Input _Output +#else + Texture2D _Input; +RWTexture2D _Output; +#endif + +uint4 _Sizes; // xy: InputSize; zw: OutputSize + +#define _InputSize _Sizes.xy +#define _OutputSize _Sizes.zw + +[numthreads(8, 8, 1)] +void KerName(uint3 id : SV_DispatchThreadID) +{ + if (all(id.xy < _OutputSize)) + { +#ifndef VALUE + #ifdef HORIZONTAL + const float2 minMax = _MinMax[uint2(0, id.y)].xy; + #elif defined(VERTICAL) + const float2 minMax = _MinMax[uint2(id.x, 0)].xy; + #else //if defined(SINGLE) + const float2 minMax = _MinMax[uint2(0, 0)].xy; + #endif +#else + const float2 minMax = _MinMax.xy; +#endif + const float minVal = minMax.x; + const float maxVal = minMax.y; + + _Output[id.xy] = saturate(((_Input[id.xy].x - minVal)/(maxVal - minVal))).xxxx; + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Rescale01.compute.meta b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Rescale01.compute.meta new file mode 100644 index 00000000000..daf887df99d --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/CoreResources/Rescale01.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3f105a3c2868fe44b9b51fc2f9105503 +ComputeShaderImporter: + externalObjects: {} + currentAPIMask: 262148 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/Textures/RTHandlesDeleter.cs b/com.unity.render-pipelines.high-definition/Runtime/Core/Textures/RTHandlesDeleter.cs new file mode 100644 index 00000000000..1ef7f888264 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/Textures/RTHandlesDeleter.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; + +namespace UnityEngine.Rendering.HighDefinition +{ + /// + /// RTHandleDeleter to schedule a release of a RTHandle in N ('lifetime') frame + /// + internal static class RTHandleDeleter + { + internal class RTHandleDesc + { + public int lifetime = 3; + public RTHandle rtHandle = null; + } + + internal static List m_RTHandleDescs = new List(); + + /// + /// Schedule a release of a RTHandle in 'lifetime' frames + /// + /// Considered rtHandle. + /// lifetime remaining of this rtHandle (unit: frame), default: 3 frames. + internal static void ScheduleRelease(RTHandle rtHandle, int lifetime = 3) + { + if (rtHandle != null && lifetime > 0) + { + RTHandleDesc desc = new RTHandleDesc(); + desc.lifetime = lifetime; + desc.rtHandle = rtHandle; + m_RTHandleDescs.Add(desc); + } + else + { + rtHandle?.Release(); + } + } + + /// + /// Schedule a release of a RTHandle in 'lifetime' frames + /// + internal static void Update() + { + foreach (RTHandleDesc desc in m_RTHandleDescs) + { + --desc.lifetime; + } + + // Release 'too old' RTHandle + for (int i = m_RTHandleDescs.Count - 1; i >= 0; i--) + { + var cur = m_RTHandleDescs[i]; + if (cur.lifetime <= 0) + { + cur.rtHandle.Release(); + m_RTHandleDescs.Remove(cur); + } + } + } + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/Textures/RTHandlesDeleter.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/Core/Textures/RTHandlesDeleter.cs.meta new file mode 100644 index 00000000000..e23284df2ce --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/Textures/RTHandlesDeleter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 35791fc361943d240aa627cc4ea9bea3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Core/Textures/TextureCacheCubemap.cs b/com.unity.render-pipelines.high-definition/Runtime/Core/Textures/TextureCacheCubemap.cs index 82c52aec49a..771eb096b7b 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Core/Textures/TextureCacheCubemap.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Core/Textures/TextureCacheCubemap.cs @@ -125,8 +125,8 @@ public bool AllocTextureArray(int numCubeMaps, int width, GraphicsFormat format, if (m_CubeBlitMaterial) { - m_CubeMipLevelPropName = Shader.PropertyToID("_cubeMipLvl"); - m_cubeSrcTexPropName = Shader.PropertyToID("_srcCubeTexture"); + m_CubeMipLevelPropName = HDShaderIDs._CubeMipLvl; + m_cubeSrcTexPropName = HDShaderIDs._SrcCubeTexture; } } else diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/AtmosphericScattering/ShaderVariablesAtmosphericScattering.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Lighting/AtmosphericScattering/ShaderVariablesAtmosphericScattering.hlsl index a7a59afb9b5..b58b2519bf6 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/AtmosphericScattering/ShaderVariablesAtmosphericScattering.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/AtmosphericScattering/ShaderVariablesAtmosphericScattering.hlsl @@ -1,6 +1,10 @@ TEXTURE3D(_VBufferLighting); TEXTURECUBE_ARRAY(_SkyTexture); +TEXTURE2D_ARRAY(_SkyTextureIntegrals); +TEXTURE2D_ARRAY(_SkyTextureMarginals); +TEXTURE2D_ARRAY(_SkyTextureConditionalMarginals); + #define _MipFogNear _MipFogParameters.x #define _MipFogFar _MipFogParameters.y #define _MipFogMaxMip _MipFogParameters.z diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs index 8d369531b2a..8fd8b40469f 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDProfileId.cs @@ -148,6 +148,8 @@ internal enum HDProfileId XRMirrorView, XRCustomMirrorView, XRDepthCopy, + BuildMaginals, + BuildMaginalInternal, // Low res transparency DownsampleDepth, diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs index 2c5046f77ad..8a380c232f6 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs @@ -1772,6 +1772,22 @@ ref _cullingResults using (new ProfilingScope(null, ProfilingSampler.Get(HDProfileId.HDRenderPipelineAllRenderRequest))) { + // Time sliced marginals build + // Ideally once each frame + { + var cmd = CommandBufferPool.Get(""); + using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.BuildMaginals))) + { + // Generate Marginals texture for importance sampling + ImportanceSamplers.Update(cmd); + // Release a RTHandle when their lifetime counter scheduled became 0 + RTHandleDeleter.Update(); + } + renderContext.ExecuteCommandBuffer(cmd); + CommandBufferPool.Release(cmd); + renderContext.Submit(); + } + // Execute render request graph, in reverse order for (int i = renderRequestIndicesToRender.Count - 1; i >= 0; --i) { @@ -2205,7 +2221,6 @@ void Callback(CommandBuffer c, HDCamera cam) } else { - // When debug is enabled we need to clear otherwise we may see non-shadows areas with stale values. if (hdCamera.frameSettings.IsEnabled(FrameSettingsField.ContactShadows) && m_CurrentDebugDisplaySettings.data.fullScreenDebugMode == FullScreenDebugMode.ContactShadows) { diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs index 17cf3ef9900..3934d2b318e 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs @@ -606,6 +606,33 @@ static class HDShaderIDs public static readonly int _InputVelocityMagnitudeHistory = Shader.PropertyToID("_InputVelocityMagnitudeHistory"); public static readonly int _OutputVelocityMagnitudeHistory = Shader.PropertyToID("_OutputVelocityMagnitudeHistory"); + public static readonly int _Input = Shader.PropertyToID("_Input"); + public static readonly int _InputVal = Shader.PropertyToID("_InputVal"); + public static readonly int _Output = Shader.PropertyToID("_Output"); + public static readonly int _Samples = Shader.PropertyToID("_Samples"); + public static readonly int _Sizes = Shader.PropertyToID("_Sizes"); + public static readonly int _Iteration = Shader.PropertyToID("_Iteration"); + public static readonly int _Integral = Shader.PropertyToID("_Integral"); + public static readonly int _MinMax = Shader.PropertyToID("_MinMax"); + public static readonly int _SrcCubeTexture = Shader.PropertyToID("_SrcCubeTexture"); + public static readonly int _SrcCubeTextureArray = Shader.PropertyToID("_SrcCubeTextureArray"); + public static readonly int _CubeMipLvl = Shader.PropertyToID("_CubeMipLvl"); + public static readonly int _CubeArrayIndex = Shader.PropertyToID("_CubeArrayIndex"); + public static readonly int _BuildPDF = Shader.PropertyToID("_BuildPDF"); + public static readonly int _PreMultiplyByJacobian = Shader.PropertyToID("_PreMultiplyByJacobian"); + public static readonly int _PreMultiplyByCosTheta = Shader.PropertyToID("_PreMultiplyByCosTheta"); + public static readonly int _PreMultiplyBySolidAngle = Shader.PropertyToID("_PreMultiplyBySolidAngle"); + + public static readonly int _PDF = Shader.PropertyToID("_PDF"); + public static readonly int _CDF = Shader.PropertyToID("_CDF"); + public static readonly int _Marginal = Shader.PropertyToID("_Marginal"); + public static readonly int _ConditionalMarginal = Shader.PropertyToID("_ConditionalMarginal"); + public static readonly int _SkyTextureImportanceSamplerReady = Shader.PropertyToID("_SkyTextureImportanceSamplerReady"); + public static readonly int _SkyTextureSizeInfos = Shader.PropertyToID("_SkyTextureSizeInfos"); + public static readonly int _SkyTextureIntegrals = Shader.PropertyToID("_SkyTextureIntegrals"); + public static readonly int _SkyTextureMarginals = Shader.PropertyToID("_SkyTextureMarginals"); + public static readonly int _SkyTextureConditionalMarginals = Shader.PropertyToID("_SkyTextureConditionalMarginals"); + public static readonly int _TargetScale = Shader.PropertyToID("_TargetScale"); public static readonly int _Params = Shader.PropertyToID("_Params"); public static readonly int _Params1 = Shader.PropertyToID("_Params1"); diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Reflections/RaytracingReflections.compute b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Reflections/RaytracingReflections.compute index 59819e0cfe9..733ea9702f3 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Reflections/RaytracingReflections.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Reflections/RaytracingReflections.compute @@ -163,7 +163,7 @@ void RaytracingReflectionsFullRes(uint3 dispatchThreadId : SV_DispatchThreadID, // Override the roughness by the clearcoat value of this is a clear coat float4 coatMask = LOAD_TEXTURE2D_X(_SsrClearCoatMaskTexture, currentCoord); normalData.perceptualRoughness = HasClearCoatMask(coatMask) ? CLEAR_COAT_PERCEPTUAL_ROUGHNESS : normalData.perceptualRoughness; - + // Create the local ortho basis float3x3 localToWorld = GetLocalFrame(normalData.normalWS); diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPipelineResources.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPipelineResources.cs index 1adb07ae3df..0566c906855 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPipelineResources.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPipelineResources.cs @@ -159,12 +159,26 @@ public sealed class ShaderResources public ComputeShader encodeBC6HCS; [Reload("Runtime/Core/CoreResources/CubeToPano.shader")] public Shader cubeToPanoPS; + [Reload("Runtime/Core/CoreResources/CubeToHemiPano.shader")] + public Shader cubeToHemiPanoPS; [Reload("Runtime/Core/CoreResources/BlitCubeTextureFace.shader")] public Shader blitCubeTextureFacePS; [Reload("Runtime/Material/LTCAreaLight/FilterAreaLightCookies.shader")] public Shader filterAreaLightCookiesPS; [Reload("Runtime/Core/CoreResources/ClearUIntTextureArray.compute")] public ComputeShader clearUIntTextureCS; + [Reload("Runtime/Core/CoreResources/GPUScan.compute")] + public ComputeShader gpuScanCS; + [Reload("Runtime/Core/CoreResources/Arithmetics.compute")] + public ComputeShader gpuArithmeticsCS; + [Reload("Runtime/Core/CoreResources/Rescale01.compute")] + public ComputeShader rescale01CS; + [Reload("Runtime/Core/CoreResources/ImportanceSample2D.compute")] + public ComputeShader importanceSample2DCS; + [Reload("Runtime/Core/CoreResources/ImportanceSamplingFromSamples.compute")] + public ComputeShader ImportanceSamplingFromSamplesCS; + [Reload("Runtime/Core/CoreResources/InverseCDF1D.compute")] + public ComputeShader inverseCDF1DCS; // XR [Reload("Runtime/ShaderLibrary/XRMirrorView.shader")] diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/HDUtils.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/HDUtils.cs index f09c5257339..def006811eb 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/HDUtils.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/HDUtils.cs @@ -948,6 +948,24 @@ internal static HDAdditionalCameraData TryGetAdditionalCameraDataOrDefault(Camer {GraphicsFormat.RGB_BC6H_SFloat, 1}, // BC6H uses 128 bits for each 4x4 tile which is 8 bits per pixel }; + static Dictionary graphicsFormatChannelsCount = new Dictionary + { + // Init some default format so we don't allocate more memory on the first frame. + {GraphicsFormat.R16G16B16A16_SFloat, 4}, + {GraphicsFormat.R32G32B32A32_SFloat, 4}, + {GraphicsFormat.R16_SFloat, 1}, + {GraphicsFormat.R32_SFloat, 1}, + }; + + static Dictionary graphicsFormatMaxPrecisionBits = new Dictionary + { + // Init some default format so we don't allocate more memory on the first frame. + {GraphicsFormat.R16G16B16A16_SFloat, 16}, + {GraphicsFormat.R32G32B32A32_SFloat, 32}, + {GraphicsFormat.R16_SFloat, 16}, + {GraphicsFormat.R32_SFloat, 32}, + }; + /// /// Compute the size in bytes of a GraphicsFormat. Does not works with compressed formats. /// @@ -973,6 +991,66 @@ internal static int GetFormatSizeInBytes(GraphicsFormat format) return size; } + /// + /// Compute the number of channel of a GraphicsFormat. Does not works with compressed formats. + /// + /// + /// Channels Count + internal static int GetFormatChannelsCount(GraphicsFormat format) + { + if (graphicsFormatChannelsCount.TryGetValue(format, out var channelsCount)) + return channelsCount; + + // Compute the size by parsing the enum name: Note that it does not works with compressed formats + string name = format.ToString(); + int underscoreIndex = name.IndexOf('_'); + name = name.Substring(0, underscoreIndex == -1 ? name.Length : underscoreIndex); + + channelsCount = 0; + foreach (char c in name) + { + if (c == 'R') + ++channelsCount; + else if (c == 'G') + ++channelsCount; + else if (c == 'B') + ++channelsCount; + else if (c == 'A') + ++channelsCount; + } + + graphicsFormatChannelsCount[format] = channelsCount; + + return channelsCount; + } + + /// + /// Compute the max precision (in bits) of a given format. + /// + /// + /// Max precision in bits + internal static int GetFormatMaxPrecisionBits(GraphicsFormat format) + { + if (graphicsFormatMaxPrecisionBits.TryGetValue(format, out var maxBitsPrecision)) + return maxBitsPrecision; + + // Compute the size by parsing the enum name: Note that it does not works with compressed formats + maxBitsPrecision = 0; + string name = format.ToString(); + for (int k = 32; k >= 0; k--) + { + if (name.IndexOf(k.ToString()) != -1) + { + maxBitsPrecision = k; + break; + } + } + + graphicsFormatMaxPrecisionBits[format] = maxBitsPrecision; + + return maxBitsPrecision; + } + internal static void DisplayUnsupportedMessage(string msg) { Debug.LogError(msg); diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSampler2D.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSampler2D.cs new file mode 100644 index 00000000000..3e2708f12c2 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSampler2D.cs @@ -0,0 +1,190 @@ +using UnityEngine.Experimental.Rendering; + +namespace UnityEngine.Rendering.HighDefinition +{ + static class ImportanceSampler2D + { + internal static GraphicsFormat GetFormat(uint channelCount, bool isFullPrecision = false) + { + if (isFullPrecision) + { + if (channelCount == 1) + return GraphicsFormat.R32_SFloat; + else if (channelCount == 2) + return GraphicsFormat.R32G32_SFloat; + else if (channelCount == 4) + return GraphicsFormat.R32G32B32A32_SFloat; + else + return GraphicsFormat.None; + } + else + { + if (channelCount == 1) + return GraphicsFormat.R16_SFloat; + else if (channelCount == 2) + return GraphicsFormat.R16G16_SFloat; + else if (channelCount == 4) + return GraphicsFormat.R16G16B16A16_SFloat; + else + return GraphicsFormat.None; + } + } + + /// + /// Build Mariginal textures for 2D texture 'density' + /// + /// A Columns marginal texture + /// Full resolution marginal texture + /// Density, not normalized PDF, must be single channel + /// Command Buffer + public static void GenerateMarginals( + out RTHandle marginal, out RTHandle conditionalMarginal, + RTHandle density, + CommandBuffer cmd) + { + int width = density.rt.width; + int height = density.rt.height; + + Debug.Assert(HDUtils.GetFormatChannelsCount(density.rt.graphicsFormat) == 1); + + bool isFullPrecision = HDUtils.GetFormatMaxPrecisionBits(density.rt.graphicsFormat) == 32; + + GraphicsFormat format1 = GetFormat(1, isFullPrecision); + GraphicsFormat format2 = GetFormat(2, isFullPrecision); + GraphicsFormat format4 = GetFormat(4, isFullPrecision); + + // 1. CDF of Rows (density where each rows is a CDF) + RTHandle cdfFull = GPUScan.ComputeOperation(density, cmd, GPUScan.Operation.Add, GPUScan.Direction.Horizontal, format1); + RTHandleDeleter.ScheduleRelease(cdfFull); + // 2. CDF L (L: CDF of {Sum of Rows}) + RTHandle sumRows = RTHandles.Alloc(1, height, colorFormat: format1, enableRandomWrite: true); + RTHandleDeleter.ScheduleRelease(sumRows); + // Last columns contains the data we want + cmd.CopyTexture(cdfFull, 0, 0, width - 1, 0, 1, height, sumRows, 0, 0, 0, 0); + // Pre-Normalize to avoid overflow + RTHandle minMaxSumRows = GPUScan.ComputeOperation(sumRows, cmd, GPUScan.Operation.MinMax, GPUScan.Direction.Vertical, format2); + RTHandleDeleter.ScheduleRelease(minMaxSumRows); + Rescale01(sumRows, minMaxSumRows, GPUScan.Direction.Vertical, cmd, true); + RTHandle cdfL = GPUScan.ComputeOperation(sumRows, cmd, GPUScan.Operation.Add, GPUScan.Direction.Vertical); + RTHandleDeleter.ScheduleRelease(cdfL); + // 3. Min Max of each Rows + RTHandle minMaxRows = GPUScan.ComputeOperation(cdfFull, cmd, GPUScan.Operation.MinMax, GPUScan.Direction.Horizontal); + RTHandleDeleter.ScheduleRelease(minMaxRows); + // 4. Min Max of L + RTHandle minMaxCDFL = GPUScan.ComputeOperation(cdfL, cmd, GPUScan.Operation.MinMax, GPUScan.Direction.Vertical); + RTHandleDeleter.ScheduleRelease(minMaxCDFL); + // 5. Rescale CDF Rows (to 01) + Rescale01(cdfFull, minMaxRows, GPUScan.Direction.Horizontal, cmd); + // 6. Rescale CDF L (to 01) + Rescale01(cdfL, minMaxCDFL, GPUScan.Direction.Vertical, cmd, true); + // 7. Inv CDF Rows + conditionalMarginal = InverseCDF1D.ComputeInverseCDF(cdfFull, density, cmd, InverseCDF1D.SumDirection.Horizontal, format4); + // 8. Inv CDF L + marginal = InverseCDF1D.ComputeInverseCDF(cdfL, density, cmd, InverseCDF1D.SumDirection.Vertical, format1); + } + + /// + /// Helper to Rescale a RenderTarget between 0 & 1, with a Given MinMax + /// + /// Render Targer Handle + /// Renter Target Handle which contains the MinMax + /// Direction: {Vertical, Horizontal} + /// Constant Buffer + /// Single, true if the minMax is a 1x1 texture + private static void Rescale01(RTHandle tex, RTHandle minMax, GPUScan.Direction direction, CommandBuffer cmd, bool single = false) + { + var hdrp = HDRenderPipeline.defaultAsset; + ComputeShader rescale01 = hdrp.renderPipelineResources.shaders.rescale01CS; + + rescale01.EnableKeyword("READ_WRITE"); + string addon0 = ""; + if (single) + { + addon0 += "S"; + } + else if (direction == GPUScan.Direction.Horizontal) + { + addon0 += "H"; + } + else // if (direction == GPUOperation.Direction.Vertical) + { + addon0 += "V"; + } + + int kernel = rescale01.FindKernel("CSMain" + addon0); + + cmd.SetComputeTextureParam(rescale01, kernel, HDShaderIDs._Output, tex); + cmd.SetComputeTextureParam(rescale01, kernel, HDShaderIDs._MinMax, minMax); + cmd.SetComputeIntParams (rescale01, HDShaderIDs._Sizes, + tex.rt.width, tex.rt.height, tex.rt.width, tex.rt.height); + + int numTilesX = (tex.rt.width + (8 - 1))/8; + int numTilesY = (tex.rt.height + (8 - 1))/8; + + cmd.DispatchCompute(rescale01, kernel, numTilesX, numTilesY, 1); + } + + /// + /// Helper to generate Importance Sampled samples (RG: UV on Equirectangular Map, G: PDF, B: CDF) + /// + /// Samples Count + /// Marginal from ImportanceSamplersSystem + /// Conditional Marginal from ImportanceSamplersSystem + /// Direction: {Vertical, Horizontal} + /// Command Buffer + /// true if the Marginal was generated for Hemisphere + /// + public static RTHandle GenerateSamples(uint samplesCount, RTHandle marginal, RTHandle conditionalMarginal, GPUScan.Direction direction, CommandBuffer cmd, bool hemiSphere = false) + { + var hdrp = HDRenderPipeline.defaultAsset; + ComputeShader importanceSample2D = hdrp.renderPipelineResources.shaders.importanceSample2DCS; + + string addon = ""; + if (hemiSphere) + addon += "Hemi"; + + if (direction == GPUScan.Direction.Horizontal) + { + addon += "H"; + } + else + { + addon += "V"; + } + + RTHandle samples = RTHandles.Alloc((int)samplesCount, 1, colorFormat: GraphicsFormat.R32G32B32A32_SFloat, enableRandomWrite: true); + + int kernel = importanceSample2D.FindKernel("CSMain" + addon); + + int numTilesX = (samples.rt.width + (8 - 1))/8; + if (cmd != null) + { + cmd.SetComputeTextureParam(importanceSample2D, kernel, HDShaderIDs._Marginal, marginal); + cmd.SetComputeTextureParam(importanceSample2D, kernel, HDShaderIDs._ConditionalMarginal, conditionalMarginal); + cmd.SetComputeTextureParam(importanceSample2D, kernel, HDShaderIDs._Output, samples); + if (direction == GPUScan.Direction.Horizontal) + cmd.SetComputeFloatParams(importanceSample2D, HDShaderIDs._Sizes, + conditionalMarginal.rt.height, 1.0f/conditionalMarginal.rt.height, 0.5f/conditionalMarginal.rt.height, samplesCount); + else + cmd.SetComputeFloatParams(importanceSample2D, HDShaderIDs._Sizes, + conditionalMarginal.rt.width, 1.0f/conditionalMarginal.rt.width, 0.5f/conditionalMarginal.rt.width, samplesCount); + cmd.DispatchCompute(importanceSample2D, kernel, numTilesX, 1, 1); + } + else + { + importanceSample2D.SetTexture(kernel, HDShaderIDs._Marginal, marginal); + importanceSample2D.SetTexture(kernel, HDShaderIDs._ConditionalMarginal, conditionalMarginal); + importanceSample2D.SetTexture(kernel, HDShaderIDs._Output, samples); + if (direction == GPUScan.Direction.Horizontal) + importanceSample2D.SetFloats(HDShaderIDs._Sizes, + conditionalMarginal.rt.height, 1.0f/conditionalMarginal.rt.height, 0.5f/conditionalMarginal.rt.height, samplesCount); + else + importanceSample2D.SetFloats(HDShaderIDs._Sizes, + conditionalMarginal.rt.width, 1.0f/conditionalMarginal.rt.width, 0.5f/conditionalMarginal.rt.width, samplesCount); + importanceSample2D.Dispatch(kernel, numTilesX, 1, 1); + } + + return samples; + } + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSampler2D.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSampler2D.cs.meta new file mode 100644 index 00000000000..df61dbd5916 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSampler2D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ffa8b5650025154a82814909b875f79 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplers.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplers.cs new file mode 100644 index 00000000000..b04ad471d58 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplers.cs @@ -0,0 +1,99 @@ +using UnityEngine.Experimental.Rendering; + +namespace UnityEngine.Rendering.HighDefinition +{ + /// + /// Default instance of a ImportanceSamplers + /// + public static class ImportanceSamplers + { + static ImportanceSamplersSystem s_DefaultInstance = new ImportanceSamplersSystem(); + + /// + /// Check if an Importance Sampling exist (generated or schedule for generation). + /// + /// Unique ID to identify the marginals. + public static bool Exist(int identifier) + { + return s_DefaultInstance.Exist(identifier); + } + + /// + /// Check if an Importance Sampling exist & ready (generated or schedule for generation). + /// + /// Unique ID to identify the marginals. + public static bool ExistAndReady(int identifier) + { + return s_DefaultInstance.ExistAndReady(identifier); + } + + /// + /// Getter for marginal textures. + /// + /// Unique ID to identify the marginals. + public static ImportanceSamplersSystem.MarginalTextures GetMarginals(int identifier) + { + return s_DefaultInstance.GetMarginals(identifier); + } + + /// + /// Schedule generation of the marginal textures. return if the task was scheduled + /// + /// Unique ID to identify this scheduling. + /// Texture2D or CubeMap which used for the the generation of the important sampling. + /// if the pdfTexture is a Cubemap or a CubemapArray, buildHemisphere allow to enforce to build the marginals only for the Upper Hemisphere. + public static bool ScheduleMarginalGeneration(int identifier, Texture pdfTexture, bool buildHemisphere = false) + { + return s_DefaultInstance.ScheduleMarginalGeneration(identifier, pdfTexture, buildHemisphere); + } + + /// + /// Schedule generation of the marginal textures. Even if the identifier already exist (always return true) + /// + /// Unique ID to identify this scheduling. + /// Texture2D or CubeMap which used for the the generation of the important sampling. + /// if the pdfTexture is a Cubemap or a CubemapArray, buildHemisphere allow to enforce to build the marginals only for the Upper Hemisphere. + public static bool ScheduleMarginalGenerationForce(int identifier, Texture pdfTexture, bool buildHemisphere = false) + { + return s_DefaultInstance.ScheduleMarginalGenerationForce(identifier, pdfTexture, buildHemisphere); + } + + /// + /// Schedule a release of Marginal Textures + /// + /// Unique ID to identify this Release. + public static bool ScheduleRelease(int identifier) + { + return s_DefaultInstance.InternalScheduleRelease(identifier); + } + + /// + /// Update the logics, done once per frame + /// + /// Command buffer provided to setup shader constants. + public static void Update(CommandBuffer cmd) + { + s_DefaultInstance.Update(cmd); + } + + /// + /// Get convenient ID to identify a texture in the Importance Sampling + /// + /// Texture which we want to have an ID from. + /// Used if texture is a Cubemap. + public static int GetIdentifier(Texture texture, bool buildHemisphere = false) + { + if (texture == null) + return -1; + + int hash = 23*texture.GetHashCode(); + hash = 23*hash + texture.updateCount.GetHashCode(); + + if (texture.dimension == TextureDimension.Cube || + texture.dimension == TextureDimension.CubeArray) + hash = 23*hash + buildHemisphere.GetHashCode(); + + return hash; + } + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplers.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplers.cs.meta new file mode 100644 index 00000000000..9666f09d16c --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3eab5168403d58143a224186d1a8c4ab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplersSystem.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplersSystem.cs new file mode 100644 index 00000000000..f47bb179ba8 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplersSystem.cs @@ -0,0 +1,511 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine.Experimental.Rendering; + +namespace UnityEngine.Rendering.HighDefinition +{ + /// + /// System managing a set of RTHandle textures for + /// + public partial class ImportanceSamplersSystem : IDisposable + { + /// + /// Information needed to process Importance Sampling + /// If the input used was Cubemap of CubemapArray the Marginals information was for Equirectangular + /// The user have to convert LatLong (IR^2) to Direction (IR^3) + /// HLSL: LatlongToDirectionCoordinate(uv) + /// + public class MarginalTextures + { + /// + /// Marginal texture of the sum of columns (1 x Height) {R: InvCDF, G:PDF, B:CDF} + /// PDF on the green channel, is only premultiplied with Jacobian (multiplied by Cosine if hemisphere) + /// + public RTHandle marginal = null; + /// + /// Conditional Marginal texture of each rows (Width x Height) {R: InvCDF, G:PDF, B:CDF} + /// PDF on the green channel, is only premultiplied with Jacobian (multiplied by Cosine if hemisphere) + /// + public RTHandle conditionalMarginal = null; + /// + /// Integral without the Solid angle, if hemisphere: premultiplied with the Jacobian cosined weighted, otherwise only premultiplied with the Jacobian + /// + public RTHandle integral = null; + } + + /// + /// Marginal informations set of informations needed for the time sliced generation of Marginals + /// + internal class MarginalInfos + { + /// + /// Input texture scheduled for Importance Sampling Generator + /// Supported Dimension: {Texture2D, Texture2DArray, Cubemap, CubemapArray}, with or without Mips + /// If the input is a Cubemap/CubemapArray internally the Marginal will be generated for Equirectangular + /// (or HemiEquirectangular if buildHemisphere is true) projection + /// + public Texture input = null; + /// + /// Informations needed to perform Importance Sampling + /// + public MarginalTextures marginals = null; + /// + /// Current slice progressed (information used only if inProgress is true) + /// + public int currentSlice = 0; + /// + /// Current mip progressed (information used only if inProgress is true) + /// + public int currentMip = 0; + /// + /// Current age of Marginal Generation scheduled (schedule generation when age is 0) + /// + public int age = -8; + /// + /// Indicator if the Marginals are available for binding + /// + public bool isReady = false; + /// + /// Indicator if the Marginals are Work in Progress (Process time sliced) + /// + public bool inProgress = false; + /// + /// true to build hemisphere cosine weighted hemisphere + /// + public bool buildHemisphere = false; + } + + internal static List m_Keys = new List(4); + internal static Dictionary m_InternalData = new Dictionary(4); + + /// + /// RTHandleSystem constructor. + /// + public ImportanceSamplersSystem() + { + } + + /// + /// Disposable pattern implementation. + /// + public void Dispose() + { + // TODO + } + + /// + /// Check if an Importance Sampling exist (generated or schedule for generation). + /// + /// Unique ID to identify the marginals. + public bool Exist(int identifier) + { + return m_InternalData.ContainsKey(identifier); + } + + /// + /// Check if an Importance Sampling exist & ready (generated or schedule for generation). + /// + /// Unique ID to identify the marginals. + public bool ExistAndReady(int identifier) + { + return Exist(identifier) && m_InternalData[identifier].isReady; + } + + /// + /// Getter for marginal textures. + /// + /// Unique ID to identify the marginals. + public MarginalTextures GetMarginals(int identifier) + { + if (Exist(identifier) == false) + { + return null; + } + + if (m_InternalData[identifier].isReady) + { + return m_InternalData[identifier].marginals; + } + else + { + return null; + } + } + + /// + /// Schedule generation of the marginal textures. return if the task was scheduled + /// + /// Unique ID to identify this scheduling. + /// Texture2D or CubeMap which used for the the generation of the important sampling. + /// if the pdfTexture is a Cubemap or a CubemapArray, buildHemisphere allow to enforce to build the marginals only for the Upper Hemisphere. + public bool ScheduleMarginalGeneration(int identifier, Texture pdfTexture, bool buildHemisphere = false) + { + if (Exist(identifier) == false) + { + return InternalScheduleMarginalGeneration(identifier, pdfTexture, buildHemisphere); + } + else + { + return false; + } + } + + /// + /// Schedule generation of the marginal textures. Even if the identifier already exist (always return true) + /// + /// Unique ID to identify this scheduling. + /// Texture2D or CubeMap which used for the the generation of the important sampling. + /// if the pdfTexture is a Cubemap or a CubemapArray, buildHemisphere allow to enforce to build the marginals only for the Upper Hemisphere. + public bool ScheduleMarginalGenerationForce(int identifier, Texture pdfTexture, bool buildHemisphere = false) + { + InternalScheduleRelease(identifier); + + return InternalScheduleMarginalGeneration(identifier, pdfTexture, buildHemisphere); + } + + /// + /// Schedule generation of the marginal textures. Even if the identifier already exist (always return true) + /// Internal use only + /// + /// Unique ID to identify this scheduling. + /// Texture2D or CubeMap which used for the the generation of the important sampling. + internal bool InternalScheduleMarginalGeneration(int identifier, Texture pdfTexture, bool buildHemisphere = false) + { + MarginalInfos toGenerate = new MarginalInfos(); + toGenerate.marginals = new MarginalTextures(); + toGenerate.input = pdfTexture; + toGenerate.marginals.marginal = null; + toGenerate.marginals.conditionalMarginal = null; + toGenerate.marginals.integral = null; + toGenerate.currentSlice = 0; + toGenerate.currentMip = 0; + toGenerate.age = -4; + toGenerate.isReady = false; + toGenerate.inProgress = false; + if (pdfTexture.dimension == TextureDimension.Tex2D || pdfTexture.dimension == TextureDimension.Tex2DArray) + toGenerate.buildHemisphere = false; + else + toGenerate.buildHemisphere = buildHemisphere; + m_InternalData.Add(identifier, toGenerate); + + return true; + } + + /// + /// Schedule a release of Marginal Textures + /// + /// Unique ID to identify this Release. + public bool ScheduleRelease(int identifier) + { + return InternalScheduleRelease(identifier); + } + + /// + /// Schedule a release of Marginal Textures, Internal use only + /// + /// Unique ID to identify this Release. + internal bool InternalScheduleRelease(int identifier) + { + if (Exist(identifier)) + { + MarginalInfos current = m_InternalData[identifier]; + RTHandleDeleter.ScheduleRelease(current.marginals.marginal); + RTHandleDeleter.ScheduleRelease(current.marginals.conditionalMarginal); + RTHandleDeleter.ScheduleRelease(current.marginals.integral); + + m_InternalData.Remove(identifier); + + return true; + } + else + { + return false; + } + } + + /// + /// Update the logics, done once per frame + /// + /// Command buffer provided to setup shader constants. + public void Update(CommandBuffer cmd) + { + m_Keys.Clear(); + m_Keys.InsertRange(0, m_InternalData.Keys); + foreach (var key in m_Keys) + { + if (m_InternalData[key].input == null) + { + m_InternalData.Remove(key); + } + } + + foreach (var cur in m_InternalData) + { + ++cur.Value.age; + if (cur.Value.isReady == false || cur.Value.inProgress) + { + // Do only one per frame + // One slice & one mip per frame + if (cur.Value.age >= 0) + { + GenerateMarginals(cur, cmd); + } + break; + } + } + } + + + /// + /// Get the format used for internal process, Internal use only + /// + /// Supported {1, 2, 4}. + /// isFullPrecision == true => float, half otherwise. + internal static GraphicsFormat GetFormat(int channelCount, bool isFullPrecision = false) + { + if (isFullPrecision) + { + if (channelCount == 1) + return GraphicsFormat.R32_SFloat; + else if (channelCount == 2) + return GraphicsFormat.R32G32_SFloat; + else if (channelCount == 4) + return GraphicsFormat.R32G32B32A32_SFloat; + else + return GraphicsFormat.None; + } + else + { + if (channelCount == 1) + return GraphicsFormat.R16_SFloat; + else if (channelCount == 2) + return GraphicsFormat.R16G16_SFloat; + else if (channelCount == 4) + return GraphicsFormat.R16G16B16A16_SFloat; + else + return GraphicsFormat.None; + } + } + + /// + /// Effective generation + /// + /// Informations needed to generate the marginal textures. + internal void GenerateMarginals(KeyValuePair current, CommandBuffer cmd) + { + MarginalInfos value = current.Value; + + bool hasMip = value.input.mipmapCount > 1; + bool isFullPrecision; + //if (HDUtils.GetFormatMaxPrecisionBits(current.Value.input.graphicsFormat) == 32) + // Full precision Mandatory + { + isFullPrecision = true; + } + //else + //{ + // isFullPrecision = false; + //} + + GraphicsFormat format1 = GetFormat(1, isFullPrecision); + GraphicsFormat format4 = GetFormat(4, isFullPrecision); + + int width = -1; + int height = -1; + int slicesCount = 1; + + bool buildMarginalArray = false; + + if (value.input.dimension == TextureDimension.Tex2D || + value.input.dimension == TextureDimension.Tex2DArray) + { + width = value.input.width; + height = value.input.height; + + if (value.input.dimension == TextureDimension.Tex2DArray) + { + slicesCount = (value.input as Texture2DArray).depth; + buildMarginalArray = true; + } + } + else if (value.input.dimension == TextureDimension.Cube || + value.input.dimension == TextureDimension.CubeArray) + { + // Latlong/equirectangular + // TODO: Octahedral (Compute: Jacobian), Octahedral_ConstArea vs Octahedral_Isotropic + width = 4*value.input.width; + if (value.buildHemisphere) + height = value.input.width; + else + height = 2*value.input.width; + + if (value.input.dimension == TextureDimension.CubeArray) + { + slicesCount = (value.input as CubemapArray).cubemapCount; + buildMarginalArray = true; + } + } + else + { + Debug.LogError("ImportanceSamplerSystem: Marginal texture generator only avaiable for Texture2D{Array?} or Cubemap{Array?}."); + } + + RTHandle rtInput = RTHandles.Alloc(value.input); + RTHandleDeleter.ScheduleRelease(rtInput); + + RTHandle marginal = null; + RTHandle conditionalMarginal = null; + RTHandle integral = null; + + using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.BuildMaginalInternal))) + { + // Compute one slice & one mip per frame, we allocate the marginals once + if (value.marginals.marginal == null) + { + value.marginals.marginal = + RTHandles.Alloc(1, height, slices: slicesCount, + dimension: buildMarginalArray ? TextureDimension.Tex2DArray : TextureDimension.Tex2D, + colorFormat: format1, enableRandomWrite: true, useMipMap: hasMip, autoGenerateMips: false); + } + if (value.marginals.conditionalMarginal == null) + { + value.marginals.conditionalMarginal = + RTHandles.Alloc(width, height, slices: slicesCount, + dimension: buildMarginalArray ? TextureDimension.Tex2DArray : TextureDimension.Tex2D, + colorFormat: format4, enableRandomWrite: true, useMipMap: hasMip, autoGenerateMips: false); + } + if (value.marginals.integral == null) + { + value.marginals.integral = + RTHandles.Alloc(1, 1, slices: slicesCount, + dimension: buildMarginalArray ? TextureDimension.Tex2DArray : TextureDimension.Tex2D, + colorFormat: format1, enableRandomWrite: true, useMipMap: false, autoGenerateMips: false); + } + + if (value.input.dimension == TextureDimension.Tex2D || + value.input.dimension == TextureDimension.Tex2DArray) + { + int curWidth = Mathf.RoundToInt((float)width /Mathf.Pow(2.0f, (float)value.currentMip)); + int curHeight = Mathf.RoundToInt((float)height/Mathf.Pow(2.0f, (float)value.currentMip)); + + // Begin: Integrate to have the proper PDF + if (current.Value.currentMip == 0) + { + RTHandle rowTotal = GPUScan.ComputeOperation(rtInput, cmd, GPUScan.Operation.Total, GPUScan.Direction.Horizontal, rtInput.rt.graphicsFormat); + integral = GPUScan.ComputeOperation(rowTotal, cmd, GPUScan.Operation.Total, GPUScan.Direction.Vertical, rtInput.rt.graphicsFormat); + RTHandleDeleter.ScheduleRelease(rowTotal); + RTHandleDeleter.ScheduleRelease(integral); + } + // End + RTHandle texCopy = RTHandles.Alloc( curWidth, curHeight, + colorFormat: format1, + enableRandomWrite: true, useMipMap: false, autoGenerateMips: false); + RTHandleDeleter.ScheduleRelease(texCopy); + GPUArithmetic.ComputeOperation(texCopy, rtInput, null, cmd, GPUArithmetic.Operation.Mean); + + ImportanceSampler2D.GenerateMarginals(out marginal, out conditionalMarginal, texCopy, cmd); + } + else if (value.input.dimension == TextureDimension.Cube || + value.input.dimension == TextureDimension.CubeArray) + { + int curWidth = Mathf.RoundToInt((float)width /Mathf.Pow(2.0f, (float)value.currentMip)); + int curHeight = Mathf.RoundToInt((float)height/Mathf.Pow(2.0f, (float)value.currentMip)); + + RTHandle latLongMap = RTHandles.Alloc( curWidth, curHeight, + colorFormat: format4, + enableRandomWrite: true); + RTHandleDeleter.ScheduleRelease(latLongMap); + + var hdrp = HDRenderPipeline.defaultAsset; + Material usedMat; + if (value.buildHemisphere) + usedMat = CoreUtils.CreateEngineMaterial(hdrp.renderPipelineResources.shaders.cubeToHemiPanoPS); + else + usedMat = CoreUtils.CreateEngineMaterial(hdrp.renderPipelineResources.shaders.cubeToPanoPS); + if (value.input.dimension == TextureDimension.Cube) + { + usedMat.SetTexture(HDShaderIDs._SrcCubeTexture, rtInput); + } + else + { + usedMat.SetTexture(HDShaderIDs._SrcCubeTextureArray, rtInput); + } + usedMat.SetInt (HDShaderIDs._CubeMipLvl, current.Value.currentMip); + usedMat.SetInt (HDShaderIDs._CubeArrayIndex, current.Value.currentSlice); + usedMat.SetInt (HDShaderIDs._BuildPDF, 0); + usedMat.SetInt (HDShaderIDs._PreMultiplyBySolidAngle, 0); + usedMat.SetInt (HDShaderIDs._PreMultiplyByCosTheta, 0); + usedMat.SetInt (HDShaderIDs._PreMultiplyByJacobian, 1); + usedMat.SetVector (HDShaderIDs._Sizes, new Vector4( (float)latLongMap.rt.width, (float)latLongMap.rt.height, + 1.0f/((float)latLongMap.rt.width), 1.0f/((float)latLongMap.rt.height))); + + cmd.Blit(Texture2D.whiteTexture, latLongMap, usedMat, value.input.dimension == TextureDimension.Cube ? 0 : 1); + + RTHandle latLongMap1 = RTHandles.Alloc( curWidth, curHeight, + colorFormat: format1, + enableRandomWrite: true); + GPUArithmetic.ComputeOperation(latLongMap1, latLongMap, null, cmd, GPUArithmetic.Operation.Mean); + + // Begin: Integrate to have the proper PDF + if (current.Value.currentMip == 0) + { + RTHandle totalRows = GPUScan.ComputeOperation(latLongMap1, cmd, GPUScan.Operation.Total, GPUScan.Direction.Horizontal, format1); + integral = GPUScan.ComputeOperation(totalRows, cmd, GPUScan.Operation.Total, GPUScan.Direction.Vertical, format1); + RTHandleDeleter.ScheduleRelease(totalRows); + RTHandleDeleter.ScheduleRelease(integral); + + float coef = Mathf.PI*Mathf.PI/((float)(latLongMap1.rt.width*latLongMap1.rt.height)); + if (current.Value.buildHemisphere) + coef *= 1.0f; + else + coef *= 2.0f; + + GPUArithmetic.ComputeOperation(integral, integral, new Vector4(coef, coef, coef, coef), cmd, GPUArithmetic.Operation.Mult); + + } + // End: Integrate Equirectangular Map + + ImportanceSampler2D.GenerateMarginals(out marginal, out conditionalMarginal, latLongMap1, cmd); + } + else + { + Debug.LogError("ImportanceSamplersSystem.GenerateMarginals, try to generate marginal texture for a non valid dimension (supported Tex2D, Tex2DArray, Cubemap, CubemapArray)."); + } + } + + cmd.CopyTexture(marginal, 0, 0, value.marginals.marginal, current.Value.currentSlice, current.Value.currentMip); + cmd.CopyTexture(conditionalMarginal, 0, 0, value.marginals.conditionalMarginal, current.Value.currentSlice, current.Value.currentMip); + if (current.Value.currentMip == 0) + cmd.CopyTexture(integral, 0, 0, value.marginals.integral, current.Value.currentSlice, 0); + + if (current.Value.currentMip + 1 == value.input.mipmapCount) + { + if (current.Value.currentSlice + 1 == slicesCount) + { + current.Value.inProgress = false; + current.Value.isReady = true; + current.Value.currentMip = 0; + current.Value.currentSlice = 0; + } + else + { + current.Value.inProgress = true; + current.Value.isReady = false; + } + } + else + { + current.Value.inProgress = true; + current.Value.isReady = false; + } + + current.Value.currentMip++; + if (current.Value.currentMip == value.input.mipmapCount) + { + current.Value.currentSlice++; + current.Value.currentMip = 0; + } + } + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplersSystem.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplersSystem.cs.meta new file mode 100644 index 00000000000..6fdc274768c --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/ImportanceSamplersSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f99d44d9c82a3994f9e631cd7e2e4cd1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/InverseCDF1D.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/InverseCDF1D.cs new file mode 100644 index 00000000000..4669d2b6d2a --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/InverseCDF1D.cs @@ -0,0 +1,60 @@ +using UnityEngine.Experimental.Rendering; +using UnityEngine.Rendering.HighDefinition; + +namespace UnityEngine.Rendering +{ + // CDF: Cumulative Distribution Function + class InverseCDF1D + { + public enum SumDirection + { + Vertical, + Horizontal + } + + static public RTHandle ComputeInverseCDF(RTHandle cdf, RTHandle fullPDF, CommandBuffer cmd, SumDirection direction, GraphicsFormat sumFormat = GraphicsFormat.None) + { + if (cdf == null) + { + return null; + } + + var hdrp = HDRenderPipeline.defaultAsset; + ComputeShader invCDFCS = hdrp.renderPipelineResources.shaders.inverseCDF1DCS; + + GraphicsFormat format; + if (sumFormat == GraphicsFormat.None) + format = GraphicsFormat.R16G16B16A16_SFloat; + else + format = sumFormat; + + int width = cdf.rt.width; + int height = cdf.rt.height; + + RTHandle invCDF = RTHandles.Alloc(width, height, colorFormat: format, enableRandomWrite: true); + + string addon; + if (direction == SumDirection.Vertical) + { + addon = "V"; + } + else + { + addon = "H"; + } + + // RGB to Greyscale + int kernel = invCDFCS.FindKernel("CSMain" + addon); + cmd.SetComputeTextureParam(invCDFCS, kernel, HDShaderIDs._CDF, cdf); + cmd.SetComputeTextureParam(invCDFCS, kernel, HDShaderIDs._Output, invCDF); + cmd.SetComputeTextureParam(invCDFCS, kernel, HDShaderIDs._PDF, fullPDF); + cmd.SetComputeIntParams (invCDFCS, HDShaderIDs._Sizes, + cdf.rt.width, cdf.rt.height, invCDF.rt.width, invCDF.rt.height); + int numTilesX = (invCDF.rt.width + (8 - 1))/8; + int numTilesY = (invCDF.rt.height + (8 - 1))/8; + cmd.DispatchCompute(invCDFCS, kernel, numTilesX, numTilesY, 1); + + return invCDF; + } + } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/InverseCDF1D.cs.meta b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/InverseCDF1D.cs.meta new file mode 100644 index 00000000000..a2c465c286a --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Utility/InverseCDF1D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 117054d91a9155b48ae1fc5ea9276535 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ImportanceSampling2D.hlsl b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ImportanceSampling2D.hlsl new file mode 100644 index 00000000000..0d7355caae0 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ImportanceSampling2D.hlsl @@ -0,0 +1,95 @@ +#ifndef UNITY_IMPORTANCE_SAMPLING_2D +#define UNITY_IMPORTANCE_SAMPLING_2D + +float2 FixPDFInfo(float2 info) +{ + float pdf = info.x; + float cdf = info.y; + + //if (pdf < 1e-6f) + // pdf = 0.0f; + + return float2(pdf, cdf); +} + +#ifdef HORIZONTAL +void GetImportanceSampledUV(out float2 uv, float2 xi, float3 pixHeightsInfos, TEXTURE2D_PARAM(marginalRow, used_samplerMarg), TEXTURE2D_PARAM(conditionalMarginal, used_samplerCondMarg)) +{ + uv.y = saturate(SAMPLE_TEXTURE2D_LOD(marginalRow, used_samplerMarg, float2(0.0f, xi.x), 0).x); + // pixInfos: x: Height, y: 1.0f/Height, z: 0.5f/Height + uv.y = floor(uv.y*pixHeightsInfos.x)*pixHeightsInfos.y + pixHeightsInfos.z; + uv.x = saturate(SAMPLE_TEXTURE2D_LOD(conditionalMarginal, used_samplerCondMarg, float2(xi.y, uv.y), 0).x); +} + +void GetImportanceSampledUVArray(out float2 uv, float2 xi, float3 pixHeightsInfos, TEXTURE2D_ARRAY_PARAM(marginalRow, used_samplerMarg), TEXTURE2D_ARRAY_PARAM(conditionalMarginal, used_samplerCondMarg)) +{ + uv.y = saturate(SAMPLE_TEXTURE2D_ARRAY_LOD(marginalRow, used_samplerMarg, float2(0.0f, xi.x), 0, 0).x); + // pixInfos: x: Height, y: 1.0f/Height, z: 0.5f/Height + uv.y = floor(uv.y*pixHeightsInfos.x)*pixHeightsInfos.y + pixHeightsInfos.z; + uv.x = saturate(SAMPLE_TEXTURE2D_ARRAY_LOD(conditionalMarginal, used_samplerCondMarg, float2(xi.y, uv.y), 0, 0).x); +} +#elif defined(VERTICAL) +void GetImportanceSampledUV(out float2 uv, float2 xi, float3 pixHeightsInfos, TEXTURE2D_PARAM(marginalRow, used_samplerMarg), TEXTURE2D_PARAM(conditionalMarginal, used_samplerCondMarg)) +{ + uv.x = saturate(SAMPLE_TEXTURE2D_LOD(marginalRow, used_samplerMarg, float2(xi.x, 0.0f), 0).x); + // pixInfos: x: Width, y: 1.0f/Width, z: 0.5f/Width + uv.x = floor(uv.x*pixHeightsInfos.x)*pixHeightsInfos.y + pixHeightsInfos.z; + uv.y = saturate(SAMPLE_TEXTURE2D_LOD(conditionalMarginal, used_samplerCondMarg, float2(uv.x, xi.y), 0).x); +} + +void GetImportanceSampledUVArray(out float2 uv, float2 xi, float3 pixHeightsInfos, TEXTURE2D_ARRAY_PARAM(marginalRow, used_samplerMarg), TEXTURE2D_ARRAY_PARAM(conditionalMarginal, used_samplerCondMarg)) +{ + uv.x = saturate(SAMPLE_TEXTURE2D_ARRAY_LOD(marginalRow, used_samplerMarg, float2(xi.x, 0.0f), 0, 0).x); + // pixInfos: x: Width, y: 1.0f/Width, z: 0.5f/Width + uv.x = floor(uv.x*pixHeightsInfos.x)*pixHeightsInfos.y + pixHeightsInfos.z; + uv.y = saturate(SAMPLE_TEXTURE2D_ARRAY_LOD(conditionalMarginal, used_samplerCondMarg, float2(uv.x, xi.y), 0, 0).x); +} +#else +#error ImportanceSampling2D.hlsl must define if HORIZONTAL or VERTICAL +#endif + +float2 ImportanceSamplingHemiLatLong(out float2 uv, out float3 w, float2 xi, float3 pixHeightsInfos, TEXTURE2D_PARAM(marginalRow, used_samplerMarg), TEXTURE2D_PARAM(conditionalMarginal, used_samplerCondMarg)) +{ + GetImportanceSampledUV(uv, xi, pixHeightsInfos, marginalRow, used_samplerMarg, conditionalMarginal, used_samplerCondMarg); + // The pdf (without jacobian) stored on the y channel + float2 info = SAMPLE_TEXTURE2D_LOD(conditionalMarginal, used_samplerCondMarg, uv, 0).yz; + + w = normalize(LatlongToDirectionCoordinate(saturate(uv*float2(1.0f, 0.5f)))); + + return FixPDFInfo(info); +} + +float2 ImportanceSamplingHemiLatLongArray(out float2 uv, out float3 w, float2 xi, float3 pixHeightsInfos, TEXTURE2D_ARRAY_PARAM(marginalRow, used_samplerMarg), TEXTURE2D_ARRAY_PARAM(conditionalMarginal, used_samplerCondMarg)) +{ + GetImportanceSampledUVArray(uv, xi, pixHeightsInfos, marginalRow, used_samplerMarg, conditionalMarginal, used_samplerCondMarg); + // The pdf (without jacobian) stored on the y channel + float2 info = SAMPLE_TEXTURE2D_ARRAY_LOD(conditionalMarginal, used_samplerCondMarg, uv, 0, 0).yz; + + w = normalize(LatlongToDirectionCoordinate(saturate(uv*float2(1.0f, 0.5f)))); + + return FixPDFInfo(info); +} + +float2 ImportanceSamplingLatLong(out float2 uv, out float3 w, float2 xi, float3 pixHeightsInfos, TEXTURE2D_PARAM(marginalRow, used_samplerMarg), TEXTURE2D_PARAM(conditionalMarginal, used_samplerCondMarg)) +{ + GetImportanceSampledUV(uv, xi, pixHeightsInfos, marginalRow, used_samplerMarg, conditionalMarginal, used_samplerCondMarg); + // The pdf (without jacobian) stored on the y channel + float2 info = SAMPLE_TEXTURE2D_LOD(conditionalMarginal, used_samplerCondMarg, uv, 0).yz; + + w = normalize(LatlongToDirectionCoordinate(saturate(uv))); + + return FixPDFInfo(info); +} + +float2 ImportanceSamplingLatLongArray(out float2 uv, out float3 w, float2 xi, float3 pixHeightsInfos, TEXTURE2D_ARRAY_PARAM(marginalRow, used_samplerMarg), TEXTURE2D_ARRAY_PARAM(conditionalMarginal, used_samplerCondMarg)) +{ + GetImportanceSampledUVArray(uv, xi, pixHeightsInfos, marginalRow, used_samplerMarg, conditionalMarginal, used_samplerCondMarg); + // The pdf (without jacobian) stored on the y channel + float2 info = SAMPLE_TEXTURE2D_ARRAY_LOD(conditionalMarginal, used_samplerCondMarg, uv, 0, 0).yz; + + w = normalize(LatlongToDirectionCoordinate(saturate(uv))); + + return FixPDFInfo(info); +} + +#endif // UNITY_IMPORTANCE_SAMPLING_2D diff --git a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ImportanceSampling2D.hlsl.meta b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ImportanceSampling2D.hlsl.meta new file mode 100644 index 00000000000..d0d2c6e262b --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ImportanceSampling2D.hlsl.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: afb149079f1c2aa47be9db7420587668 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesFunctions.hlsl b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesFunctions.hlsl index 98293352ddc..1e28b7985d1 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesFunctions.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesFunctions.hlsl @@ -148,4 +148,14 @@ float3 TransformPreviousObjectToWorld(float3 positionOS) return mul(previousModelMatrix, float4(positionOS, 1.0)).xyz; } +float SampleToPDFMeasure(float3 value) +{ + return (value.r + value.g + value.b)*(1.0f/3.0f); +} + +float SampleToPDFMeasure(float4 value) +{ + return SampleToPDFMeasure(value.rgb); +} + #endif // UNITY_SHADER_VARIABLES_FUNCTIONS_INCLUDED diff --git a/com.unity.render-pipelines.high-definition/Runtime/Sky/HDRISky/HDRISkyRenderer.cs b/com.unity.render-pipelines.high-definition/Runtime/Sky/HDRISky/HDRISkyRenderer.cs index e0a24492356..77351811392 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Sky/HDRISky/HDRISkyRenderer.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Sky/HDRISky/HDRISkyRenderer.cs @@ -111,6 +111,7 @@ public override void PreRenderSky(BuiltinSkyParameters builtinParams, bool rende public override void RenderSky(BuiltinSkyParameters builtinParams, bool renderForCubemap, bool renderSunDisk) { var hdriSky = builtinParams.skySettings as HDRISky; + float intensity, phi, backplatePhi; GetParameters(out intensity, out phi, out backplatePhi, builtinParams, hdriSky); int passID; @@ -129,24 +130,24 @@ public override void RenderSky(BuiltinSkyParameters builtinParams, bool renderFo passID = m_RenderFullscreenSkyWithBackplateID; } - m_SkyHDRIMaterial.SetTexture(HDShaderIDs._Cubemap, hdriSky.hdriSky.value); - m_SkyHDRIMaterial.SetVector(HDShaderIDs._SkyParam, new Vector4(intensity, 0.0f, Mathf.Cos(phi), Mathf.Sin(phi))); - m_SkyHDRIMaterial.SetVector(HDShaderIDs._BackplateParameters0, GetBackplateParameters0(hdriSky)); - m_SkyHDRIMaterial.SetVector(HDShaderIDs._BackplateParameters1, GetBackplateParameters1(backplatePhi, hdriSky)); - m_SkyHDRIMaterial.SetVector(HDShaderIDs._BackplateParameters2, GetBackplateParameters2(hdriSky)); - m_SkyHDRIMaterial.SetColor(HDShaderIDs._BackplateShadowTint, hdriSky.shadowTint.value); - uint shadowFilter = 0u; - if (hdriSky.pointLightShadow.value) - shadowFilter |= unchecked((uint)LightFeatureFlags.Punctual); - if (hdriSky.dirLightShadow.value) - shadowFilter |= unchecked((uint)LightFeatureFlags.Directional); - if (hdriSky.rectLightShadow.value) - shadowFilter |= unchecked((uint)LightFeatureFlags.Area); - m_SkyHDRIMaterial.SetInt(HDShaderIDs._BackplateShadowFilter, unchecked((int)shadowFilter)); - - // This matrix needs to be updated at the draw call frequency. - m_PropertyBlock.SetMatrix(HDShaderIDs._PixelCoordToViewDirWS, builtinParams.pixelCoordToViewDirMatrix); - CoreUtils.DrawFullScreen(builtinParams.commandBuffer, m_SkyHDRIMaterial, m_PropertyBlock, passID); - } + m_SkyHDRIMaterial.SetTexture(HDShaderIDs._Cubemap, hdriSky.hdriSky.value); + m_SkyHDRIMaterial.SetVector(HDShaderIDs._SkyParam, new Vector4(intensity, 0.0f, Mathf.Cos(phi), Mathf.Sin(phi))); + m_SkyHDRIMaterial.SetVector(HDShaderIDs._BackplateParameters0, GetBackplateParameters0(hdriSky)); + m_SkyHDRIMaterial.SetVector(HDShaderIDs._BackplateParameters1, GetBackplateParameters1(backplatePhi, hdriSky)); + m_SkyHDRIMaterial.SetVector(HDShaderIDs._BackplateParameters2, GetBackplateParameters2(hdriSky)); + m_SkyHDRIMaterial.SetColor(HDShaderIDs._BackplateShadowTint, hdriSky.shadowTint.value); + uint shadowFilter = 0u; + if (hdriSky.pointLightShadow.value) + shadowFilter |= unchecked((uint)LightFeatureFlags.Punctual); + if (hdriSky.dirLightShadow.value) + shadowFilter |= unchecked((uint)LightFeatureFlags.Directional); + if (hdriSky.rectLightShadow.value) + shadowFilter |= unchecked((uint)LightFeatureFlags.Area); + m_SkyHDRIMaterial.SetInt(HDShaderIDs._BackplateShadowFilter, unchecked((int)shadowFilter)); + + // This matrix needs to be updated at the draw call frequency. + m_PropertyBlock.SetMatrix(HDShaderIDs._PixelCoordToViewDirWS, builtinParams.pixelCoordToViewDirMatrix); + CoreUtils.DrawFullScreen(builtinParams.commandBuffer, m_SkyHDRIMaterial, m_PropertyBlock, passID); } } +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Sky/HDRISky/IntegrateHDRISky.shader b/com.unity.render-pipelines.high-definition/Runtime/Sky/HDRISky/IntegrateHDRISky.shader index 2b2ac0af321..7166a8f576c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Sky/HDRISky/IntegrateHDRISky.shader +++ b/com.unity.render-pipelines.high-definition/Runtime/Sky/HDRISky/IntegrateHDRISky.shader @@ -1,88 +1,136 @@ Shader "Hidden/HDRP/IntegrateHDRI" { - Properties + HLSLINCLUDE + + #pragma vertex Vert + + #pragma editor_sync_compilation + #pragma target 4.5 + #pragma only_renderers d3d11 ps4 xboxone 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.core/ShaderLibrary/ImageBasedLighting.hlsl" + #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" + + struct Attributes + { + uint vertexID : SV_VertexID; + }; + + struct Varyings { - [HideInInspector] - _Cubemap ("", CUBE) = "white" {} + float4 positionCS : SV_POSITION; + float2 texCoord : TEXCOORD0; + }; + + TextureCube _Cubemap; + + Varyings Vert(Attributes input) + { + Varyings output; + + output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID); + output.texCoord = GetFullScreenTriangleTexCoord(input.vertexID); + + return output; } - SubShader + // With HDRI that have a large range (including the sun) it can be challenging to + // compute the lux value without multiple importance sampling. + // We instead use a brute force Uniforme Spherical integration of the upper hemisphere + // with a large number of sample. This is fine as this happen in the editor. + float3 GetUpperHemisphereLuxValue(TEXTURECUBE_PARAM(skybox, sampler_skybox), float3 N) { - Tags{ "RenderPipeline" = "HDRenderPipeline" } - Pass + float3 sum = 0.0f; + //const float dphi = 2.0f*PI/1024.0f; + //const float dtheta = 0.5f*PI/256.0f; + const float dphi = 0.0005f; + const float dtheta = 0.0005f; + const float coef = dphi*dtheta; + for (float phi = 0.0f; phi < 2.0f*PI; phi += dphi) { - ZTest Always Cull Off ZWrite Off - - HLSLPROGRAM - #pragma editor_sync_compilation - #pragma vertex Vert - #pragma fragment Frag - #pragma target 4.5 - #pragma only_renderers d3d11 playstation xboxone 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.core/ShaderLibrary/ImageBasedLighting.hlsl" - #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" - - struct Attributes + for (float theta = 0.0f; theta < 0.5f*PI; theta += dtheta) { - uint vertexID : SV_VertexID; - }; + // SphericalToCartesian function is for Z up, lets move to Y up with TransformGLtoDX + float3 L = TransformGLtoDX(SphericalToCartesian(phi, cos(theta))); + float3 val = SAMPLE_TEXTURECUBE_LOD(skybox, sampler_skybox, L, 0).rgb; + sum += (cos(theta)*sin(theta)*coef)*val; + } + } + + return sum; + } - struct Varyings + float3 GetSphereLuxValue(TEXTURECUBE_PARAM(skybox, sampler_skybox), float3 N) + { + float3 sum = 0.0f; + //const float dphi = 2.0f*PI/1024.0f; + //const float dtheta = PI/512.0f; + const float dphi = 0.00075f; + const float dtheta = 0.00075f; + const float coef = dphi*dtheta; + for (float phi = 0.0f; phi < 2.0f*PI; phi += dphi) + { + for (float theta = 0.0f; theta < PI; theta += dtheta) { - float4 positionCS : SV_POSITION; - float2 texCoord : TEXCOORD0; - }; + // SphericalToCartesian function is for Z up, lets move to Y up with TransformGLtoDX + float3 L = TransformGLtoDX(SphericalToCartesian(phi, cos(theta))); + float3 val = SAMPLE_TEXTURECUBE_LOD(skybox, sampler_skybox, L, 0).rgb; + sum += (sin(theta)*coef)*val; + } + } - TextureCube _Cubemap; + return sum; + } - Varyings Vert(Attributes input) - { - Varyings output; + float4 Frag(Varyings input) : SV_Target + { + // Integrate upper hemisphere (Y up) + float3 N = float3(0.0, 1.0, 0.0); - output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID); - output.texCoord = GetFullScreenTriangleTexCoord(input.vertexID); + float3 intensity = GetUpperHemisphereLuxValue(TEXTURECUBE_ARGS(_Cubemap, s_point_clamp_sampler), N); - return output; - } - + return float4(intensity.rgb, max(intensity.r, max(intensity.g, intensity.b))); + } - // With HDRI that have a large range (including the sun) it can be challenging to - // compute the lux value without multiple importance sampling. - // We instead use a brute force Uniforme Spherical integration of the upper hemisphere - // with a large number of sample. This is fine as this happen in the editor. - real3 GetUpperHemisphereLuxValue(TEXTURECUBE_PARAM(skybox, sampler_skybox), real3 N) - { - float3 sum = 0.0; - const float dphi = 0.005; - const float dtheta = 0.005; - const float coef = dphi*dtheta; - for (float phi = 0; phi < 2.0 * PI; phi += dphi) - { - for (float theta = 0; theta < PI / 2.0; theta += dtheta) - { - // SphericalToCartesian function is for Z up, lets move to Y up with TransformGLtoDX - float3 L = TransformGLtoDX(SphericalToCartesian(phi, cos(theta))); - real3 val = SAMPLE_TEXTURECUBE_LOD(skybox, sampler_skybox, L, 0).rgb; - sum += (cos(theta)*sin(theta)*coef)*val; - } - } - - return sum; - } + float4 FragSphere(Varyings input) : SV_Target + { + // Integrate upper hemisphere (Y up) + float3 N = float3(0.0, 1.0, 0.0); - float4 Frag(Varyings input) : SV_Target - { - // Integrate upper hemisphere (Y up) - float3 N = float3(0.0, 1.0, 0.0); + float3 intensity = GetSphereLuxValue(TEXTURECUBE_ARGS(_Cubemap, s_point_clamp_sampler), N); - float3 intensity = GetUpperHemisphereLuxValue(TEXTURECUBE_ARGS(_Cubemap, s_trilinear_clamp_sampler), N); + return float4(intensity.rgb, max(intensity.r, max(intensity.g, intensity.b))); + } - return float4(intensity.rgb, Luminance(intensity)); - } + ENDHLSL + SubShader + { + // Hemisphere Integral + Pass + { + ZWrite Off + ZTest Always + Blend Off + Cull Off + + HLSLPROGRAM + #pragma fragment Frag + ENDHLSL + } + + // Sphere Integral + Pass + { + ZWrite Off + ZTest Always + Blend Off + Cull Off + + HLSLPROGRAM + #pragma fragment FragSphere ENDHLSL } } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Sky/SkyManager.cs b/com.unity.render-pipelines.high-definition/Runtime/Sky/SkyManager.cs index de6b404b536..343937dc551 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Sky/SkyManager.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Sky/SkyManager.cs @@ -381,7 +381,7 @@ Texture GetSkyCubemap(SkyUpdateContext skyContext) Texture GetReflectionTexture(SkyUpdateContext skyContext) { if (skyContext.IsValid() && IsCachedContextValid(skyContext)) - { + { ref var context = ref m_CachedSkyContexts[skyContext.cachedSkyRenderingContextId]; return context.renderingContext.skyboxBSDFCubemapArray; } @@ -506,6 +506,7 @@ void RenderCubemapGGXConvolution(SkyUpdateContext skyContext) m_BuiltinParameters.commandBuffer.CopyTexture(m_SkyboxBSDFCubemapIntermediate, i, renderingContext.skyboxBSDFCubemapArray, 6 * bsdfIdx + i); } } + renderingContext.skyboxBSDFCubemapArray.IncrementUpdateCount(); } } @@ -571,6 +572,13 @@ bool AcquireSkyRenderingContext(SkyUpdateContext updateContext, int newHash, str } ReleaseCachedContext(updateContext.cachedSkyRenderingContextId); + + Texture skyReflection = GetReflectionTexture(updateContext); + if (skyReflection != m_BlackCubemapArray) + { + int marginalID = ImportanceSamplers.GetIdentifier(skyReflection); + ImportanceSamplers.ScheduleRelease(marginalID); + } } else { @@ -815,6 +823,35 @@ public void UpdateEnvironment(HDCamera hdCamera, ScriptableRenderContext renderC var reflectionTexture = GetReflectionTexture(hdCamera.lightingSky); cmd.SetGlobalTexture(HDShaderIDs._SkyTexture, reflectionTexture); + + int skyID = ImportanceSamplers.GetIdentifier(reflectionTexture); + if (ImportanceSamplers.ExistAndReady(skyID)) + { + ImportanceSamplersSystem.MarginalTextures marginals = ImportanceSamplers.GetMarginals(skyID); + + cmd.SetGlobalInt (HDShaderIDs._SkyTextureImportanceSamplerReady, 1); + cmd.SetGlobalVector (HDShaderIDs._SkyTextureSizeInfos, new Vector4( + marginals.conditionalMarginal.rt.height, + 1.0f/marginals.conditionalMarginal.rt.height, + 0.5f/marginals.conditionalMarginal.rt.height, + 0.0f + )); + cmd.SetGlobalTexture(HDShaderIDs._SkyTextureIntegrals, marginals.integral); + cmd.SetGlobalTexture(HDShaderIDs._SkyTextureMarginals, marginals.marginal); + cmd.SetGlobalTexture(HDShaderIDs._SkyTextureConditionalMarginals, marginals.conditionalMarginal); + } + else + { + RTHandle black = TextureXR.GetBlackTextureArray(); + cmd.SetGlobalInt (HDShaderIDs._SkyTextureImportanceSamplerReady, 0); + cmd.SetGlobalVector (HDShaderIDs._SkyTextureSizeInfos, Vector4.one); + cmd.SetGlobalTexture(HDShaderIDs._SkyTextureIntegrals, black); + cmd.SetGlobalTexture(HDShaderIDs._SkyTextureMarginals, black); + cmd.SetGlobalTexture(HDShaderIDs._SkyTextureConditionalMarginals, black); + + int cubemapID = ImportanceSamplers.GetIdentifier(reflectionTexture); + ImportanceSamplers.ScheduleMarginalGeneration(cubemapID, reflectionTexture); + } } internal void UpdateBuiltinParameters(SkyUpdateContext skyContext, HDCamera hdCamera, Light sunLight, RTHandle colorBuffer, RTHandle depthBuffer, DebugDisplaySettings debugSettings, int frameIndex, CommandBuffer cmd)