diff --git a/TestProjects/HDRP_Tests/Assets/GraphicTests/Scenes/1x_Materials/1401_HairGraph/HairTest_Marschner_Opaque_DualScattering.mat b/TestProjects/HDRP_Tests/Assets/GraphicTests/Scenes/1x_Materials/1401_HairGraph/HairTest_Marschner_Opaque_DualScattering.mat index b48035366b0..f4646f4c2e8 100644 --- a/TestProjects/HDRP_Tests/Assets/GraphicTests/Scenes/1x_Materials/1401_HairGraph/HairTest_Marschner_Opaque_DualScattering.mat +++ b/TestProjects/HDRP_Tests/Assets/GraphicTests/Scenes/1x_Materials/1401_HairGraph/HairTest_Marschner_Opaque_DualScattering.mat @@ -271,8 +271,8 @@ Material: - _ShiverDirectionality: 0.5 - _ShiverDrag: 0.2 - _Smoothness: 0.5 - - _SmoothnessRemapMax: 0.89 - - _SmoothnessRemapMin: 0.547 + - _SmoothnessRemapMax: 0.903 + - _SmoothnessRemapMin: 0.553 - _SpecularAAScreenSpaceVariance: 0.1 - _SpecularAAThreshold: 0.2 - _SpecularMultiplier: 0.348 @@ -318,10 +318,10 @@ Material: - _ZWrite: 1 m_Colors: - Color_6FC6C3A4: {r: 0.29999998, g: 0.19499996, b: 0.089999996, a: 1} - - _BaseColor: {r: 0.5, g: 0.34403664, b: 0.24770638, a: 1} + - _BaseColor: {r: 0.8207547, g: 0.6714855, b: 0.32907617, a: 1} - _Base_UV_Mask: {r: 1, g: 0, b: 0, a: 0} - _Base_UV_Tiling_and_Offset: {r: 1, g: 1, b: 0, a: 0} - - _Color: {r: 0.5, g: 0.3440366, b: 0.24770635, a: 1} + - _Color: {r: 0.8207547, g: 0.6714855, b: 0.32907614, a: 1} - _Diffuse_Color: {r: 0.643, g: 0.47758824, b: 0.35943696, a: 1} - _DoubleSidedConstants: {r: 1, g: 1, b: 1, a: 0} - _EmissionColor: {r: 1, g: 1, b: 1, a: 1} @@ -329,6 +329,7 @@ Material: - _InvPrimScale: {r: 1, g: 1, b: 0, a: 0} - _IridescenceThicknessRemap: {r: 0, g: 1, b: 0, a: 0} - _SpecularColor: {r: 0.8679245, g: 0.559068, b: 0.2251691, a: 1} + - _Strand_Probe: {r: 0.98, g: -1.46, b: 0.13, a: 0} - _ThicknessRemap: {r: 0, g: 1, b: 0, a: 0} - _TransmittanceColor: {r: 1, g: 1, b: 1, a: 1} - _UVDetailsMappingMask: {r: 1, g: 0, b: 0, a: 0} diff --git a/TestProjects/HDRP_Tests/Assets/GraphicTests/Scenes/1x_Materials/1401_HairGraph/Shadergraph/MarschnerDualScattering/SG_Hair_Marschner_DS.shadergraph b/TestProjects/HDRP_Tests/Assets/GraphicTests/Scenes/1x_Materials/1401_HairGraph/Shadergraph/MarschnerDualScattering/SG_Hair_Marschner_DS.shadergraph index da86ccffce8..3e085cbf319 100644 --- a/TestProjects/HDRP_Tests/Assets/GraphicTests/Scenes/1x_Materials/1401_HairGraph/Shadergraph/MarschnerDualScattering/SG_Hair_Marschner_DS.shadergraph +++ b/TestProjects/HDRP_Tests/Assets/GraphicTests/Scenes/1x_Materials/1401_HairGraph/Shadergraph/MarschnerDualScattering/SG_Hair_Marschner_DS.shadergraph @@ -62,6 +62,9 @@ }, { "m_Id": "7c176772d8494e69a1e6e71caf3824c8" + }, + { + "m_Id": "72360475635d4c3e9d0b562c2f5a03e9" } ], "m_Keywords": [], @@ -239,6 +242,18 @@ }, { "m_Id": "88754b8e9dad4e1ea0eda24699d31a17" + }, + { + "m_Id": "4e73224a74e941e1877cc31e24b97d29" + }, + { + "m_Id": "16f03f918a0b4c8d9f1fc8c119567a27" + }, + { + "m_Id": "e8ccffcf76814cc8aa48bcc65f39d189" + }, + { + "m_Id": "3af9ff7462044f3f8b8484e921d79538" } ], "m_GroupDatas": [ @@ -372,6 +387,20 @@ "m_SlotId": 0 } }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "3af9ff7462044f3f8b8484e921d79538" + }, + "m_SlotId": 0 + }, + "m_InputSlot": { + "m_Node": { + "m_Id": "88754b8e9dad4e1ea0eda24699d31a17" + }, + "m_SlotId": 0 + } + }, { "m_OutputSlot": { "m_Node": { @@ -930,6 +959,9 @@ }, { "m_Id": "88754b8e9dad4e1ea0eda24699d31a17" + }, + { + "m_Id": "e8ccffcf76814cc8aa48bcc65f39d189" } ] }, @@ -1141,7 +1173,7 @@ "m_Hidden": false, "m_ShaderOutputName": "RadialSmoothness", "m_StageCapability": 2, - "m_Value": 0.5, + "m_Value": 0.699999988079071, "m_DefaultValue": 0.5, "m_Labels": [] } @@ -1340,6 +1372,38 @@ "m_DefaultType": 3 } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.VertexIDNode", + "m_ObjectId": "16f03f918a0b4c8d9f1fc8c119567a27", + "m_Group": { + "m_Id": "" + }, + "m_Name": "Vertex ID", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": -402.0, + "y": 966.0000610351563, + "width": 99.0, + "height": 76.99993896484375 + } + }, + "m_Slots": [ + { + "m_Id": "65fff3c7db124feb923a2caa61d64845" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + } +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.BlockNode", @@ -1884,10 +1948,10 @@ "m_ShaderOutputName": "StrandCountProbe", "m_StageCapability": 2, "m_Value": { - "x": 1.0, + "x": -10.0, "y": 0.0, - "z": 1.0, - "w": 0.0 + "z": 0.0, + "w": 10.0 }, "m_DefaultValue": { "x": 0.0, @@ -2089,6 +2153,41 @@ "m_Labels": [] } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.PropertyNode", + "m_ObjectId": "3af9ff7462044f3f8b8484e921d79538", + "m_Group": { + "m_Id": "" + }, + "m_Name": "Property", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": -174.0, + "y": 705.0, + "width": 131.0, + "height": 149.00006103515626 + } + }, + "m_Slots": [ + { + "m_Id": "b2e76247d41f4423b6f027de5a9ffda2" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_Property": { + "m_Id": "72360475635d4c3e9d0b562c2f5a03e9" + } +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.PropertyNode", @@ -2550,6 +2649,47 @@ "m_SerializedDescriptor": "SurfaceDescription.BentNormal" } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.RandomRangeNode", + "m_ObjectId": "4e73224a74e941e1877cc31e24b97d29", + "m_Group": { + "m_Id": "" + }, + "m_Name": "Random Range", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": -98.00000762939453, + "y": 974.0, + "width": 208.0, + "height": 326.0 + } + }, + "m_Slots": [ + { + "m_Id": "f06db4f5a8584f669bf4572d4e87681e" + }, + { + "m_Id": "b5a1df7978164f17b7693eedd9e66b7e" + }, + { + "m_Id": "e52ad65e865f42d7a8353a0891731f97" + }, + { + "m_Id": "e226ab4eebe14e4c8c65aa25064f7977" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + } +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", @@ -2924,6 +3064,21 @@ } } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "64d9bd694c8844ceb82a12d3a3004311", + "m_Id": 0, + "m_DisplayName": "Strand Shadow Bias", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "StrandShadowBias", + "m_StageCapability": 2, + "m_Value": 1.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.Vector4MaterialSlot", @@ -2966,6 +3121,21 @@ ] } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "65fff3c7db124feb923a2caa61d64845", + "m_Id": 0, + "m_DisplayName": "Out", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "Out", + "m_StageCapability": 1, + "m_Value": 0.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.SampleTexture2DNode", @@ -3153,6 +3323,33 @@ } } +{ + "m_SGVersion": 1, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector4ShaderProperty", + "m_ObjectId": "72360475635d4c3e9d0b562c2f5a03e9", + "m_Guid": { + "m_GuidSerialized": "fb379852-ed9d-4cf2-83c6-16711c6d7e96" + }, + "m_Name": "Strand Probe", + "m_DefaultRefNameVersion": 1, + "m_RefNameGeneratedByDisplayName": "Strand Probe", + "m_DefaultReferenceName": "_Strand_Probe", + "m_OverrideReferenceName": "", + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + } +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", @@ -4786,6 +4983,31 @@ } } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector4MaterialSlot", + "m_ObjectId": "b2e76247d41f4423b6f027de5a9ffda2", + "m_Id": 0, + "m_DisplayName": "Strand Probe", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "Out", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "w": 0.0 + }, + "m_Labels": [] +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.DynamicVectorMaterialSlot", @@ -4884,6 +5106,21 @@ "m_DefaultType": 0 } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "b5a1df7978164f17b7693eedd9e66b7e", + "m_Id": 1, + "m_DisplayName": "Min", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Min", + "m_StageCapability": 3, + "m_Value": 0.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", @@ -5370,8 +5607,8 @@ "m_ObjectId": "d30f1aa1a02847c89af2bab89f780e13", "m_MaterialType": 1, "m_ScatteringMode": 1, - "m_GeometryType": 1, - "m_UseRoughenedAzimuthalScattering": true + "m_ColorParameterization": 0, + "m_GeometryType": 1 } { @@ -5777,6 +6014,21 @@ "m_Labels": [] } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "e226ab4eebe14e4c8c65aa25064f7977", + "m_Id": 3, + "m_DisplayName": "Out", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "Out", + "m_StageCapability": 3, + "m_Value": 0.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.PositionMaterialSlot", @@ -5801,6 +6053,21 @@ "m_Space": 0 } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "e52ad65e865f42d7a8353a0891731f97", + "m_Id": 2, + "m_DisplayName": "Max", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Max", + "m_StageCapability": 3, + "m_Value": 1.0, + "m_DefaultValue": 1.0, + "m_Labels": [] +} + { "m_SGVersion": 1, "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", @@ -5866,6 +6133,39 @@ } } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.BlockNode", + "m_ObjectId": "e8ccffcf76814cc8aa48bcc65f39d189", + "m_Group": { + "m_Id": "" + }, + "m_Name": "SurfaceDescription.StrandShadowBias", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 0.0, + "y": 0.0, + "width": 0.0, + "height": 0.0 + } + }, + "m_Slots": [ + { + "m_Id": "64d9bd694c8844ceb82a12d3a3004311" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SerializedDescriptor": "SurfaceDescription.StrandShadowBias" +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", @@ -6092,6 +6392,27 @@ } } +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector2MaterialSlot", + "m_ObjectId": "f06db4f5a8584f669bf4572d4e87681e", + "m_Id": 0, + "m_DisplayName": "Seed", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Seed", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0 + }, + "m_Labels": [] +} + { "m_SGVersion": 0, "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", @@ -6208,6 +6529,9 @@ }, { "m_Id": "226d0088ad363282a5742f217833b48e" + }, + { + "m_Id": "72360475635d4c3e9d0b562c2f5a03e9" } ] } diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/1401_HairGraph.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/1401_HairGraph.png index 023fe3a1bbb..b2d0fd070c6 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/1401_HairGraph.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/LinuxEditor/Vulkan/None/1401_HairGraph.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa40242b3892d08220c2c0a1540ecf2adf9d25cb4df6fe3ebcd0354fc02d731c -size 116205 +oid sha256:457abfc9e67d3b2818aff1308c156238cf154d3207b673b7c2a201f2d88af200 +size 116513 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1401_HairGraph.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1401_HairGraph.png index 88aa1fd3114..fb2471aaf42 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1401_HairGraph.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/OSXEditor/Metal/None/1401_HairGraph.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6521a7c072681dc845922bc1178715aa529ce60a458b2331438d74f40539f5be -size 118104 +oid sha256:61e43150e332dd0e548592a334019530c03bd4a7437fcec241a5a6d014e34dc5 +size 118602 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/1401_HairGraph.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/1401_HairGraph.png index 1edadd05c05..fb2471aaf42 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/1401_HairGraph.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D11/None/1401_HairGraph.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7850a3100c4a9a1e1311ebe164a3a20c7ec9bc182c1b9803a976d13a0729e7d -size 118129 +oid sha256:61e43150e332dd0e548592a334019530c03bd4a7437fcec241a5a6d014e34dc5 +size 118602 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/1401_HairGraph.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/1401_HairGraph.png index 1edadd05c05..fb2471aaf42 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/1401_HairGraph.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Direct3D12/None/1401_HairGraph.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7850a3100c4a9a1e1311ebe164a3a20c7ec9bc182c1b9803a976d13a0729e7d -size 118129 +oid sha256:61e43150e332dd0e548592a334019530c03bd4a7437fcec241a5a6d014e34dc5 +size 118602 diff --git a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/1401_HairGraph.png b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/1401_HairGraph.png index 32b395d6c53..b2d0fd070c6 100644 --- a/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/1401_HairGraph.png +++ b/TestProjects/HDRP_Tests/Assets/ReferenceImages/Linear/WindowsEditor/Vulkan/None/1401_HairGraph.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d70ae3d0457e5a1529716a24f6735a567b3a23e11806a6710ca80a2b31ad7c14 -size 116171 +oid sha256:457abfc9e67d3b2818aff1308c156238cf154d3207b673b7c2a201f2d88af200 +size 116513 diff --git a/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl b/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl index 9f47fdf0b5f..7c0c747371e 100644 --- a/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl +++ b/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl @@ -1342,6 +1342,12 @@ real SafeDiv(real numer, real denom) return (numer != denom) ? numer / denom : 1; } +// Perform a square root safe of imaginary number. +real SafeSqrt(real x) +{ + return sqrt(max(0, x)); +} + // Assumes that (0 <= x <= Pi). real SinFromCos(real cosX) { diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairData.cs b/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairData.cs index 81836a1a115..ff7f33aa037 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairData.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairData.cs @@ -12,6 +12,13 @@ public enum MaterialType Marschner } + public enum ColorParameterization + { + BaseColor, + Melanin, + Absorption + } + public enum GeometryType { Cards, @@ -42,21 +49,21 @@ public ScatteringMode scatteringMode } [SerializeField] - GeometryType m_GeometryType; + ColorParameterization m_ColorParameterization = ColorParameterization.BaseColor; - public GeometryType geometryType + public ColorParameterization colorParameterization { - get => m_GeometryType; - set => m_GeometryType = value; + get => m_ColorParameterization; + set => m_ColorParameterization = value; } [SerializeField] - bool m_UseRoughenedAzimuthalScattering = false; + GeometryType m_GeometryType; - public bool useRoughenedAzimuthalScattering + public GeometryType geometryType { - get => m_UseRoughenedAzimuthalScattering; - set => m_UseRoughenedAzimuthalScattering = value; + get => m_GeometryType; + set => m_GeometryType = value; } } } diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairPropertyBlocks.cs b/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairPropertyBlocks.cs index 3f0cf4f451e..b6587b23865 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairPropertyBlocks.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairPropertyBlocks.cs @@ -37,9 +37,9 @@ class HairAdvancedOptionsPropertyBlock : AdvancedOptionsPropertyBlock { class Styles { + public static GUIContent colorParameterization = new GUIContent("Color Mode", "Indicates the way the hair fiber cortex color is parameterized."); public static GUIContent geometryType = new GUIContent("Geometry Type", "Indicates the type of geometry being used to represent the hair, allowing the shading model to make informed approximations."); public static GUIContent scatteringMode = new GUIContent("Scattering Mode", "TODO"); - public static GUIContent useRoughenedAzimuthalScattering = new GUIContent("Allow Radial Smoothness", "Adds a Radial Smoothness block to the target, controlling the internal scattering of the light paths and absorption that occurs within the fiber."); } HairData hairData; @@ -50,6 +50,9 @@ protected override void CreatePropertyGUI() { base.CreatePropertyGUI(); + // Hide the color mode for now and let it silently default to Base Color. We will discuss with artists before we expose it. + // AddProperty(Styles.colorParameterization, () => hairData.colorParameterization, (newValue) => hairData.colorParameterization = newValue); + // Hair specific properties GUI AddProperty(Styles.geometryType, () => hairData.geometryType, (newValue) => hairData.geometryType = newValue); @@ -58,8 +61,6 @@ protected override void CreatePropertyGUI() // For now only allow scattering mode for strands, as the multiple scattering was developed against this for 21.2. if (hairData.geometryType == HairData.GeometryType.Strands) AddProperty(Styles.scatteringMode, () => hairData.scatteringMode, (newValue) => hairData.scatteringMode = newValue); - - AddProperty(Styles.useRoughenedAzimuthalScattering, () => hairData.useRoughenedAzimuthalScattering, (newValue) => hairData.useRoughenedAzimuthalScattering = newValue); } } } diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairSubTarget.cs b/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairSubTarget.cs index 3057a004516..0b57c4dd62d 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairSubTarget.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/HairSubTarget.cs @@ -61,8 +61,9 @@ public HairData hairData public static FieldDescriptor HairStrandDirection = new FieldDescriptor(string.Empty, "HairStrandDirection", "_HAIR_STRAND_DIRECTION 1"); public static FieldDescriptor UseLightFacingNormal = new FieldDescriptor(string.Empty, "UseLightFacingNormal", "_USE_LIGHT_FACING_NORMAL 1"); public static FieldDescriptor Transmittance = new FieldDescriptor(string.Empty, "Transmittance", "_TRANSMITTANCE 1"); - public static FieldDescriptor UseRoughenedAzimuthalScattering = new FieldDescriptor(string.Empty, "UseRoughenedAzimuthalScattering", "_USE_ROUGHENED_AZIMUTHAL_SCATTERING 1"); public static FieldDescriptor ScatteringAdvanced = new FieldDescriptor(string.Empty, "ScatteringAdvanced", "_USE_ADVANCED_MULTIPLE_SCATTERING 1"); + public static FieldDescriptor AbsorptionFromColor = new FieldDescriptor(string.Empty, "AbsorptionFromColor", "_ABSORPTION_FROM_COLOR 1"); + public static FieldDescriptor AbsorptionFromMelanin = new FieldDescriptor(string.Empty, "AbsorptionFromMelanin", "_ABSORPTION_FROM_MELANIN 1"); public override void GetFields(ref TargetFieldContext context) { @@ -77,8 +78,9 @@ public override void GetFields(ref TargetFieldContext context) context.AddField(RimTransmissionIntensity, descs.Contains(HDBlockFields.SurfaceDescription.RimTransmissionIntensity) && context.pass.validPixelBlocks.Contains(HDBlockFields.SurfaceDescription.RimTransmissionIntensity)); context.AddField(UseLightFacingNormal, hairData.geometryType == HairData.GeometryType.Strands); context.AddField(Transmittance, descs.Contains(HDBlockFields.SurfaceDescription.Transmittance) && context.pass.validPixelBlocks.Contains(HDBlockFields.SurfaceDescription.Transmittance)); - context.AddField(UseRoughenedAzimuthalScattering, hairData.useRoughenedAzimuthalScattering); context.AddField(ScatteringAdvanced, useAdvancedMultipleScattering); + context.AddField(AbsorptionFromColor, hairData.colorParameterization == HairData.ColorParameterization.BaseColor); + context.AddField(AbsorptionFromMelanin, hairData.colorParameterization == HairData.ColorParameterization.Melanin); // Misc context.AddField(SpecularAA, lightingData.specularAA && @@ -106,7 +108,16 @@ public override void GetActiveBlocks(ref TargetActiveBlockContext context) } else { - context.AddBlock(HDBlockFields.SurfaceDescription.RadialSmoothness, hairData.useRoughenedAzimuthalScattering); + // Color parameterization for cortex (default is base color) + context.AddBlock(HDBlockFields.SurfaceDescription.AbsorptionCoefficient, hairData.colorParameterization == HairData.ColorParameterization.Absorption); + context.AddBlock(HDBlockFields.SurfaceDescription.Eumelanin, hairData.colorParameterization == HairData.ColorParameterization.Melanin); + context.AddBlock(HDBlockFields.SurfaceDescription.Pheomelanin, hairData.colorParameterization == HairData.ColorParameterization.Melanin); + + // Need to explicitly remove the base color here as it is by default always included. + if (hairData.colorParameterization != HairData.ColorParameterization.BaseColor) + context.activeBlocks.Remove(BlockFields.SurfaceDescription.BaseColor); + + context.AddBlock(HDBlockFields.SurfaceDescription.RadialSmoothness); context.AddBlock(HDBlockFields.SurfaceDescription.CuticleAngle); // TODO: Refraction Index diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/ShaderPass.template.hlsl b/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/ShaderPass.template.hlsl index 3a58bf2544e..dcca09b338a 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/ShaderPass.template.hlsl +++ b/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/ShaderPass.template.hlsl @@ -28,6 +28,12 @@ void BuildSurfaceData(FragInputs fragInputs, inout SurfaceDescription surfaceDes // copy across graph values, if defined $SurfaceDescription.BaseColor: surfaceData.diffuseColor = surfaceDescription.BaseColor; + + $SurfaceDescription.AbsorptionCoefficient: surfaceData.absorption = surfaceDescription.AbsorptionCoefficient; + + $SurfaceDescription.Eumelanin: surfaceData.eumelanin = surfaceDescription.Eumelanin; + $SurfaceDescription.Pheomelanin: surfaceData.pheomelanin = surfaceDescription.Pheomelanin; +\ $SurfaceDescription.SpecularOcclusion: surfaceData.specularOcclusion = surfaceDescription.SpecularOcclusion; $SurfaceDescription.Smoothness: surfaceData.perceptualSmoothness = surfaceDescription.Smoothness; $SurfaceDescription.Occlusion: surfaceData.ambientOcclusion = surfaceDescription.Occlusion; diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/ShaderPassDefine.template.hlsl b/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/ShaderPassDefine.template.hlsl index 5ac4ee72b3a..d85b7817336 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/ShaderPassDefine.template.hlsl +++ b/com.unity.render-pipelines.high-definition/Editor/Material/Hair/ShaderGraph/ShaderPassDefine.template.hlsl @@ -8,3 +8,5 @@ $SpecularOcclusionCustom: #define _SPECULAR_OCCLUSION_CUSTOM 1 $Specular.AA: #define _ENABLE_GEOMETRIC_SPECULAR_AA 1 $ScatteringAdvanced: #define _USE_ADVANCED_MULTIPLE_SCATTERING 1 $UseRoughenedAzimuthalScattering: #define _USE_ROUGHENED_AZIMUTHAL_SCATTERING 1 +$AbsorptionFromColor: #define _ABSORPTION_FROM_COLOR 1 +$AbsorptionFromMelanin: #define _ABSORPTION_FROM_MELANIN 1 diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/HDBlockFields.cs b/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/HDBlockFields.cs index a04f7eb5dbc..c0bcf4648fb 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/HDBlockFields.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Material/ShaderGraph/HDBlockFields.cs @@ -127,13 +127,20 @@ public struct SurfaceDescription public static BlockFieldDescriptor SecondarySpecularShift = new BlockFieldDescriptor(SurfaceDescription.name, "SecondarySpecularShift", "Secondary Specular Shift", "SURFACEDESCRIPTION_SECONDARYSPECULARSHIFT", new FloatControl(-0.1f), ShaderStage.Fragment); public static BlockFieldDescriptor RadialSmoothness = new BlockFieldDescriptor(SurfaceDescription.name, "RadialSmoothness", "Radial Smoothness", "SURFACEDESCRIPTION_RADIALSMOOTHNESS", - new FloatControl(0.5f), ShaderStage.Fragment); + new FloatControl(0.7f), ShaderStage.Fragment); public static BlockFieldDescriptor CuticleAngle = new BlockFieldDescriptor(SurfaceDescription.name, "CuticleAngle", "Cuticle Angle", "SURFACEDESCRIPTION_CUTICLEANGLE", new FloatControl(3f), ShaderStage.Fragment); public static BlockFieldDescriptor StrandCountProbe = new BlockFieldDescriptor(SurfaceDescription.name, "StrandCountProbe", "Strand Count Probe", "SURFACEDESCRIPTION_STRANDCOUNTPROBE", new Vector4Control(Vector4.zero), ShaderStage.Fragment); public static BlockFieldDescriptor StrandShadowBias = new BlockFieldDescriptor(SurfaceDescription.name, "StrandShadowBias", "Strand Shadow Bias", "SURFACEDESCRIPTION_STRANDSHADOWBIAS", new FloatControl(0f), ShaderStage.Fragment); + public static BlockFieldDescriptor AbsorptionCoefficient = new BlockFieldDescriptor(SurfaceDescription.name, "AbsorptionCoefficient", "Absorption", "SURFACEDESCRIPTION_ABSORPTIONCOEFFICIENT", + new Vector3Control(new Vector3(.06f, 0.1f, 0.2f)), ShaderStage.Fragment); + public static BlockFieldDescriptor Eumelanin = new BlockFieldDescriptor(SurfaceDescription.name, "Eumelanin", "Eumelanin", "SURFACEDESCRIPTION_EUMELANIN", + new FloatControl(0.3f), ShaderStage.Fragment); + public static BlockFieldDescriptor Pheomelanin = new BlockFieldDescriptor(SurfaceDescription.name, "Pheomelanin", "Pheomelanin", "SURFACEDESCRIPTION_PHEOMELANIN", + new FloatControl(0.3f), ShaderStage.Fragment); + // -------------------------------------------------- // StackLit diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.cs b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.cs index 18321e1468c..3ea52b24c74 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.cs @@ -75,6 +75,13 @@ public struct SurfaceData public float secondarySpecularShift; // Marschner + [SurfaceDataAttributes("Absorption Coefficient")] + public Vector3 absorption; + [SurfaceDataAttributes("Eumelanin")] + public float eumelanin; + [SurfaceDataAttributes("Pheomelanin")] + public float pheomelanin; + [SurfaceDataAttributes("Azimuthal Roughness")] public float perceptualRadialSmoothness; [SurfaceDataAttributes("Cuticle Angle")] @@ -128,6 +135,8 @@ public struct BSDFData public float roughnessT; public float roughnessB; + public float h; + // Kajiya kay public float secondaryPerceptualRoughness; public Vector3 secondarySpecularTint; @@ -141,6 +150,7 @@ public struct BSDFData public float lightPathLength; + public float cuticleAngle; public float cuticleAngleR; public float cuticleAngleTT; public float cuticleAngleTRT; @@ -149,7 +159,7 @@ public struct BSDFData public float roughnessTT; public float roughnessTRT; - public float roughnessRadial; + public float perceptualRoughnessRadial; // Global Scattering public Vector4 strandCountProbe; @@ -162,16 +172,14 @@ public struct BSDFData // Init precomputed texture //----------------------------------------------------------------------------- - private Texture2D m_PreIntegratedAzimuthalScatteringLUT; + // TODO: It would be good to select varying dimensions based on the need for resolution on certain axis, for now stick with constant + private const int m_Dim = 64; // X - Roughness // Y - Theta // Z - Absorption private ComputeShader m_PreIntegratedFiberScatteringCS; private RenderTexture m_PreIntegratedFiberScatteringLUT; - private const int m_DimTheta = 64; - private const int m_DimBeta = 64; - private const int m_DimAbsorption = 64; private bool m_PreIntegratedFiberScatteringIsInit; // X - Theta @@ -194,29 +202,28 @@ public override void Build(HDRenderPipelineAsset hdAsset, HDRenderPipelineRuntim PreIntegratedFGD.instance.Build(PreIntegratedFGD.FGDIndex.FGD_GGXAndDisneyDiffuse); LTCAreaLight.instance.Build(); - m_PreIntegratedAzimuthalScatteringLUT = defaultResources.textures.preintegratedAzimuthalScattering; - // Initialize the dual scattering LUT. - m_PreIntegratedFiberScatteringLUT = new RenderTexture(m_DimTheta, m_DimBeta, 0, GraphicsFormat.R16G16_SFloat) + m_PreIntegratedFiberScatteringLUT = new RenderTexture(m_Dim, m_Dim, 0, GraphicsFormat.R16G16_SFloat) { dimension = TextureDimension.Tex3D, - volumeDepth = m_DimAbsorption, + volumeDepth = m_Dim, enableRandomWrite = true, hideFlags = HideFlags.HideAndDontSave, filterMode = FilterMode.Point, wrapMode = TextureWrapMode.Clamp, - name = CoreUtils.GetRenderTargetAutoName(m_DimTheta, m_DimBeta, 0, GraphicsFormat.R16G16_SFloat, "PreIntegratedFiberScattering") + name = CoreUtils.GetRenderTargetAutoName(m_Dim, m_Dim, 0, GraphicsFormat.R16G16_SFloat, "PreIntegratedFiberScattering") }; m_PreIntegratedFiberScatteringLUT.Create(); - m_PreIntegratedFiberAverageScatteringLUT = new RenderTexture(m_DimTheta, m_DimAbsorption, 0, GraphicsFormat.R16G16B16A16_SFloat) + m_PreIntegratedFiberAverageScatteringLUT = new RenderTexture(m_Dim, m_Dim, 0, GraphicsFormat.R16G16B16A16_SFloat) { - dimension = TextureDimension.Tex2D, + dimension = TextureDimension.Tex3D, + volumeDepth = m_Dim, enableRandomWrite = true, hideFlags = HideFlags.HideAndDontSave, filterMode = FilterMode.Point, wrapMode = TextureWrapMode.Clamp, - name = CoreUtils.GetRenderTargetAutoName(m_DimTheta, m_DimAbsorption, 0, GraphicsFormat.R16G16B16A16_SFloat, "PreIntegratedAverageFiberScattering") + name = CoreUtils.GetRenderTargetAutoName(m_Dim, m_Dim, 0, GraphicsFormat.R16G16B16A16_SFloat, "PreIntegratedAverageFiberScattering") }; m_PreIntegratedFiberAverageScatteringLUT.Create(); @@ -242,21 +249,25 @@ public override void RenderInit(CommandBuffer cmd) if (m_PreIntegratedFiberScatteringCS == null) return; - // Preintegration of the dual scattering LUT. - if (!m_PreIntegratedFiberScatteringIsInit) + // Note: Need to preintegrate the azimuthal distribution first as the average attenuation is dependant on it. + if (!m_PreIntegratedFiberAverageScatteringIsInit) { - cmd.SetComputeTextureParam(m_PreIntegratedFiberScatteringCS, 0, _PreIntegratedHairFiberScatteringUAV, m_PreIntegratedFiberScatteringLUT); - cmd.DispatchCompute(m_PreIntegratedFiberScatteringCS, 0, HDUtils.DivRoundUp(m_DimTheta, 8), HDUtils.DivRoundUp(m_DimBeta, 8), HDUtils.DivRoundUp(m_DimAbsorption, 8)); + cmd.SetComputeTextureParam(m_PreIntegratedFiberScatteringCS, 1, _PreIntegratedAverageHairFiberScatteringUAV, m_PreIntegratedFiberAverageScatteringLUT); + cmd.DispatchCompute(m_PreIntegratedFiberScatteringCS, 1, HDUtils.DivRoundUp(m_Dim, 8), HDUtils.DivRoundUp(m_Dim, 8), HDUtils.DivRoundUp(m_Dim, 8)); - m_PreIntegratedFiberScatteringIsInit = true; + m_PreIntegratedFiberAverageScatteringIsInit = true; } - if (!m_PreIntegratedFiberAverageScatteringIsInit) + // Bind the distributions for the next LUT computation + cmd.SetGlobalTexture(_PreIntegratedAverageHairFiberScattering, m_PreIntegratedFiberAverageScatteringLUT); + + // Preintegration of the dual scattering LUT. + if (!m_PreIntegratedFiberScatteringIsInit) { - cmd.SetComputeTextureParam(m_PreIntegratedFiberScatteringCS, 1, _PreIntegratedAverageHairFiberScatteringUAV, m_PreIntegratedFiberAverageScatteringLUT); - cmd.DispatchCompute(m_PreIntegratedFiberScatteringCS, 1, HDUtils.DivRoundUp(m_DimTheta, 8), HDUtils.DivRoundUp(m_DimBeta, 8), 1); + cmd.SetComputeTextureParam(m_PreIntegratedFiberScatteringCS, 0, _PreIntegratedHairFiberScatteringUAV, m_PreIntegratedFiberScatteringLUT); + cmd.DispatchCompute(m_PreIntegratedFiberScatteringCS, 0, HDUtils.DivRoundUp(m_Dim, 8), HDUtils.DivRoundUp(m_Dim, 8), HDUtils.DivRoundUp(m_Dim, 8)); - m_PreIntegratedFiberAverageScatteringIsInit = true; + m_PreIntegratedFiberScatteringIsInit = true; } } @@ -265,9 +276,6 @@ public override void Bind(CommandBuffer cmd) PreIntegratedFGD.instance.Bind(cmd, PreIntegratedFGD.FGDIndex.FGD_GGXAndDisneyDiffuse); LTCAreaLight.instance.Bind(cmd); - if (m_PreIntegratedAzimuthalScatteringLUT != null) - cmd.SetGlobalTexture(HDShaderIDs._PreIntegratedAzimuthalScattering, m_PreIntegratedAzimuthalScatteringLUT); - if (m_PreIntegratedFiberScatteringLUT == null) { throw new Exception("Pre-Integrated Hair Fiber LUT not available!"); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.cs.hlsl index d9706516356..9caeaa3d104 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.cs.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.cs.hlsl @@ -30,10 +30,13 @@ #define DEBUGVIEW_HAIR_SURFACEDATA_SECONDARY_SPECULAR_TINT (1414) #define DEBUGVIEW_HAIR_SURFACEDATA_SPECULAR_SHIFT (1415) #define DEBUGVIEW_HAIR_SURFACEDATA_SECONDARY_SPECULAR_SHIFT (1416) -#define DEBUGVIEW_HAIR_SURFACEDATA_AZIMUTHAL_ROUGHNESS (1417) -#define DEBUGVIEW_HAIR_SURFACEDATA_CUTICLE_ANGLE (1418) -#define DEBUGVIEW_HAIR_SURFACEDATA_STRAND_COUNT_PROBE (1419) -#define DEBUGVIEW_HAIR_SURFACEDATA_STRAND_SHADOW_BIAS (1420) +#define DEBUGVIEW_HAIR_SURFACEDATA_ABSORPTION_COEFFICIENT (1417) +#define DEBUGVIEW_HAIR_SURFACEDATA_EUMELANIN (1418) +#define DEBUGVIEW_HAIR_SURFACEDATA_PHEOMELANIN (1419) +#define DEBUGVIEW_HAIR_SURFACEDATA_AZIMUTHAL_ROUGHNESS (1420) +#define DEBUGVIEW_HAIR_SURFACEDATA_CUTICLE_ANGLE (1421) +#define DEBUGVIEW_HAIR_SURFACEDATA_STRAND_COUNT_PROBE (1422) +#define DEBUGVIEW_HAIR_SURFACEDATA_STRAND_SHADOW_BIAS (1423) // // UnityEngine.Rendering.HighDefinition.Hair+BSDFData: static fields @@ -57,24 +60,26 @@ #define DEBUGVIEW_HAIR_BSDFDATA_BITANGENT_WS (1466) #define DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_T (1467) #define DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_B (1468) -#define DEBUGVIEW_HAIR_BSDFDATA_SECONDARY_PERCEPTUAL_ROUGHNESS (1469) -#define DEBUGVIEW_HAIR_BSDFDATA_SECONDARY_SPECULAR_TINT (1470) -#define DEBUGVIEW_HAIR_BSDFDATA_SPECULAR_EXPONENT (1471) -#define DEBUGVIEW_HAIR_BSDFDATA_SECONDARY_SPECULAR_EXPONENT (1472) -#define DEBUGVIEW_HAIR_BSDFDATA_SPECULAR_SHIFT (1473) -#define DEBUGVIEW_HAIR_BSDFDATA_SECONDARY_SPECULAR_SHIFT (1474) -#define DEBUGVIEW_HAIR_BSDFDATA_ABSORPTION (1475) -#define DEBUGVIEW_HAIR_BSDFDATA_LIGHT_PATH_LENGTH (1476) -#define DEBUGVIEW_HAIR_BSDFDATA_CUTICLE_ANGLE_R (1477) -#define DEBUGVIEW_HAIR_BSDFDATA_CUTICLE_ANGLE_TT (1478) -#define DEBUGVIEW_HAIR_BSDFDATA_CUTICLE_ANGLE_TRT (1479) -#define DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_R (1480) -#define DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_TT (1481) -#define DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_TRT (1482) -#define DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_RADIAL (1483) -#define DEBUGVIEW_HAIR_BSDFDATA_STRAND_COUNT_PROBE (1484) -#define DEBUGVIEW_HAIR_BSDFDATA_STRAND_SHADOW_BIAS (1485) -#define DEBUGVIEW_HAIR_BSDFDATA_SPLINE_VISIBILITY (1486) +#define DEBUGVIEW_HAIR_BSDFDATA_H (1469) +#define DEBUGVIEW_HAIR_BSDFDATA_SECONDARY_PERCEPTUAL_ROUGHNESS (1470) +#define DEBUGVIEW_HAIR_BSDFDATA_SECONDARY_SPECULAR_TINT (1471) +#define DEBUGVIEW_HAIR_BSDFDATA_SPECULAR_EXPONENT (1472) +#define DEBUGVIEW_HAIR_BSDFDATA_SECONDARY_SPECULAR_EXPONENT (1473) +#define DEBUGVIEW_HAIR_BSDFDATA_SPECULAR_SHIFT (1474) +#define DEBUGVIEW_HAIR_BSDFDATA_SECONDARY_SPECULAR_SHIFT (1475) +#define DEBUGVIEW_HAIR_BSDFDATA_ABSORPTION (1476) +#define DEBUGVIEW_HAIR_BSDFDATA_LIGHT_PATH_LENGTH (1477) +#define DEBUGVIEW_HAIR_BSDFDATA_CUTICLE_ANGLE (1478) +#define DEBUGVIEW_HAIR_BSDFDATA_CUTICLE_ANGLE_R (1479) +#define DEBUGVIEW_HAIR_BSDFDATA_CUTICLE_ANGLE_TT (1480) +#define DEBUGVIEW_HAIR_BSDFDATA_CUTICLE_ANGLE_TRT (1481) +#define DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_R (1482) +#define DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_TT (1483) +#define DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_TRT (1484) +#define DEBUGVIEW_HAIR_BSDFDATA_PERCEPTUAL_ROUGHNESS_RADIAL (1485) +#define DEBUGVIEW_HAIR_BSDFDATA_STRAND_COUNT_PROBE (1486) +#define DEBUGVIEW_HAIR_BSDFDATA_STRAND_SHADOW_BIAS (1487) +#define DEBUGVIEW_HAIR_BSDFDATA_SPLINE_VISIBILITY (1488) // Generated from UnityEngine.Rendering.HighDefinition.Hair+SurfaceData // PackingRules = Exact @@ -95,6 +100,9 @@ struct SurfaceData float3 secondarySpecularTint; float specularShift; float secondarySpecularShift; + float3 absorption; + float eumelanin; + float pheomelanin; float perceptualRadialSmoothness; float cuticleAngle; float4 strandCountProbe; @@ -122,6 +130,7 @@ struct BSDFData float3 bitangentWS; float roughnessT; float roughnessB; + float h; float secondaryPerceptualRoughness; float3 secondarySpecularTint; float specularExponent; @@ -130,13 +139,14 @@ struct BSDFData float secondarySpecularShift; float3 absorption; float lightPathLength; + float cuticleAngle; float cuticleAngleR; float cuticleAngleTT; float cuticleAngleTRT; float roughnessR; float roughnessTT; float roughnessTRT; - float roughnessRadial; + float perceptualRoughnessRadial; float4 strandCountProbe; float strandShadowBias; float splineVisibility; @@ -203,6 +213,15 @@ void GetGeneratedSurfaceDataDebug(uint paramId, SurfaceData surfacedata, inout f case DEBUGVIEW_HAIR_SURFACEDATA_SECONDARY_SPECULAR_SHIFT: result = surfacedata.secondarySpecularShift.xxx; break; + case DEBUGVIEW_HAIR_SURFACEDATA_ABSORPTION_COEFFICIENT: + result = surfacedata.absorption; + break; + case DEBUGVIEW_HAIR_SURFACEDATA_EUMELANIN: + result = surfacedata.eumelanin.xxx; + break; + case DEBUGVIEW_HAIR_SURFACEDATA_PHEOMELANIN: + result = surfacedata.pheomelanin.xxx; + break; case DEBUGVIEW_HAIR_SURFACEDATA_AZIMUTHAL_ROUGHNESS: result = surfacedata.perceptualRadialSmoothness.xxx; break; @@ -283,6 +302,9 @@ void GetGeneratedBSDFDataDebug(uint paramId, BSDFData bsdfdata, inout float3 res case DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_B: result = bsdfdata.roughnessB.xxx; break; + case DEBUGVIEW_HAIR_BSDFDATA_H: + result = bsdfdata.h.xxx; + break; case DEBUGVIEW_HAIR_BSDFDATA_SECONDARY_PERCEPTUAL_ROUGHNESS: result = bsdfdata.secondaryPerceptualRoughness.xxx; break; @@ -307,6 +329,9 @@ void GetGeneratedBSDFDataDebug(uint paramId, BSDFData bsdfdata, inout float3 res case DEBUGVIEW_HAIR_BSDFDATA_LIGHT_PATH_LENGTH: result = bsdfdata.lightPathLength.xxx; break; + case DEBUGVIEW_HAIR_BSDFDATA_CUTICLE_ANGLE: + result = bsdfdata.cuticleAngle.xxx; + break; case DEBUGVIEW_HAIR_BSDFDATA_CUTICLE_ANGLE_R: result = bsdfdata.cuticleAngleR.xxx; break; @@ -325,8 +350,8 @@ void GetGeneratedBSDFDataDebug(uint paramId, BSDFData bsdfdata, inout float3 res case DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_TRT: result = bsdfdata.roughnessTRT.xxx; break; - case DEBUGVIEW_HAIR_BSDFDATA_ROUGHNESS_RADIAL: - result = bsdfdata.roughnessRadial.xxx; + case DEBUGVIEW_HAIR_BSDFDATA_PERCEPTUAL_ROUGHNESS_RADIAL: + result = bsdfdata.perceptualRoughnessRadial.xxx; break; case DEBUGVIEW_HAIR_BSDFDATA_STRAND_COUNT_PROBE: result = bsdfdata.strandCountProbe.xyz; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.hlsl index f0bcd554e23..addf5c46471 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.hlsl @@ -20,7 +20,7 @@ // For TT, the dominant contribution comes from light transmitted straight through the fiber (thus 0). // For TRT, a similar observation is made and v3/2 is used to approximate. #define HAIR_H_TT 0.0 -#define HAIR_H_TRT 0.866 +#define HAIR_H_TRT 0.86602540378 // #define HAIR_DISPLAY_REFERENCE_BSDF // #define HAIR_DISPLAY_REFERENCE_IBL @@ -32,6 +32,56 @@ #define MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_SCATTERING (1 << 19) #define MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_LONGITUDINAL (1 << 20) +//----------------------------------------------------------------------------- +// Absorption Parameterization Mappings +//----------------------------------------------------------------------------- + +// Ref: A Practical and Controllable Hair and Fur Model for Production Path Tracing Eq. 9 +float3 AbsorptionFromReflectance(float3 diffuseColor, float azimuthalRoughness) +{ + float beta = azimuthalRoughness; + float beta2 = beta * beta; + float beta3 = beta2 * beta; + float beta4 = beta3 * beta; + float beta5 = beta4 * beta; + + // Least squares fit of an inverse mapping between scattering parameters and scattering albedo. + float denom = 5.969 - (0.215 * beta) + (2.532 * beta2) - (10.73 * beta3) + (5.574 * beta4) + (0.245 * beta5); + + float3 t = log(diffuseColor) / denom; + return t * t; +} + +// Require an inverse mapping, as we parameterize the LUTs by reflectance wavelength (or for approximation that rely on diffuse). +float3 ReflectanceFromAbsorption(float3 absorption, float azimuthalRoughness) +{ + float beta = azimuthalRoughness; + float beta2 = beta * beta; + float beta3 = beta2 * beta; + float beta4 = beta3 * beta; + float beta5 = beta4 * beta; + + // Least squares fit of an inverse mapping between scattering parameters and scattering albedo. + float denom = 5.969 - (0.215 * beta) + (2.532 * beta2) - (10.73 * beta3) + (5.574 * beta4) + (0.245 * beta5); + + float3 t = -sqrt(absorption) * denom; + return exp(t); +} + +// Ref: An Energy-Conserving Hair Reflectance Model Sec. 6.1 +float3 AbsorptionFromMelanin(float eumelanin, float pheomelanin) +{ + const float3 eA = float3(0.419, 0.697, 1.37); + const float3 eP = float3(0.187, 0.4, 1.05); + + return (eumelanin * eA) + (pheomelanin * eP); +} + +float3 ReflectanceFromMelanin(float eumelanin, float pheomelanin, float azimuthalRoughness) +{ + return ReflectanceFromAbsorption( AbsorptionFromMelanin(eumelanin, pheomelanin), azimuthalRoughness ); +} + //----------------------------------------------------------------------------- // Helper functions/variable specific to this material //----------------------------------------------------------------------------- @@ -41,11 +91,28 @@ // To be used as an approximation to d'Eon et al's Energy Conserving Longitudinal Scattering Function. // TODO: Move me to BSDF.hlsl -real3 D_LongitudinalScatteringGaussian(real3 theta, real3 beta) +real3 D_LongitudinalScatteringGaussian(real3 thetaH, real3 beta) { - real3 v = theta / beta; const real sqrtTwoPi = 2.50662827463100050241; - return rcp(beta * sqrtTwoPi) * exp(-0.5 * v * v); + return rcp(beta * sqrtTwoPi) * exp(-Sq(thetaH) / (2 * Sq(beta))); +} + +float GetHFromTube(float3 L, float3 N, float3 T) +{ + // Angle of inclination from normal plane. + float sinTheta = dot(L, T); + + // Project w to the normal plane. + float3 LProj = SafeNormalize(L - sinTheta * T); + + // Find gamma in the normal plane. + float cosGamma = dot(LProj, N); + + // Need to account for the sign to recover -1..1 + float sgn = sign(dot(N, cross(LProj, T))); + + // Length along the fiber width. + return SafeSqrt(1 - Sq(cosGamma)) * sgn; } float ModifiedRefractionIndex(float cosThetaD) @@ -58,22 +125,6 @@ float ModifiedRefractionIndex(float cosThetaD) return 1.19 / cosThetaD + (0.36 * cosThetaD); } -// Ref: A Practical and Controllable Hair and Fur Model for Production Path Tracing -float3 DiffuseColorToAbsorption(float3 diffuseColor, float azimuthalRoughness) -{ - float beta = azimuthalRoughness; - float beta2 = beta * beta; - float beta3 = beta2 * beta; - float beta4 = beta3 * beta; - float beta5 = beta4 * beta; - - // Least squares fit of an inverse mapping between scattering parameters and scattering albedo. - float denom = 5.969 - (0.215 * beta) + (2.532 * beta2) - (10.73 * beta3) + (5.574 * beta4) + (0.245 * beta5); - - float3 t = log(diffuseColor) / denom; - return t * t; -} - float4 GetDiffuseOrDefaultColor(BSDFData bsdfData, float replace) { return float4(bsdfData.diffuseColor, 0.0); @@ -228,10 +279,9 @@ BSDFData ConvertSurfaceDataToBSDFData(uint2 positionSS, SurfaceData surfaceData) // Marschner if (HasFlag(surfaceData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_MARSCHNER)) { - // Note: Light Path Length is computed per-light. - // Cuticle Angle const float cuticleAngle = radians(surfaceData.cuticleAngle); + bsdfData.cuticleAngle = -cuticleAngle; bsdfData.cuticleAngleR = -cuticleAngle; bsdfData.cuticleAngleTT = cuticleAngle * 0.5; bsdfData.cuticleAngleTRT = cuticleAngle * 1.5; @@ -243,23 +293,27 @@ BSDFData ConvertSurfaceDataToBSDFData(uint2 positionSS, SurfaceData surfaceData) bsdfData.roughnessTRT = roughnessL * 2.0; // Azimuthal Roughness - #if _USE_ROUGHENED_AZIMUTHAL_SCATTERING - bsdfData.roughnessRadial = PerceptualSmoothnessToRoughness(surfaceData.perceptualRadialSmoothness); + bsdfData.perceptualRoughnessRadial = PerceptualSmoothnessToPerceptualRoughness(surfaceData.perceptualRadialSmoothness); + + // Absorption. Note: We require diffuse color to parameterize LUTs and for approximation purposes. + #if _ABSORPTION_FROM_COLOR + bsdfData.absorption = AbsorptionFromReflectance(surfaceData.diffuseColor, bsdfData.perceptualRoughnessRadial); + #elif _ABSORPTION_FROM_MELANIN + bsdfData.absorption = AbsorptionFromMelanin(surfaceData.eumelanin, surfaceData.pheomelanin); + bsdfData.diffuseColor = ReflectanceFromMelanin(surfaceData.eumelanin, surfaceData.pheomelanin, bsdfData.perceptualRoughnessRadial); #else - // Need to provide some sensible default in case of no roughened azimuthal scattering, since currently our - // absorption is dependent on it. 0.3 is generally a good default for hair, but 0.8 seems to get us closer - // to an absorption that better matches the Kajiya diffuse component. - bsdfData.roughnessRadial = 0.8; + bsdfData.absorption = surfaceData.absorption; + bsdfData.diffuseColor = ReflectanceFromAbsorption(bsdfData.absorption, bsdfData.perceptualRoughnessRadial); #endif - // Absorption - bsdfData.absorption = DiffuseColorToAbsorption(surfaceData.diffuseColor, bsdfData.roughnessRadial); - #if _USE_ADVANCED_MULTIPLE_SCATTERING bsdfData.strandCountProbe = surfaceData.strandCountProbe; bsdfData.strandShadowBias = surfaceData.strandShadowBias; bsdfData.splineVisibility = -1; #endif + + // Only necesarry for reference. + // bsdfData.h = -1 + 2 * InterleavedGradientNoise(positionSS, _TaaFrameInfo.z); } ApplyDebugToBSDFData(bsdfData); @@ -488,37 +542,80 @@ LightTransportData GetLightTransportData(SurfaceData surfaceData, BuiltinData bu // BSDF share between directional light, punctual light and area light (reference) //----------------------------------------------------------------------------- -#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairReference.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReference.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/PreIntegratedAzimuthalScattering.hlsl" #ifdef _USE_ADVANCED_MULTIPLE_SCATTERING #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/MultipleScattering/HairMultipleScattering.hlsl" #endif //_USE_ADVANCED_MULTIPLE_SCATTERING + bool IsNonZeroBSDF(float3 V, float3 L, PreLightData preLightData, BSDFData bsdfData) { return true; // Due to either reflection or transmission being always active } -void GetMarschnerAngle(float3 T, float3 V, float3 L, - out float thetaH, out float cosThetaD, out float sinThetaI, out float cosPhi) +struct HairAngle +{ + float sinThetaI; + float sinThetaO; + float cosThetaI; + float cosThetaO; + float cosThetaD; + float thetaH; + float phiI; + float phiO; + float phi; + float cosPhi; + float sinThetaT; + float cosThetaT; +}; + +void GetHairAngleLocal(float3 wo, float3 wi, inout HairAngle angles) { - // Optimized math for spherical coordinate angle derivation. - // Ref: Light Scattering from Human Hair Fibers - sinThetaI = dot(T, L); - float sinThetaR = dot(T, V); + angles.sinThetaO = wo.x; + angles.sinThetaI = wi.x; - float thetaI = FastASin(sinThetaI); - float thetaR = FastASin(sinThetaR); - thetaH = (thetaI + thetaR) * 0.5; + float thetaO = FastASin(angles.sinThetaO); + float thetaI = FastASin(angles.sinThetaI); + angles.thetaH = (thetaI + thetaO) * 0.5; - cosThetaD = cos((thetaR - thetaI) * 0.5); + angles.cosThetaD = cos((thetaO - thetaI) * 0.5); + angles.cosThetaI = SafeSqrt(1 - Sq(angles.sinThetaI)); + angles.cosThetaO = SafeSqrt(1 - Sq(angles.sinThetaO)); + + angles.phiI = FastAtan2(wi.z, wi.y); + angles.phiO = FastAtan2(wo.z, wo.y); + angles.phi = angles.phiI - angles.phiO; + + angles.cosPhi = cos(angles.phi); + + angles.sinThetaT = angles.sinThetaO / 1.55; + angles.cosThetaT = SafeSqrt(1 - Sq(angles.sinThetaT)); +} + +void GetHairAngleWorld(float3 V, float3 L, float3 T, inout HairAngle angles) +{ + angles.sinThetaO = dot(T, V); + angles.sinThetaI = dot(T, L); + + float thetaO = FastASin(angles.sinThetaO); + float thetaI = FastASin(angles.sinThetaI); + angles.thetaH = (thetaI + thetaO) * 0.5; + + angles.cosThetaD = cos((thetaO - thetaI) * 0.5); + angles.cosThetaO = cos(thetaO); + angles.cosThetaI = cos(thetaI); - // Ref: Hair Animation and Rendering in the Nalu Demo // Projection onto the normal plane, and since phi is the relative angle, we take the cosine in this projection. - float3 LProj = L - sinThetaI * T; - float3 VProj = V - sinThetaR * T; - cosPhi = dot(LProj, VProj) * rsqrt(dot(LProj, LProj) * dot(VProj, VProj) + 0.0001); // zero-div guard + float3 VProj = V - angles.sinThetaO * T; + float3 LProj = L - angles.sinThetaI * T; + angles.cosPhi = dot(LProj, VProj) * rsqrt(dot(LProj, LProj) * dot(VProj, VProj) + 1e-5); // zero-div guard + angles.phi = FastACos(angles.cosPhi); + + // Fixed for approximate human hair IOR + angles.sinThetaT = angles.sinThetaO / 1.55; + angles.cosThetaT = SafeSqrt(1 - Sq(angles.sinThetaT)); } CBSDF EvaluateBSDF(float3 V, float3 L, PreLightData preLightData, BSDFData bsdfData) @@ -583,9 +680,6 @@ CBSDF EvaluateBSDF(float3 V, float3 L, PreLightData preLightData, BSDFData bsdfD if (HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_MARSCHNER)) { -#ifdef HAIR_DISPLAY_REFERENCE_BSDF - cbsdf = EvaluateMarschnerReference(V, L, bsdfData); -#else // Approximation of the three primary paths in a hair fiber (R, TT, TRT), with concepts from: // "Strand-Based Hair Rendering in Frostbite" (Tafuri 2019) // "A Practical and Controllable Hair and Fur Model for Production Path Tracing" (Chiang 2016) @@ -596,8 +690,18 @@ CBSDF EvaluateBSDF(float3 V, float3 L, PreLightData preLightData, BSDFData bsdfD // Reminder: All of these flags are known at compile time and the compiler will strip away the unused paths. // Retrieve angles via spherical coordinates in the hair shading space. - float thetaH, cosThetaD, sinThetaI, cosPhi; - GetMarschnerAngle(T, V, L, thetaH, cosThetaD, sinThetaI, cosPhi); + HairAngle angles; + ZERO_INITIALIZE(HairAngle, angles); +#if 0 + // Transform to the local frame for spherical coordinates, + // Note that the strand direction is assumed to lie pointing down the +X axis. + const float3x3 frame = GetLocalFrame(bsdfData.geomNormalWS, bsdfData.hairStrandDirectionWS); + const float3 wo = mul(V, transpose(frame)); + const float3 wi = mul(L, transpose(frame)); + GetHairAngleLocal(wo, wi, angles); +#else + GetHairAngleWorld(V, L, T, angles); +#endif const float3 alpha = float3( bsdfData.cuticleAngleR, @@ -612,85 +716,79 @@ CBSDF EvaluateBSDF(float3 V, float3 L, PreLightData preLightData, BSDFData bsdfD ); // The index of refraction that can be used to analyze scattering in the normal plane (Bravais' Law). - float etaPrime = ModifiedRefractionIndex(cosThetaD); + const float etaPrime = ModifiedRefractionIndex(angles.cosThetaD); // Reduced absorption coefficient. - // Note: Technically should divide absorption by thetaT here, but comparing to reference - // proved a negligible difference and thus not worth the extra computation cost. - float3 mu = bsdfData.absorption; + const float3 mu = bsdfData.absorption; - // Various terms reused between lobe evaluation. - float D = 0; - float3 A, F, Tr, S = 0; + // Various misc. terms reused between lobe evaluation. + float3 F, Tr, S = 0; - // Evaluate the longitudinal scattering for all three lobes. - float3 M = 1; + // Evaluate the longitudinal scattering for all three paths. + const float3 M = D_LongitudinalScatteringGaussian(angles.thetaH - alpha, beta); - // We have a flag for this as we require a version of the BCSDF that evaluates only azimuthal scattering (N = A * D) for pre-integration purposes. - if (!HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_LONGITUDINAL)) - { - M = D_LongitudinalScatteringGaussian(thetaH - alpha, beta); - } + // Save the attenuations in case of multiple scattering. + float3 A[3]; + + // Fetch the preintegrated azimuthal distributions for each path + const float3 D = GetRoughenedAzimuthalScatteringDistribution(angles.phi, angles.cosThetaD, bsdfData.perceptualRoughnessRadial); // Solve the first three lobes (R, TT, TRT). - // TODO: Residual TRRT+ Lobe. (accounts for ~15% energy otherwise lost by the first three lobes). // R - if (!HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_R)) { - // Distribution and attenuation for this path as proposed by d'Eon et al, replaced with a trig identity for cos half phi. - D = 0.25 * sqrt(0.5 + 0.5 * cosPhi); - A = F_Schlick(bsdfData.fresnel0, sqrt(0.5 + 0.5 * dot(L, V))); - - S += M[0] * A * D; + // Attenuation for this path as proposed by d'Eon et al, replaced with a trig identity for cos half phi. + A[0] = F_Schlick(bsdfData.fresnel0, sqrt(0.5 + 0.5 * dot(L, V))); + S += M[0] * A[0] * D[0]; } // TT if (!HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_TT)) { - #if _USE_ROUGHENED_AZIMUTHAL_SCATTERING - // This lobe's distribution is determined by sampling coefficients from a pre-integrated LUT of the distribution and evaluating a gaussian. - D = GetPreIntegratedAzimuthalScatteringTransmissionDistribution(bsdfData.roughnessRadial, cosThetaD, cosPhi); - #else - // Karis' approximation of Pixar's logisitic with scale of √0.35 - D = exp(-3.65 * cosPhi - 3.98); - #endif - // Attenutation (Simplified for H = 0) - // Note: H = ~0.55 seems to be more suitable for this lobe's attenuation, but H = 0 allows us to simplify more of the math at the cost of slightly more error. - // Plot: https://www.desmos.com/calculator/pum8esu6ot - F = F_Schlick(bsdfData.fresnel0, cosThetaD); - Tr = exp(-4 * mu); - A = Sq(1 - F) * Tr; + float cosGammaO = SafeSqrt(1 - Sq(HAIR_H_TT)); + float cosTheta = angles.cosThetaO * cosGammaO; + F = F_Schlick(bsdfData.fresnel0, cosTheta); + + float sinGammaT = HAIR_H_TT / etaPrime; + float cosGammaT = SafeSqrt(1 - Sq(sinGammaT)); + Tr = exp(-mu * (2 * cosGammaT / angles.cosThetaT)); - S += M[1] * A * D; + A[1] = Sq(1 - F) * Tr; + + S += M[1] * A[1] * D[1]; } // TRT - if (!HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_TRT)) { - // This lobe's distribution is determined by Frostbite's improvement over Karis' TRT approximation (maintaining Azimuthal Roughness). - const float scaleFactor = saturate(1.5 * (1 - bsdfData.roughnessRadial)); - D = scaleFactor * exp(scaleFactor * (17.0 * cosPhi - 16.78)); - // Attenutation (Simplified for H = √3/2) - F = F_Schlick(bsdfData.fresnel0, cosThetaD * 0.5); - Tr = exp(-2 * mu * (1 + cos(2 * FastASin(HAIR_H_TRT / etaPrime)))); - A = Sq(1 - F) * F * Sq(Tr); + float cosGammaO = SafeSqrt(1 - Sq(HAIR_H_TRT)); + float cosTheta = angles.cosThetaO * cosGammaO; + F = F_Schlick(bsdfData.fresnel0, cosTheta); + + float sinGammaT = HAIR_H_TRT / etaPrime; + float cosGammaT = SafeSqrt(1 - Sq(sinGammaT)); + Tr = exp(-mu * (2 * cosGammaT / angles.cosThetaT)); - S += M[2] * A * D; + A[2] = Sq(1 - F) * F * Sq(Tr); + + S += M[2] * A[2] * D[2]; } + // TODO: Residual TRRT+ Lobe. (accounts for ~15% energy otherwise lost by the first three lobes). + + // This seems necesarry to match the reference. + S *= INV_PI; + // Transmission event is built into the model. // Some stubborn NaNs have cropped up due to the angle optimization, we suppress them here with a max for now. cbsdf.specR = max(S, 0); - #endif // Multiple Scattering #if _USE_ADVANCED_MULTIPLE_SCATTERING if (!HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_SCATTERING)) { - cbsdf.specR = EvaluateMultipleScattering(L, cbsdf.specR, bsdfData, alpha, beta, thetaH, sinThetaI); + cbsdf.specR = EvaluateMultipleScattering(L, cbsdf.specR, bsdfData, alpha, beta, angles.thetaH, angles.sinThetaI, D, A); } else #endif diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairPathTracing.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairPathTracing.hlsl index 43c87687a6b..2cc208722dd 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairPathTracing.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairPathTracing.hlsl @@ -2,11 +2,31 @@ #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingMaterial.hlsl" #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingBSDF.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceFillingCurves.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Random.hlsl" + +// https://www.pbrt.org/hair.pdf +float2 DemuxFloat(float x) +{ + uint64_t v = x * (1ull << 32); + + uint2 bits = uint2(Compact1By1(v), Compact1By1(v >> 1)); + + return float2(bits.x / float(1 << 16), + bits.y / float(1 << 16)); +} + // -------------------------------------------------------------------------------------- void ProcessBSDFData(PathIntersection pathIntersection, BuiltinData builtinData, inout BSDFData bsdfData) { - // TODO + // NOTE: Currently we don't support ray-aligned ribbons in the acceleration structure, so our only H-calculation routines + // are either stochastic or derived from a tube intersection. +#if 1 + bsdfData.h = GetHFromTube(-WorldRayDirection(), bsdfData.normalWS, bsdfData.hairStrandDirectionWS); +#else + bsdfData.h = -1 + 2 * InterleavedGradientNoise(pathIntersection.pixelCoord, _RaytracingSampleIndex); +#endif } bool CreateMaterialData(PathIntersection pathIntersection, BuiltinData builtinData, BSDFData bsdfData, inout float3 shadingPosition, inout float theSample, out MaterialData mtlData) @@ -29,23 +49,47 @@ void EvaluateMaterial(MaterialData mtlData, float3 sampleDir, out MaterialResult { Init(result); - CBSDF cbsdf = EvaluateMarschnerReference(mtlData.V, sampleDir, mtlData.bsdfData); + // Transform to the local frame for spherical coordinates, + // Note that the strand direction is assumed to lie pointing down the X axis, as this is expected by the BSDF. + float3x3 frame = GetLocalFrame(mtlData.bsdfData.normalWS, mtlData.bsdfData.hairStrandDirectionWS); + float3 wi = mul(sampleDir, transpose(frame)); + float3 wo = mul(mtlData.V, transpose(frame)); - result.specValue = cbsdf.specR; + CBSDF cbsdf = EvaluateHairReference(wo, wi, mtlData.bsdfData); - // TODO: Importance Sample - result.specPdf = INV_FOUR_PI; + result.specValue = cbsdf.specR * abs(wi.z); } bool SampleMaterial(MaterialData mtlData, float3 inputSample, out float3 sampleDir, out MaterialResult result) { Init(result); +#if 0 // We sample the sphere due to reflective and transmittive events. sampleDir = SampleSphereUniform(inputSample.x, inputSample.y); EvaluateMaterial(mtlData, sampleDir, result); + result.specPdf = INV_FOUR_PI; +#else + // Transform to the local frame for spherical coordinates, + // Note that the strand direction is assumed to lie pointing down the X axis, as this is expected by the BSDF. + float3x3 frame = GetLocalFrame(mtlData.bsdfData.normalWS, mtlData.bsdfData.hairStrandDirectionWS); + float3 wo = mul(mtlData.V, transpose(frame)); + + // Need four random samples, derive two extra ones from the given third. + float4 u = float4( + inputSample.xy, + DemuxFloat(inputSample.z) + ); + + CBSDF cbsdf = SampleHairReference(wo, sampleDir, result.specPdf, u, mtlData.bsdfData); + result.specValue = cbsdf.specR * abs(sampleDir.z); + + // Transform back into world space. + sampleDir = mul(sampleDir, frame); +#endif + return true; } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairReference.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairReference.hlsl deleted file mode 100644 index 8533befa5b8..00000000000 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairReference.hlsl +++ /dev/null @@ -1,396 +0,0 @@ -// #define HAIR_REFERENCE_NEAR_FIELD -// #define HAIR_REFERENCE_LONGITUDINAL_ENERGY_CONSERVING - -struct ReferenceInputs -{ - float thetaI; - float thetaR; - float thetaD; - float thetaT; - - float phiI; - float phiR; - float phi; - - float LdotV; - - float h; - - float eta; - float etaP; - float3 fresnel0; - - float shifts[3]; - float variances[3]; - float logisticScale; - - float3 absorption; - float3 absorptionP; -}; - -float ModifiedIOR(float ior, float thetaD) -{ - float sinThetaD = sin(thetaD); - float num = (ior * ior) - (sinThetaD * sinThetaD); - return sqrt(num) / cos(thetaD); -} - -float HyperbolicCosecant(float x) -{ - return rcp(sinh(x)); -} - -// Plot: https://www.desmos.com/calculator/4dnfmn9xal -float RoughnessToLongitudinalVariance(float roughness) -{ -#ifdef HAIR_REFERENCE_LONGITUDINAL_ENERGY_CONSERVING - float beta = roughness; - float v = (0.726 * beta) + (0.812 * beta * beta) + (3.7 * pow(beta, 20.0)); - return v * v; -#else - return roughness; -#endif -} - -float RoughnessToLogisticalScale(float roughness) -{ - float beta = roughness; - return (0.265 * beta) + (1.194 * beta * beta) + (5.372 * pow(beta, 22.0)); -} - -// Precompute the factorials (should really precompute the squared value). -static const float FACTORIAL[11] = { 1.0, - 1.0, - 2.0, - 6.0, - 24.0, - 120.0, - 720.0, - 5040.0, - 40320.0, - 362880.0, - 3628800.0 }; - -// Modified Bessel Function of the First Kind -float BesselI(float x) -{ - float b = 0; - - UNITY_UNROLL - for (int i = 0; i <= 10; ++i) - { - const float f = FACTORIAL[i]; - b += pow(x, 2.0 * i) / (pow(4, i) * f * f); - } - - return b; -} - -// Remap the azimuthal direction to the normalized logistic function on -PI to PI. -float RemapLogisticAngle(float a) -{ - if (a < -PI) - a += TWO_PI; - - if (a > +PI) - a -= TWO_PI; - - return a; -} - -// Ref: Light Scattering from Human Hair Fibers -float AzimuthalDirection(uint p, float etaPrime, float h) -{ - float gammaI = asin(h); - float gammaT = asin(h / etaPrime); - float omega = (2 * p * gammaT) - (2 * gammaI) + (p * PI); - - return omega; -} - -float3 Attenuation(uint p, float h, float LdotV, float thetaD, float etaPrime, float3 fresnel0, float3 absorption) -{ - float3 A; - - if (p == 0) - { - // Attenuation term for R is a special case.s - A = F_Schlick(fresnel0, sqrt(0.5 + 0.5 * LdotV)); - } - else - { - float3 f = F_Schlick(fresnel0, acos(cos(thetaD) * cos(asin(h)))); - float gammaT = asin(h / etaPrime); - float3 T = exp(-2 * absorption * (1 + cos(2 * gammaT))); - - // A = pow(1 - f, 2.0) * pow(f, p - 1) * pow(T, p); - - if (p == 1) - A = Sq(1 - f) * T; - else - A = Sq(1 - f) * f * Sq(T); - } - - return A; -} - -// Ref: [A Practical and Controllable Hair and Fur Model for Production Path Tracing] -// Plot: https://www.desmos.com/calculator/cmy0eig6ln -float LogisticAzimuthalAngularDistribution(float x, float s) -{ - const float a = -PI; - const float b = +PI; - - const float scalePeakTerm = sqrt(PI / 8.0); - s *= scalePeakTerm; - - float normalizeTerm = rcp(rcp(1 + exp(a / s)) - rcp(1 + exp(b / s))); - - float distributionN = exp(-x / s); - float distributionD = s * Sq(1 + distributionN); - - return normalizeTerm * (distributionN / distributionD); -} - -float Gaussian(float beta, float phi) -{ - return exp(-0.5 * (phi * phi) / (beta * beta)) * rcp(sqrt(TWO_PI) * beta); -} - -// Ref: [An Energy-Conserving Hair Reflectance Model] -float GaussianDetector(float beta, float phi) -{ - float D = 0; - - // Higher order detection is negligible for (beta < 80º). - int order = 4; - - for (int k = -order; k <= order; k++) - { - D += Gaussian(beta, phi - (TWO_PI * k)); - } - - return D; -} - -float3 AzimuthalScatteringNearField(uint p, ReferenceInputs inputs) -{ - // Evaluation of near field azimuthal scattering is done with the true offset (h). - // It leverages the monte carlo integration of the pathtracer to solve the full integral. - float3 A = Attenuation(p, inputs.h, inputs.LdotV, inputs.thetaD, inputs.etaP, inputs.fresnel0, inputs.absorptionP); - - float azimuth = AzimuthalDirection(p, inputs.etaP, inputs.h); - - // Remap to the logistic function. - azimuth = RemapLogisticAngle(azimuth); - - float D = LogisticAzimuthalAngularDistribution(inputs.logisticScale, inputs.phi - azimuth); - - return A * D; -} - -// Plot: https://www.desmos.com/calculator/i86ekgtzlg -float3 AzimuthalScatteringFarField(uint p, ReferenceInputs inputs) -{ - // Integrate azimuthal scattering over the fiber width using a gaussian quadrature. - // Np(phi) = 0.5 * Int{-1, 1}{A(p, h) * D(phi - Omega)dh} where h is the fiber axis offset. - float3 N = 0; - - // Quadrature of order 35 is sufficient for all but very smooth hairs (beta < 2º). - const uint n = 35; - - for (uint i = 0; i < n; i++) - { - // Remap h to -1..1 - float h = 2 * ((float)i / n) - 1; - - float3 A = Attenuation(p, h, inputs.LdotV, inputs.thetaD, inputs.etaP, inputs.fresnel0, inputs.absorptionP); - - float omega = AzimuthalDirection(p, inputs.etaP, h); - - float D = GaussianDetector(inputs.logisticScale, inputs.phi - omega); - - N += A * D; - } - - N *= 2.0 / n; - - return 0.5 * N; -} - -// Ref: [An Energy-Conserving Hair Reflectance Model] -// Plot: https://www.desmos.com/calculator/jmf1ofgfdv -float LongitudinalScattering(uint p, ReferenceInputs inputs) -{ - const float v = max(0.0001, inputs.variances[p]); - float thetaI = inputs.thetaI; - float thetaR = inputs.thetaR; - - float M; - -#ifdef HAIR_REFERENCE_LONGITUDINAL_ENERGY_CONSERVING - // Apply the cuticle shift. - thetaR -= inputs.shifts[p]; - - if (v < 0.1) - { - // Ref: [https://publons.com/review/414383/] - // Small variances (< ~0.1) produce numerical issues due to limited floating precision. - float a = (cos(-thetaI) * cos(thetaR)) / v; - float b = (sin(-thetaI) * sin(thetaR)) / v; - - // The log of the bessel function may also be problematic for larger inputs (> ~12)... - float lnI0; - if (a > 12) - { - // ...in which case it's approximated. - lnI0 = a + 0.5 * (-log(TWO_PI) + log(rcp(a)) + rcp(8 * a)); - } - else - { - lnI0 = log(BesselI(a)); - } - - M = exp(lnI0 + b - rcp(v) + 0.6931 + log(rcp(2 * v))); - } - else - { - M = HyperbolicCosecant(rcp(v)) / (2 * v); - M *= exp((sin(-thetaI) * sin(thetaR)) / v); - M *= BesselI((cos(-thetaI) * cos(thetaR)) / v); - } -#else - const float thetaH = 0.5 * (thetaI + thetaR); - M = D_LongitudinalScatteringGaussian(thetaH - inputs.shifts[p], v).x; -#endif - - return M; -} - -float3 AzimuthalScattering(uint p, ReferenceInputs inputs) -{ - float3 N; - -#ifdef HAIR_REFERENCE_NEAR_FIELD - // Disney Integration of N(phi, h) (Near-Field). - N = AzimuthalScatteringNearField(p, inputs); -#else - // D'Eon's integration over fiber width and Gaussian Detector (Far-Field). - N = AzimuthalScatteringFarField(p, inputs); -#endif - - return N; -} - -CBSDF EvaluateMarschnerReference(float3 V, float3 L, BSDFData bsdfData) -{ - CBSDF cbsdf; - ZERO_INITIALIZE(CBSDF, cbsdf); - - // Transform to the local frame for spherical coordinates - float3x3 frame = GetLocalFrame(bsdfData.hairStrandDirectionWS); - float3 I = TransformWorldToTangent(L, frame); - float3 R = TransformWorldToTangent(V, frame); - - ReferenceInputs inputs; - ZERO_INITIALIZE(ReferenceInputs, inputs); - - // Model Reference Inputs. - // Notation Ref: Light Scattering from Human Hair Fibers - { - // Longitudinal - inputs.thetaI = HALF_PI - acos(I.z); - inputs.thetaR = HALF_PI - acos(R.z); - inputs.thetaD = (inputs.thetaR - inputs.thetaI) * 0.5; - - // Azimuthal - float phiI = atan2(I.y, I.x); - float phiR = atan2(R.y, R.x); - inputs.phi = phiR - phiI; - - inputs.variances[0] = RoughnessToLongitudinalVariance(bsdfData.roughnessR); - inputs.variances[1] = RoughnessToLongitudinalVariance(bsdfData.roughnessTT); - inputs.variances[2] = RoughnessToLongitudinalVariance(bsdfData.roughnessTRT); - - inputs.shifts[0] = bsdfData.cuticleAngleR; - inputs.shifts[1] = bsdfData.cuticleAngleTT; - inputs.shifts[2] = bsdfData.cuticleAngleTRT; - - inputs.eta = 1.55; - inputs.fresnel0 = bsdfData.fresnel0; - - // The analysis of azimuthal scattering can be restricted to the normal plane by exploiting - // the Bravais properties of a smooth cylinder fiber and using the modified index of refraction. - inputs.etaP = ModifiedIOR(inputs.eta, inputs.thetaD); - - inputs.LdotV = dot(L, V); - -#ifdef HAIR_REFERENCE_NEAR_FIELD - // Evaluation of h in the normal plane, given by gammaI = asin(h), where gammaI is the incident angle. - // Since we are using a near-field method, we can use the true h value (rather than integrating over the whole fiber width). - inputs.h = sin(acos(dot(bsdfData.normalWS, L))); - - inputs.logisticScale = RoughnessToLogisticalScale(bsdfData.roughnessRadial); -#else - // TODO: Maintain the Disney parameterization for the far field model. - inputs.logisticScale = bsdfData.roughnessRadial; -#endif - - float thetaT = asin(sin(inputs.thetaR / inputs.eta)); - inputs.absorptionP = bsdfData.absorption / cos(thetaT); - } - - float3 S = 0; - - // Factored lobe representation. Sigma Sp(thetai, thetao, phi) = Mp(thetai, thetao) * Np(phi). - for (uint p = 0; p < 3; p++) - { - if (p == 0 && HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_R)) continue; - if (p == 1 && HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_TT)) continue; - if (p == 2 && HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_TRT)) continue; - - S += LongitudinalScattering(p, inputs) * AzimuthalScattering(p, inputs); - } - - // Suppress NaNs. - S = saturate(S); - - // Transmission is currently built in to the model. Should the TT lobe be separated? - cbsdf.specR = S; - - return cbsdf; -} - -//----------------------------------------------------------------------------- -// EvaluateBSDF_Env - Reference -// ---------------------------------------------------------------------------- - -float3 IntegrateMarschnerIBLRef(LightLoopContext lightLoopContext, - float3 V, PreLightData preLightData, EnvLightData lightData, BSDFData bsdfData, - uint sampleCount = 16) -{ - float3 acc = float3(0.0, 0.0, 0.0); - - // Add some jittering on Hammersley2d - float2 randNum = InitRandom(V.xy * 0.5 + 0.5); - - // Integrate over the sphere due to reflective and transmissive events in the BSDF. - for (uint i = 0; i < sampleCount; ++i) - { - float2 u = Hammersley2d(i, sampleCount); - u = frac(u + randNum); - - float3 L = SampleSphereUniform(u.x, u.y); - - // Incident Light intensity - float4 val = SampleEnv(lightLoopContext, lightData.envIndex, L, 0, lightData.rangeCompressionFactorCompensation, 0.5); - - // BRDF Data - CBSDF cbsdf = EvaluateMarschnerReference(V, L, bsdfData); - - float weight = rcp(INV_FOUR_PI * sampleCount); - acc += val.rgb * cbsdf.specR * weight; - } - - return acc; -} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/MultipleScattering/HairMultipleScattering.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/MultipleScattering/HairMultipleScattering.hlsl index d46a9fb1185..29d69447b5f 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/MultipleScattering/HairMultipleScattering.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/MultipleScattering/HairMultipleScattering.hlsl @@ -1,5 +1,4 @@ TEXTURE3D(_PreIntegratedHairFiberScattering); -TEXTURE2D(_PreIntegratedAverageHairFiberScattering); #define FRONT 0 #define BACK 1 @@ -12,7 +11,7 @@ struct HairScatteringData float3 averageShift [2]; // Average azimuthal scattering. - float3x3 NG; + float3 NG; }; #define HALF_SQRT_INV_PI 0.28209479177387814347 @@ -48,12 +47,16 @@ HairScatteringData GetHairScatteringData(BSDFData bsdfData, float3 alpha, float3 // 1) Sample the average scattering. { // Prepare the sampling coordinate. - float X = PerceptualRoughnessToPerceptualSmoothness(bsdfData.perceptualRoughness); + // (Note, currently clamping the smoothness due to noise in the LUT, this can be fixed by preintegrating with importance sampling). + float X = clamp(PerceptualRoughnessToPerceptualSmoothness(bsdfData.perceptualRoughness), 0.0, 0.6); float Y = abs(sinThetaI); - float3 Z = clamp(bsdfData.diffuseColor, 0.01, 0.99); // Need to clamp the absorption a bit due to artifacts at these boundaries. + float3 Z = bsdfData.diffuseColor; - // Sample the LUT for each color channel (wavelength). + // Sample the LUT for each wavelength. // Note that we parameterize by diffuse color, not absorption, to fit in [0, 1]. + // It might be possible to fully support azimuthal roughness by separating the integral and using extra 2D lut. + // However the effect of azimuthal is subtle for the scattering term, mostly producing a much more saturated result for low absorptions. + // Because of this, it might be much simpler and easier to approximate the a. roughness by modulating the attenuation below. float2 R = SAMPLE_TEXTURE3D_LOD(_PreIntegratedHairFiberScattering, s_linear_clamp_sampler, float3(X, Y, Z.r), 0).xy; float2 G = SAMPLE_TEXTURE3D_LOD(_PreIntegratedHairFiberScattering, s_linear_clamp_sampler, float3(X, Y, Z.g), 0).xy; float2 B = SAMPLE_TEXTURE3D_LOD(_PreIntegratedHairFiberScattering, s_linear_clamp_sampler, float3(X, Y, Z.b), 0).xy; @@ -62,44 +65,21 @@ HairScatteringData GetHairScatteringData(BSDFData bsdfData, float3 alpha, float3 scatteringData.averageScattering[BACK] = float3(R.y, G.y, B.y); } - // 2) Sample the average azimuthal scattering + // 2) Compute the average scattering variance & shift { - // Prepare the sampling coordiante - float X = abs(sinThetaI); - float3 Y = clamp(bsdfData.diffuseColor, 0.01, 0.99); // Need to clamp the absorption a bit due to artifacts at these boundaries. + // Note: Disney suggest this weighted average but in testing against the reference it seems like the TT terms for + // the forward lobe and the TRT terms for the backward lobe are sufficient. + scatteringData.averageVariance[FRONT] = beta.y; // dot(af, beta) / af; + scatteringData.averageVariance[BACK] = beta.z; // dot(ab, beta) / ab; - // Sample the LUT for each color channel (wavelength). - // Note that we parameterize by diffuse color, not absorption, to fit in [0, 1]. - float3 R = SAMPLE_TEXTURE2D_LOD(_PreIntegratedAverageHairFiberScattering, s_linear_clamp_sampler, float2(X, Y.r), 0).xyz; - float3 G = SAMPLE_TEXTURE2D_LOD(_PreIntegratedAverageHairFiberScattering, s_linear_clamp_sampler, float2(X, Y.g), 0).xyz; - float3 B = SAMPLE_TEXTURE2D_LOD(_PreIntegratedAverageHairFiberScattering, s_linear_clamp_sampler, float2(X, Y.b), 0).xyz; - - scatteringData.NG = float3x3( float3(R.x, G.x, B.x), - float3(R.y, G.y, B.y), - float3(R.z, G.z, B.z) ); - } - - // 3) Compute the average scattering variance & shift - { - const float3 af = scatteringData.averageScattering[FRONT]; - const float3 ab = scatteringData.averageScattering[BACK]; - - // Here we should be deriving the average variance with the per-component (R, TT, TRT) - // BSDF average in the hemisphere, and not the BSDF average per-absorption channel. - float3 afW = af / dot(1, af); - float3 abW = ab / dot(1, ab); - - scatteringData.averageVariance[FRONT] = dot(beta, afW); // Front scattering (Disney Eq. 15) - scatteringData.averageVariance[BACK] = dot(beta, abW); // Back scattering (Disney Eq. 16) - - scatteringData.averageShift[FRONT] = dot(alpha, afW); // Front scattering (Disney Eq. 13) - scatteringData.averageShift[BACK] = dot(alpha, abW); // Back scattering (Disney Eq. 14) + scatteringData.averageShift[FRONT] = alpha.y; // dot(af, alpha) / af; + scatteringData.averageShift[BACK] = alpha.z; // dot(ab, alpha) / ab; } return scatteringData; } -float3 EvaluateMultipleScattering(float3 L, float3 Fs, BSDFData bsdfData, float3 alpha, float3 beta, float thetaH, float sinThetaI) +float3 EvaluateMultipleScattering(float3 L, float3 Fs, BSDFData bsdfData, float3 alpha, float3 beta, float thetaH, float sinThetaI, float3 D, float3 A[3]) { // Fetch the various preintegrated data. HairScatteringData hairScatteringData = GetHairScatteringData(bsdfData, alpha, beta, sinThetaI); @@ -119,7 +99,6 @@ float3 EvaluateMultipleScattering(float3 L, float3 Fs, BSDFData bsdfData, float3 const float3 Bb = hairScatteringData.averageVariance[BACK]; const float3 Bf2 = Sq(Bf); const float3 Bb2 = Sq(Bb); - const float3x3 NG = hairScatteringData.NG; // Global scattering. // ----------------------------------------------------------------------------------- @@ -179,26 +158,29 @@ float3 EvaluateMultipleScattering(float3 L, float3 Fs, BSDFData bsdfData, float3 // Compute the average back scattering standard deviation ( Eq. 17 ). float3 sigmaB = (1 + db * af2); - sigmaB *= (ab * sqrt((2 * Bf2) + Bb2)) + (ab3 * sqrt((2 * Bf2) + Bb2)); + sigmaB *= (ab * sqrt((2 * Bf2) + Bb2)) + (ab3 * sqrt((2 * Bf2) + (3 * Bb2))); sigmaB /= ab + (ab3 * ((2 * Bf) + (3 * Bb))); - // sigmaB = Sq(sigmaB); - - // Computes the average back scattering spread ( Eq. 15 ). - // float3 Sb = D_LongitudinalScatteringGaussian(thetaH - deltaB, sigmaB); + sigmaB = Sq(sigmaB); // Resolve the overall local scattering term ( Eq. 19 & 20 Disney ). - float3 fsBackDirect = db * 2 * Ab * D_LongitudinalScatteringGaussian(thetaH - deltaB, sigmaB); - float3 fsBackScatter = db * 2 * Ab * D_LongitudinalScatteringGaussian(thetaH - deltaB, sigmaB + sigmaF); + // Note, for now remove the square, there seems to be a discrepancy in the gaussian used in the paper (ours takes std. dev) + float3 fsBack = db * 2 * Ab * D_LongitudinalScatteringGaussian(thetaH - deltaB, sqrt(sigmaB + sigmaF)) / PI; // Resolve the approximated multiple scattering. (Approximate Eq. 22) // ------------------------------------------------------------------------------------ - const float3 MG = D_LongitudinalScatteringGaussian(thetaH - alpha, beta + sigmaF); + const float3 MG = D_LongitudinalScatteringGaussian(thetaH - alpha, beta + sqrt(sigmaF)); + + // Reuse the azimuthal component, it seems sufficient instead of a whole extra LUT for the average forward scattering on the hemisphere. + // TODO: This computation is redundant, already done in direct BSDF! + const float3x3 NG = float3x3( D.x * A[0], + D.y * A[1], + D.z * A[2] ); + const float3 fsScatter = mul(MG, NG); - const float3 Fdirect = directFraction * (Fs + fsBackDirect); - const float3 Fscatter = (Tf - directFraction) * df * (fsScatter + PI * fsBackScatter); + const float3 Fdirect = directFraction * (Fs + fsBack); + const float3 Fscatter = (Tf - directFraction) * df * (fsScatter + PI * fsBack); const float3 F = (Fdirect + Fscatter) * sqrt(1 - Sq(sinThetaI)); return max(F, 0); - } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/MultipleScattering/HairMultipleScatteringPreIntegration.compute b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/MultipleScattering/HairMultipleScatteringPreIntegration.compute index 67fd5d05449..93ebb040707 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/MultipleScattering/HairMultipleScatteringPreIntegration.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/MultipleScattering/HairMultipleScatteringPreIntegration.compute @@ -4,14 +4,16 @@ // This define is required for invoking BSDF. #define HAS_LIGHTLOOP -// #pragma enable_d3d11_debug_symbols +// This define is required to map the reflectance to absorption for the preintegration. +#define _ABSORPTION_FROM_COLOR 1 -// Instead of indirectly setting up the angles we inform the BSDF that we will be explicitly setting them. -// #define HAIR_OVERRIDE_ANGLE_BSDF +// #pragma enable_d3d11_debug_symbols #define DIM 64 -#define SPHERE_SAMPLES 128 -#define DPHI radians(2) +#define SPHERE_SAMPLES 32 + +#define DPHI radians(10) +#define DH 0.01 // HDRP generic includes #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" @@ -46,23 +48,20 @@ SurfaceData ConfigureFiberSurface(float diffuseColor, float perceptualSmoothness surfaceData.perceptualSmoothness = perceptualSmoothness; // Radial Smoothness (Azimuthal Roughness). - // TODO: Currently we don't support azimuthal roughness with dual scattering. - // surfaceData.perceptualRadialSmoothness = 0.3; + // TODO: Support varying azimuthal roughness. We don't have enough dimensions in the LUT to parameterize this as well, + // so we fall back to a sensible default for now, this one is generally acceptable for human hair (less so for animal fur). + surfaceData.perceptualRadialSmoothness = 0.3; // Cuticle Angle - surfaceData.cuticleAngle = 3; + surfaceData.cuticleAngle = 2; - // The theoretical fiber points in the Z-up direction. - surfaceData.hairStrandDirectionWS = float3(0, 0, 1); + // The theoretical fiber points points down the +x axis. + surfaceData.hairStrandDirectionWS = float3(1, 0, 0); - return surfaceData; -} + // Be sure to define the normal as well as it defines the hair shading basis + surfaceData.geomNormalWS = float3(0, 0, 1); -void ClampRoughness(inout BSDFData bsdfData) -{ - bsdfData.roughnessR = max(0.05, bsdfData.roughnessR); - bsdfData.roughnessTT = max(0.05, bsdfData.roughnessTT); - bsdfData.roughnessTRT = max(0.05, bsdfData.roughnessTRT); + return surfaceData; } // Parameterization: @@ -87,9 +86,6 @@ void Main (uint3 dispatchThreadID : SV_DispatchThreadID) // Use the conversion from the surface data to compute all of the per-lobe bsdf information. BSDFData bsdfData = ConvertSurfaceDataToBSDFData(uint2(0, 0), surfaceData); - // Need to clamp the roughness manually since we invoke the BSDF manually, without it, we get some artifacting in the LUT. - ClampRoughness(bsdfData); - // Unused in this case. PreLightData preLightData; ZERO_INITIALIZE(PreLightData, preLightData); @@ -108,96 +104,87 @@ void Main (uint3 dispatchThreadID : SV_DispatchThreadID) float2 U = Hammersley2d(w, SPHERE_SAMPLES); float3 V = SampleSphereUniform(U.x, U.y); - // Integrate over all incident phi. + // This needs to vary if we are sampling the reference. + bsdfData.h = -1 + 2 * GenerateHashedRandomFloat(w); + + // Integrate phi for isotropic irradiance for (float phi = -HALF_PI; phi < HALF_PI; phi += DPHI) { - // Places a light on the back scattering hemisphere (due to the constriction of phi to the -pi/2 to pi/2 range). - float3 L = SphericalToCartesian(phi, sinThetaI); - - // Invoke the fiber scattering function. - CBSDF cbsdf = EvaluateBSDF(V, L, preLightData, bsdfData); - const float Fs = cbsdf.specR.x; - - // Sort the energy based on which hemisphere the outgoing direction was measured from. - // Doing this effectively simplifies the complex longitudinal scattering lobes into one - // forward (TT dominant) and one backward (R / TRT dominant) lobe. The theoretical fiber - // is facing Z-up, the incident light is on the back hemisphere; thus, outgoing directions - // in the front hemisphere must be negative in the X direction. - if (V.x < 0) - { + // Places a light on the back scattering hemisphere (due to the constriction of phi to the -pi/2 to pi/2 range). + float3 L = SphericalToCartesian(phi, sinThetaI); + + // Align it with the fiber. + L = L.zyx; + + // Invoke the fiber scattering function. + // Note, currently some issues with importance sampling the reference here (NaNs) so uniform for now. +#if 0 + CBSDF cbsdf = EvaluateBSDF(V, L, preLightData, bsdfData); +#else + CBSDF cbsdf = EvaluateHairReference(L, V, bsdfData); +#endif + // PDF = 1/2PI since we technically uniform sample the hemisphere. + const float Fs = Luminance(cbsdf.specR) * abs(V.z) / INV_TWO_PI; + + // Sort the energy based on which hemisphere the outgoing direction was measured from. + // Doing this effectively simplifies the complex longitudinal scattering lobes into one + // forward (TT dominant) and one backward (R / TRT dominant) lobe. The theoretical fiber + // is facing +X, the incident light is on the back hemisphere; thus, outgoing directions + // in the front hemisphere must be negative in the Z direction. + if (V.z < 0) + { // Contribute to the average forward scattering. - A.x += Fs / INV_FOUR_PI; C.x++; - } - else - { + A.x += Fs; C.x++; + } + else + { // Contribute to the average backward scattering. - A.y += Fs / INV_FOUR_PI; C.y++; - } + A.y += Fs; C.y++; + } } } - _PreIntegratedHairFiberScatteringUAV[dispatchThreadID] = INV_PI * (A / float2(C) * 0.5); + // Note: Need to divide by 2/PI Here instead of 1/PI as suggest by the paper to match the reference. + // It's possibly due to how we sampling on the sphere instead of hemisphere but still not totally clear why. + _PreIntegratedHairFiberScatteringUAV[dispatchThreadID] = (2 / PI) * (A / float2(C)); } -// Pre-integrate the average azimuthal scattering on the front scattering semi-circle ( Ref: Equation 25 ) -// TODO: This LUT and the above are parameterized the exact same way. Find a way to combine them into one sample? +// Pre-integrate the azimuthal scattering distributions for the three primary lobes, parameterized by: +// X: Phi +// Y: Theta +// Z: Azimuthal Roughness // ----------------------------------------------------------------- -RWTexture2D _PreIntegratedAverageHairFiberScatteringUAV; +RWTexture3D _PreIntegratedAverageHairFiberScatteringUAV; -#define FLAGS_R MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_LONGITUDINAL | MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_TT | MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_TRT -#define FLAGS_TT MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_LONGITUDINAL | MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_R | MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_TRT -#define FLAGS_TRT MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_LONGITUDINAL | MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_R | MATERIALFEATUREFLAGS_HAIR_MARSCHNER_SKIP_TT - -[numthreads(8, 8, 1)] -void PreIntegrateAzimuthalScattering (uint2 dispatchThreadID : SV_DispatchThreadID) +[numthreads(8, 8, 8)] +void PreIntegrateAzimuthalScattering (uint3 dispatchThreadID : SV_DispatchThreadID) { // Convert the dispatch coordinates to the generation space [0,1]. - float2 UV = float2(((float2)dispatchThreadID + 0.5) / DIM); - - // Configure a theoretical hair fiber to evaluate the average attenuation. - // Note, since we only evaluate the azimuthal scattering we can parameterize to 2D since roughness has no effect on this component. - SurfaceData surfaceDataR = ConfigureFiberSurface(UV.y, 0.5, FLAGS_R); - SurfaceData surfaceDataTT = ConfigureFiberSurface(UV.y, 0.5, FLAGS_TT); - SurfaceData surfaceDataTRT = ConfigureFiberSurface(UV.y, 0.5, FLAGS_TRT); - - // Use the conversion from the surface data to compute all of the per-lobe bsdf information. - BSDFData bsdfDataR = ConvertSurfaceDataToBSDFData(uint2(0, 0), surfaceDataR); - BSDFData bsdfDataTT = ConvertSurfaceDataToBSDFData(uint2(0, 0), surfaceDataTT); - BSDFData bsdfDataTRT = ConvertSurfaceDataToBSDFData(uint2(0, 0), surfaceDataTRT); - - // Need to clamp the roughness manually since we invoke the BSDF manually, without it, we get some artifacting in the LUT. - ClampRoughness(bsdfDataR); - ClampRoughness(bsdfDataTT); - ClampRoughness(bsdfDataTRT); - - // Unused in this case. - PreLightData preLightData; - ZERO_INITIALIZE(PreLightData, preLightData); - - // Configure the initial incident theta direction. - float sinThetaI = UV.x; + const float3 UV = ((float3)dispatchThreadID + 0.5) / DIM; - // We are analyzing the front scattering, so place a viewer on the negative X axis. - float3 V = float3(-1, 0, 0); + // Configure the initial phi, theta, and Bn. + const float phi = FOUR_PI * UV.x - TWO_PI; // Remap 0..1 -> -2PI..2PI + const float eta = ModifiedRefractionIndex(UV.y); // IOR currently fixed for human hair (1.55). + const float s = LogisticScaleFromBeta(UV.z); // Convert to azimuthal roughness logistic scale - float3 A = 0; + float3 NG = 0; - // Integrate over all incident phi. - for (float phi = HALF_PI; phi < PI; phi += DPHI) + // Integrate over the fiber width. + for (float h = -1; h < 1; h += DH) { - // Places a light on the back scattering hemisphere (due to the constriction of phi to the -pi/2 to pi/2 range). - float3 L = SphericalToCartesian(phi, sinThetaI); + const float gammaO = FastASin(h); + const float gammaT = clamp(FastASin(h / eta), -1, 1); - // Invoke the fiber scattering function. - CBSDF cbsdfR = EvaluateBSDF(V, L, preLightData, bsdfDataR); - CBSDF cbsdfTT = EvaluateBSDF(V, L, preLightData, bsdfDataTT); - CBSDF cbsdfTRT = EvaluateBSDF(V, L, preLightData, bsdfDataTRT); + // Re-used directly from the reference. + const float NR = AzimuthalScattering(phi, 0, s, gammaO, gammaT); + const float NTT = AzimuthalScattering(phi, 1, s, gammaO, gammaT); + const float NTRT = AzimuthalScattering(phi, 2, s, gammaO, gammaT); - A += float3( cbsdfR.specR.x, - cbsdfTT.specR.x, - cbsdfTRT.specR.x ) * DPHI; + NG += float3( NR, + NTT, + NTRT ) * DH; } - _PreIntegratedAverageHairFiberScatteringUAV[dispatchThreadID] = (2 / PI) * float4(A, 1); + _PreIntegratedAverageHairFiberScatteringUAV[dispatchThreadID] = float4(NG, 1); } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/PreIntegratedAzimuthalScattering.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/PreIntegratedAzimuthalScattering.hlsl index 431dc9b5e5b..0ae6d482aca 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/PreIntegratedAzimuthalScattering.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/PreIntegratedAzimuthalScattering.hlsl @@ -1,15 +1,12 @@ -TEXTURE2D(_PreIntegratedAzimuthalScattering); +TEXTURE3D(_PreIntegratedAverageHairFiberScattering); -// Returns the roughened azimuthal scattering TT distribution term. -float GetPreIntegratedAzimuthalScatteringTransmissionDistribution(float beta, float cosTheta, float cosPhi) +// Returns the roughened azimuthal scattering distribution term for all three lobes. +float3 GetRoughenedAzimuthalScatteringDistribution(float phi, float cosThetaD, float beta) { - // Sample the coefficients. - float2 c = SAMPLE_TEXTURE2D_LOD(_PreIntegratedAzimuthalScattering, s_linear_clamp_sampler, float2(beta, cosTheta), 0).xy; + const float X = (phi + TWO_PI) / FOUR_PI; + const float Y = cosThetaD; + const float Z = beta; - // The distribution is parameterized by phi, which means we must pay the cost of inverse trig here. - float phi = FastACos(cosPhi); - - // Evaluate the gaussian with the sampled coefficients. - // Gaussian denominator is pre-computed in the second coefficient. - return c.x * exp(-Sq(phi - PI) / c.y); + // TODO: It should be possible to reduce the domain of the integration to 0 -> HALF/PI as it repeats. This will save memory. + return SAMPLE_TEXTURE3D_LOD(_PreIntegratedAverageHairFiberScattering, s_linear_clamp_sampler, float3(X, Y, Z), 0).xyz; } diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference.meta b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference.meta new file mode 100644 index 00000000000..23110fd798c --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5f5dfa3b3cfb346429b60a771a42bca6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReference.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReference.hlsl new file mode 100644 index 00000000000..4d1afda172d --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReference.hlsl @@ -0,0 +1,218 @@ +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReferenceCommon.hlsl" + +// Reference implementation of a Marschner-based energy conserving hair reflectance model with concepts from: +// "The Implementation of a Hair Scattering Model" (Pharr 2016) +// "A Practical and Controllable Hair and Fur Model for Production Path Tracing" (Chiang 2016) +// "Importance Sampling for Physically-Based Hair Fiber Models" (D'Eon 2012) +// "An Energy-Conserving Hair Reflectance Model" (d'Eon 2011) +// "Light Scattering from Human Hair Fibers" (Marschner 2003) + +void ComputeFiberAttenuations(float cosThetaO, float eta, float h, float3 T, inout float3 A[PATH_MAX + 1]) +{ + // Reconstruct the incident angle. + float cosGammaO = SafeSqrt(1 - Sq(h)); + float cosTheta = cosThetaO * cosGammaO; + + float F0 = IorToFresnel0(eta); + float F = F_Schlick(F0, cosTheta); + + // Solve for P == 0 (Reflection at the cuticle). + A[0] = F; + + // Solve for P == 1 (Solves two air-hair boundary events and one transmission event). + A[1] = Sq(1 - F) * T; + + // Solve for 2 < P < PMAX + for (uint p = 2; p < PATH_MAX; p++) + A[p] = A[p - 1] * T * F; + + // Solve for the residual lobe PMAX < P < INFINITY + // Ref: A Practical and Controllable Hair and Fur Model for Production Eq. 6 + A[PATH_MAX] = A[PATH_MAX - 1] * F * T / (1 - T * F); +} + +void ComputeFiberAttenuationsPDF(float cosThetaO, float3 sigmaA, float eta, float h, inout float APDF[PATH_MAX + 1]) +{ + float sinThetaO = SafeSqrt(1 - Sq(cosThetaO)); + + // Compute the refracted ray + float sinThetaT = sinThetaO / eta; + float cosThetaT = SafeSqrt(1 - Sq(sinThetaT)); + + float etaP = sqrt(Sq(eta) - Sq(sinThetaO)) / cosThetaO; + float sinGammaT = h / etaP; + float cosGammaT = SafeSqrt(1 - Sq(sinGammaT)); + + // Compute transmittance of single path through the fiber using Beer's Law. + float3 T = exp(-sigmaA * (2 * cosGammaT / cosThetaT)); + + float3 A[PATH_MAX + 1]; + ComputeFiberAttenuations(cosThetaO, eta, h, T, A); + + float sumY = 0; + + int i; + + for (i = 0; i <= PATH_MAX; i++) + sumY += Luminance(A[i]); + + for (i = 0; i <= PATH_MAX; i++) + APDF[i] = Luminance(A[i]) / sumY; +} + +// Ref: An Energy-Conserving Hair Reflectance Model Eq. 7 +// Plot: https://www.desmos.com/calculator/jmf1ofgfdv +float LongitudinalScattering(float cosThetaI, float cosThetaO, float sinThetaI, float sinThetaO, float v) +{ + float M; + + float a = cosThetaI * cosThetaO / v; + float b = sinThetaI * sinThetaO / v; + + if (v < 0.1) + { + // Ref: [https://publons.com/review/414383/] + // Small variances (< ~0.1) produce numerical issues due to limited floating precision. + M = exp(LogBesselI(a) - b - rcp(v) + 0.6931 + log(rcp(2 * v))); + } + else + { + M = (exp(-b) * BesselI(a)) / (sinh(1 / v) * 2 * v); + } + + return M; +} + +float AzimuthalScattering(float phi, uint p, float s, float gammaO, float gammaT) +{ + float dphi = phi - AzimuthalDirection(p, gammaO, gammaT); + + // Remap Phi to fit in the -PI..PI domain for the logistic. + while (dphi > +PI) dphi -= TWO_PI; + while (dphi < -PI) dphi += TWO_PI; + + return TrimmedLogistic(dphi, s, -PI, PI); +} + +CBSDF EvaluateHairReference(float3 wo, float3 wi, BSDFData bsdfData) +{ + // Initialize the BSDF invocation. + ReferenceBSDFData data = GetReferenceBSDFData(bsdfData); + ReferenceAngles angles = GetReferenceAngles(wi, wo); + + // Find refracted ray angles. + float sinThetaT = angles.sinThetaO / data.eta; + float cosThetaT = SafeSqrt(1 - Sq(sinThetaT)); + + // Find the modified index of refraction. + float etaP = sqrt(Sq(data.eta) - Sq(angles.sinThetaO)) / angles.cosThetaO; + + // Compute refracted angle gamma T (exploiting the Bravais properties of a cylinder). + float sinGammaT = data.h / etaP; + float cosGammaT = SafeSqrt(1 - Sq(sinGammaT)); + float gammaT = clamp(FastASin(sinGammaT), -1, 1); + + // Compute transmittance of single path through the fiber using Beer's Law. + float3 T = exp(-data.sigmaA * (2 * cosGammaT / cosThetaT)); + + // Compute the absorptions that occur in the fiber for every path. + float3 A[PATH_MAX + 1]; + ComputeFiberAttenuations(angles.cosThetaO, data.eta, data.h, T, A); + + float3 F = 0; + + for (uint p = 0; p < PATH_MAX; ++p) + { + float sinThetaO, cosThetaO; + ApplyCuticleTilts(p, angles, data, sinThetaO, cosThetaO); + + F += LongitudinalScattering(angles.cosThetaI, cosThetaO, angles.sinThetaI, sinThetaO, data.v[p]) * A[p] * + AzimuthalScattering(angles.phi, p, data.s, data.gammaO, gammaT); + } + + // Compute the residual lobe + F += LongitudinalScattering(angles.cosThetaI, angles.cosThetaO, angles.sinThetaI, angles.sinThetaO, data.v[PATH_MAX]) * A[PATH_MAX] * INV_TWO_PI; + + if(abs(wi.z) > 0) + F /= abs(wi.z + 1e-4); + + return HairFtoCBSDF(max(F, 0)); +} + +CBSDF SampleHairReference(float3 wo, out float3 wi, out float pdf, float4 u, BSDFData bsdfData) +{ + // Initialize the BSDF invocation. + ReferenceBSDFData data = GetReferenceBSDFData(bsdfData); + + // Compute angles only for the currently known outgoing direction. + ReferenceAngles angles; + ZERO_INITIALIZE(ReferenceAngles, angles); + + angles.sinThetaO = wo.x; + angles.cosThetaO = SafeSqrt(1 - Sq(angles.sinThetaO)); + angles.phiO = FastAtan2(wo.z, wo.y); + + // Determine the path to sample. + float APDF[PATH_MAX + 1]; + ComputeFiberAttenuationsPDF(angles.cosThetaO, data.sigmaA, data.eta, data.h, APDF); + + int p; + + for (p = 0; p < PATH_MAX; p++) + { + if (u.x < APDF[p]) + break; + + u.x -= APDF[p]; + } + + float sinThetaO, cosThetaO; + ApplyCuticleTilts(p, angles, data, sinThetaO, cosThetaO); + + // Note, clamping this sample seems required to prevent NaNs for very low (< ~0.1) variances. + u.y = max(u.y, 1e-4); + + // Importance sample the longitudinal scattering function using an exponential function identity to handle low variance. + // Ref: "Importance Sampling for Physically-Based Hair Fiber Models" Eq. 6 & 7 + // Ref: "Numerically stable sampling of the von Mises Fisher distribution on S2" + float sampleMP = 1 + data.v[p] * log(u.y + (1 - u.y) * exp(-2 / data.v[p])); + float sinThetaI = -sampleMP * sinThetaO + SafeSqrt(1 - Sq(sampleMP)) * cos(TWO_PI * u.z) * cosThetaO; + float cosThetaI = SafeSqrt(1 - Sq(sinThetaI)); + + // Importance sample the azimuthal scattering function + + // Find the modified index of refraction. + float etaP = sqrt(Sq(data.eta) - Sq(angles.sinThetaO)) / angles.cosThetaO; + + // Compute refracted angle gamma T (exploiting the Bravais properties of a cylinder). + float sinGammaT = data.h / etaP; + float gammaT = clamp(FastASin(sinGammaT), -1, 1); + + float phi; + + if (p < PATH_MAX) + phi = AzimuthalDirection(p, data.gammaO, gammaT) + TrimmedLogisticSampled(u.w, data.s, -PI, PI); + else + phi = TWO_PI * u.w; + + // Construct the sampled direction wi. + float phiI = angles.phiO + phi; + wi = float3(sinThetaI, cosThetaI * cos(phiI), cosThetaI * sin(phiI)); + + // Solve the overall PDF + pdf = 0; + + for (p = 0; p < PATH_MAX; p++) + { + float sinThetaOp, cosThetaOp; + ApplyCuticleTilts(p, angles, data, sinThetaOp, cosThetaOp); + + pdf += LongitudinalScattering(cosThetaI, cosThetaOp, sinThetaI, sinThetaOp, data.v[p]) * APDF[p] * + AzimuthalScattering(phi, p, data.s, data.gammaO, gammaT); + } + + // Don't forget the residual lobe + pdf += LongitudinalScattering(cosThetaI, angles.cosThetaO, sinThetaI, angles.sinThetaO, data.v[PATH_MAX]) * APDF[PATH_MAX] * INV_TWO_PI; + + return EvaluateHairReference(wo, wi, bsdfData); +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairReference.hlsl.meta b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReference.hlsl.meta similarity index 75% rename from com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairReference.hlsl.meta rename to com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReference.hlsl.meta index 50bed83a826..5175d06c13c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/HairReference.hlsl.meta +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReference.hlsl.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: deec6414df92499458370add8c145275 +guid: 7ad9dd7e98b60ce4fb6cff38e1e831d7 ShaderIncludeImporter: externalObjects: {} userData: diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReferenceCommon.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReferenceCommon.hlsl new file mode 100644 index 00000000000..59cd6853ced --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReferenceCommon.hlsl @@ -0,0 +1,226 @@ +// The # of lobes to evaluate explicitly (R, TT, TRT, TRRT+..) before summing up the remainder with a residual lobe approximation. +#define PATH_MAX 3 + +#define SQRT_PI_OVER_8 0.62665706865775012560 + +// Precompute the factorials (should really precompute the squared value). +static const float FACTORIAL[11] = { 1.0, + 1.0, + 2.0, + 6.0, + 24.0, + 120.0, + 720.0, + 5040.0, + 40320.0, + 362880.0, + 3628800.0 }; + +struct ReferenceBSDFData +{ + float h; // Intersection offset from fiber center. + float gammaO; + + float eta; // Refraction Index + float3 sigmaA; // Absorption Coefficient of Cortex + float betaM; // Longitudinal Roughness + float betaN; // Azimuthal Roughness + + float alpha; // Cuticle Tilt (Radians) + float sinAlpha[3]; + float cosAlpha[3]; + + float s; // Logistic Scale Factor + float v[PATH_MAX + 1]; // Longitudinal Variance +}; + +struct ReferenceAngles +{ + float sinThetaI; + float sinThetaO; + + float cosThetaI; + float cosThetaO; + + float phiI; + float phiO; + float phi; +}; + +// Ref: Light Scattering from Human Hair Fibers Eq. 3 +float AzimuthalDirection(uint p, float gammaO, float gammaT) +{ + return (2 * p * gammaT) - (2 * gammaO) + (p * PI); +} + +float Logistic(float x, float s) +{ + // Avoids numerical instability for large x / s ratios. + x = abs(x); + + return exp(-x / s) / (s * Sq(1 + exp(-x / s))); +} + +float LogisticCDF(float x, float s) +{ + return 1 / (1 + exp(-x / s)); +} + +float TrimmedLogistic(float x, float s, float a, float b) +{ + return Logistic(x, s) / ( LogisticCDF(b, s) - LogisticCDF(a, s) ); +} + +float TrimmedLogisticSampled(float u, float s, float a, float b) +{ + float k = LogisticCDF(b, s) - LogisticCDF(a, s); + float x = -s * log(1 / (u * k + LogisticCDF(a, s)) - 1); + + return clamp(x, a, b); +} + +// Modified Bessel Function of the First Kind +float BesselI(float x) +{ + float b = 0; + + UNITY_UNROLL + for (int i = 0; i < 10; ++i) + { + const float f = FACTORIAL[i]; + b += pow(abs(x), 2.0 * i) / (pow(4, i) * f * f); + } + + return b; +} + +float LogBesselI(float x) +{ + float lnIO; + + // The log of the bessel function may also be problematic for larger inputs (> ~12)... + if (x > 12) + { + // ...in which case it's approximated. + lnIO = x + 0.5 * (-log(TWO_PI) + log(rcp(x)) + rcp(8 * x)); + } + else + { + lnIO = log(BesselI(x)); + } + + return lnIO; +} + +void LongitudinalVarianceFromBeta(float beta, inout float v[PATH_MAX + 1]) +{ + // Ref: A Practical and Controllable Hair and Fur Model for Production Path Tracing Eq. 7 + v[0] = Sq(0.726 * beta + 0.812 * Sq(beta) + 3.7 * pow(abs(beta), 20.0)); + v[1] = 0.25 * v[0]; + v[2] = 4.0 * v[0]; + + for (int p = 3; p <= PATH_MAX; p++) + v[p] = v[2]; +} + +// Ref: A Practical and Controllable Hair and Fur Model for Production Path Tracing Eq. 8 +float LogisticScaleFromBeta(float beta) +{ + return SQRT_PI_OVER_8 * ((0.265 * beta) + (1.194 * beta * beta) + (5.372 * pow(abs(beta), 22.0))); +} + +void ApplyCuticleTilts(uint p, ReferenceAngles angles, ReferenceBSDFData data, out float sinThetaO, out float cosThetaO) +{ + if (p == 0) + { + sinThetaO = angles.sinThetaO * data.cosAlpha[1] - angles.cosThetaO * data.sinAlpha[1]; + cosThetaO = angles.cosThetaO * data.cosAlpha[1] + angles.sinThetaO * data.sinAlpha[1]; + } + else if (p == 1) + { + sinThetaO = angles.sinThetaO * data.cosAlpha[0] + angles.cosThetaO * data.sinAlpha[0]; + cosThetaO = angles.cosThetaO * data.cosAlpha[0] - angles.sinThetaO * data.sinAlpha[0]; + } + else if (p == 2) + { + sinThetaO = angles.sinThetaO * data.cosAlpha[2] + angles.cosThetaO * data.sinAlpha[2]; + cosThetaO = angles.cosThetaO * data.cosAlpha[2] - angles.sinThetaO * data.sinAlpha[2]; + } + else + { + sinThetaO = angles.sinThetaO; + cosThetaO = angles.cosThetaO; + } + + // Need to clamp for possible out of range after cuticle tilt application. + cosThetaO = abs(cosThetaO); +} + +void GetAlphaScalesFromAlpha(float alpha, inout float sinAlpha[3], inout float cosAlpha[3]) +{ + sinAlpha[0] = sin(alpha); + cosAlpha[0] = SafeSqrt(1 - Sq(sinAlpha[0])); + + // Get the lobe alpha terms by solving for the trigonometric double angle identities. + for (int i = 1; i < 3; ++i) + { + sinAlpha[i] = 2 * cosAlpha[i - 1] * sinAlpha[i - 1]; + cosAlpha[i] = Sq(cosAlpha[i - 1]) - Sq(sinAlpha[i - 1]); + } +} + +ReferenceBSDFData GetReferenceBSDFData(BSDFData bsdfData) +{ + ReferenceBSDFData data; + ZERO_INITIALIZE(ReferenceBSDFData, data); + + data.h = bsdfData.h; + data.gammaO = FastASin(data.h); + data.eta = 1.55; + data.sigmaA = bsdfData.absorption; + data.betaM = bsdfData.perceptualRoughness; + data.betaN = bsdfData.perceptualRoughnessRadial; + data.alpha = bsdfData.cuticleAngle; + data.s = LogisticScaleFromBeta(data.betaN); + + // Fill the list of variances from beta + LongitudinalVarianceFromBeta(data.betaM, data.v); + + // Fill the alpha terms for each lobe + GetAlphaScalesFromAlpha(data.alpha, data.sinAlpha, data.cosAlpha); + + return data; +} + +ReferenceAngles GetReferenceAngles(float3 wi, float3 wo) +{ + ReferenceAngles angles; + ZERO_INITIALIZE(ReferenceAngles, angles); + + angles.sinThetaI = wi.x; + angles.sinThetaO = wo.x; + + // Small epsilon to suppress various compiler warnings + div-zero guard + const float epsilon = 1e-5; + + angles.cosThetaI = SafeSqrt(1 - (Sq(angles.sinThetaI) + epsilon)); + angles.cosThetaO = SafeSqrt(1 - (Sq(angles.sinThetaO) + epsilon)); + + angles.phiI = atan2(wi.z, wi.y + epsilon); + angles.phiO = atan2(wo.z, wo.y + epsilon); + angles.phi = angles.phiI - angles.phiO; + + return angles; +} + +// Quick utility to convert into a structure used in rasterizer. +CBSDF HairFtoCBSDF(float3 F) +{ + CBSDF cbsdf; + ZERO_INITIALIZE(CBSDF, cbsdf); + + // Transmission is baked into the model. + cbsdf.specR = F; + + return cbsdf; +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReferenceCommon.hlsl.meta b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReferenceCommon.hlsl.meta new file mode 100644 index 00000000000..4428759c907 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReferenceCommon.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7a6343f8807eaac44ae874a1c12c8d9b +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineRuntimeResources.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineRuntimeResources.cs index fb8598e0a73..d3697c36efa 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineRuntimeResources.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipelineRuntimeResources.cs @@ -359,6 +359,12 @@ public sealed class ShaderResources [Reload("Runtime/RenderPipeline/Raytracing/Shaders/Denoising/DiffuseDenoiser.compute")] public ComputeShader diffuseDenoiserCS; +#if UNITY_EDITOR + // Furnace Testing (BSDF Energy Conservation) + [Reload("Tests/Editor/Utilities/FurnaceTests.compute")] + public ComputeShader furnaceTestCS; +#endif + #if UNITY_EDITOR // Iterator to retrieve all compute shaders in reflection so we don't have to keep a list of // used compute shaders up to date (prefer editor-only usage) @@ -416,10 +422,6 @@ public sealed class TextureResources [Reload("Runtime/RenderPipelineResources/Texture/CoherentNoise/ScramblingTile256SPP.png")] public Texture2D scramblingTile256SPP; - // Pre-integration LUTs - [Reload("Runtime/RenderPipelineResources/Texture/PreintegratedAzimuthalScattering.exr")] - public Texture2D preintegratedAzimuthalScattering; - // Clouds textures [Reload("Runtime/RenderPipelineResources/Texture/VolumetricClouds/CloudLutRainAO.png")] public Texture2D cloudLutRainAO; diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineRuntimeResources.asset b/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineRuntimeResources.asset index 8a3860cd341..8ecefd75dc0 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineRuntimeResources.asset +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/HDRenderPipelineRuntimeResources.asset @@ -14,130 +14,204 @@ MonoBehaviour: m_EditorClassIdentifier: shaders: defaultPS: {fileID: 4800000, guid: 6e4ae4064600d784cac1e41a9e6f2e59, type: 3} - debugDisplayLatlongPS: {fileID: 4800000, guid: c1d1d149a043a5349ba367da6c2051ba, type: 3} - debugViewMaterialGBufferPS: {fileID: 4800000, guid: 439949ea1bfa91b4ba0d04269fcde33d, type: 3} + debugDisplayLatlongPS: {fileID: 4800000, guid: c1d1d149a043a5349ba367da6c2051ba, + type: 3} + debugViewMaterialGBufferPS: {fileID: 4800000, guid: 439949ea1bfa91b4ba0d04269fcde33d, + type: 3} debugViewTilesPS: {fileID: 4800000, guid: c7c2bd17b06ceb4468e14081aaf1b96f, type: 3} debugFullScreenPS: {fileID: 4800000, guid: e874aca2df8300a488258738c31f85cf, type: 3} - debugColorPickerPS: {fileID: 4800000, guid: 8137b807709e178498f22ed710864bb0, type: 3} + debugColorPickerPS: {fileID: 4800000, guid: 8137b807709e178498f22ed710864bb0, + type: 3} debugExposurePS: {fileID: 4800000, guid: 0ef322534f047a34c96d29419d56d17a, type: 3} - debugLightVolumePS: {fileID: 4800000, guid: 8e706c0e71fcec34a8f5c9713e5e2943, type: 3} - debugLightVolumeCS: {fileID: 7200000, guid: f5d5d21faef5cf445ac2c5d8ff9c4184, type: 3} + debugLightVolumePS: {fileID: 4800000, guid: 8e706c0e71fcec34a8f5c9713e5e2943, + type: 3} + debugLightVolumeCS: {fileID: 7200000, guid: f5d5d21faef5cf445ac2c5d8ff9c4184, + type: 3} debugBlitQuad: {fileID: 4800000, guid: cf5ca5b6ef18b3f429ed707ee9ceac9f, type: 3} - debugViewVirtualTexturingBlit: {fileID: 4800000, guid: 55d195396b03b804eb78c92d468e3c8e, type: 3} + debugViewVirtualTexturingBlit: {fileID: 4800000, guid: 55d195396b03b804eb78c92d468e3c8e, + type: 3} materialError: {fileID: 4800000, guid: 79a966a5200a456188dec0d48d805614, type: 3} - probeVolumeDebugShader: {fileID: 4800000, guid: 3b21275fd12d65f49babb5286f040f2d, type: 3} + probeVolumeDebugShader: {fileID: 4800000, guid: 3b21275fd12d65f49babb5286f040f2d, + type: 3} deferredPS: {fileID: 4800000, guid: 00dd221e34a6ab349a1196b0f2fab693, type: 3} colorPyramidPS: {fileID: 4800000, guid: 2fcfb8d92f45e4549b3f0bad5d0654bf, type: 3} depthPyramidCS: {fileID: 7200000, guid: 64a553bb564274041906f78ffba955e4, type: 3} maxZCS: {fileID: 7200000, guid: e95abf8c7230c344595f41c4dd5ff517, type: 3} copyChannelCS: {fileID: 7200000, guid: a4d45eda75e8e474dbe24a31f741f3b4, type: 3} - screenSpaceReflectionsCS: {fileID: 7200000, guid: d1de9ac7d9016204da289affe9677942, type: 3} + screenSpaceReflectionsCS: {fileID: 7200000, guid: d1de9ac7d9016204da289affe9677942, + type: 3} applyDistortionPS: {fileID: 4800000, guid: 02ae56f4306413c4a96dcf005cde1971, type: 3} - clearDispatchIndirectCS: {fileID: 7200000, guid: fc1f553acb80a6446a32d33e403d0656, type: 3} + clearDispatchIndirectCS: {fileID: 7200000, guid: fc1f553acb80a6446a32d33e403d0656, + type: 3} clearLightListsCS: {fileID: 7200000, guid: 743eb3491795b9545955695d591195a1, type: 3} - buildDispatchIndirectCS: {fileID: 7200000, guid: 4eb1b418be7044c40bb5200496c50f14, type: 3} + buildDispatchIndirectCS: {fileID: 7200000, guid: 4eb1b418be7044c40bb5200496c50f14, + type: 3} buildScreenAABBCS: {fileID: 7200000, guid: 728dce960f8a9c44bbc3abb3b851d8f6, type: 3} - buildPerTileLightListCS: {fileID: 7200000, guid: 65af3444cbf4b3747a4dead7ee00cfee, type: 3} - buildPerBigTileLightListCS: {fileID: 7200000, guid: 5ee1f9d6e09abe045b2f5e0b784b9072, type: 3} - buildPerVoxelLightListCS: {fileID: 7200000, guid: 0bb1b7e0ddcd5c44baf3ddc7456eb196, type: 3} - lightListClusterClearAtomicIndexCS: {fileID: 7200000, guid: 1e3472a94b14a334a93230bbc700d7b2, type: 3} - buildMaterialFlagsCS: {fileID: 7200000, guid: fb3eda953cd6e634e877fb777be2cd08, type: 3} + buildPerTileLightListCS: {fileID: 7200000, guid: 65af3444cbf4b3747a4dead7ee00cfee, + type: 3} + buildPerBigTileLightListCS: {fileID: 7200000, guid: 5ee1f9d6e09abe045b2f5e0b784b9072, + type: 3} + buildPerVoxelLightListCS: {fileID: 7200000, guid: 0bb1b7e0ddcd5c44baf3ddc7456eb196, + type: 3} + lightListClusterClearAtomicIndexCS: {fileID: 7200000, guid: 1e3472a94b14a334a93230bbc700d7b2, + type: 3} + buildMaterialFlagsCS: {fileID: 7200000, guid: fb3eda953cd6e634e877fb777be2cd08, + type: 3} deferredCS: {fileID: 7200000, guid: 0b64f79746d2daf4198eaf6eab9af259, type: 3} contactShadowCS: {fileID: 7200000, guid: 3e6900e06dc185a4380af4dacb4db0a4, type: 3} - volumeVoxelizationCS: {fileID: 7200000, guid: c20b371db720da244b73830ec74a343a, type: 3} - volumetricLightingCS: {fileID: 7200000, guid: b4901a10df2d1e24282725e9fbc77c97, type: 3} - volumetricLightingFilteringCS: {fileID: 7200000, guid: ef9a910d0ec6ebb41ae3f5c7a69daf46, type: 3} + volumeVoxelizationCS: {fileID: 7200000, guid: c20b371db720da244b73830ec74a343a, + type: 3} + volumetricLightingCS: {fileID: 7200000, guid: b4901a10df2d1e24282725e9fbc77c97, + type: 3} + volumetricLightingFilteringCS: {fileID: 7200000, guid: ef9a910d0ec6ebb41ae3f5c7a69daf46, + type: 3} deferredTilePS: {fileID: 4800000, guid: dedaf4ea0d134ca4aad1d95a558c46e5, type: 3} - screenSpaceShadowPS: {fileID: 4800000, guid: bfa43a48695613b4ea19c58858ae1a61, type: 3} - subsurfaceScatteringCS: {fileID: 7200000, guid: b06a7993621def248addd55d0fe931b1, type: 3} + screenSpaceShadowPS: {fileID: 4800000, guid: bfa43a48695613b4ea19c58858ae1a61, + type: 3} + subsurfaceScatteringCS: {fileID: 7200000, guid: b06a7993621def248addd55d0fe931b1, + type: 3} combineLightingPS: {fileID: 4800000, guid: 2e37131331fbdca449b1a2bc47a639ca, type: 3} - debugLocalVolumetricFogAtlasPS: {fileID: 4800000, guid: 8371b763f09c7304889c22aa97ebdfd2, type: 3} - cameraMotionVectorsPS: {fileID: 4800000, guid: 035941b63024d1943af48811c1db20d9, type: 3} - clearStencilBufferPS: {fileID: 4800000, guid: 8ea49ef16606acd489439e676ab84040, type: 3} - copyStencilBufferPS: {fileID: 4800000, guid: 3d1574f1cdfa0ce4995f9bc79ed7f8ec, type: 3} + debugLocalVolumetricFogAtlasPS: {fileID: 4800000, guid: 8371b763f09c7304889c22aa97ebdfd2, + type: 3} + cameraMotionVectorsPS: {fileID: 4800000, guid: 035941b63024d1943af48811c1db20d9, + type: 3} + clearStencilBufferPS: {fileID: 4800000, guid: 8ea49ef16606acd489439e676ab84040, + type: 3} + copyStencilBufferPS: {fileID: 4800000, guid: 3d1574f1cdfa0ce4995f9bc79ed7f8ec, + type: 3} copyDepthBufferPS: {fileID: 4800000, guid: 42dfcc8fe803ece4096c58630689982f, type: 3} blitPS: {fileID: 4800000, guid: e22fc1942c664490980b8793dd4a163d, type: 3} - blitColorAndDepthPS: {fileID: 4800000, guid: b22ad378c678348729d3a3f981b9f270, type: 3} + blitColorAndDepthPS: {fileID: 4800000, guid: b22ad378c678348729d3a3f981b9f270, + type: 3} downsampleDepthPS: {fileID: 4800000, guid: 67d6171b0acc6554aad48c845ec7e67f, type: 3} - upsampleTransparentPS: {fileID: 4800000, guid: 2ad7ce40f0dbaf64dadef1f58d8524d3, type: 3} + upsampleTransparentPS: {fileID: 4800000, guid: 2ad7ce40f0dbaf64dadef1f58d8524d3, + type: 3} resolveStencilCS: {fileID: 7200000, guid: 65b89cac5f286b043a31bf8041776ee7, type: 3} blitCubemapPS: {fileID: 4800000, guid: d05913e251bed7a4992c921c62e1b647, type: 3} - buildProbabilityTablesCS: {fileID: 7200000, guid: b9f26cf340afe9145a699753531b2a4c, type: 3} - computeGgxIblSampleDataCS: {fileID: 7200000, guid: 764a24bb47ef5ba4781d9ae82ca07445, type: 3} + buildProbabilityTablesCS: {fileID: 7200000, guid: b9f26cf340afe9145a699753531b2a4c, + type: 3} + computeGgxIblSampleDataCS: {fileID: 7200000, guid: 764a24bb47ef5ba4781d9ae82ca07445, + type: 3} GGXConvolvePS: {fileID: 4800000, guid: 123ed592ad5c2494b8aed301fd609e7b, type: 3} charlieConvolvePS: {fileID: 4800000, guid: 5685fd17e71045e4ca9fefca38a7c177, type: 3} - opaqueAtmosphericScatteringPS: {fileID: 4800000, guid: 32f724728cf19904291226f239ec16f0, type: 3} + opaqueAtmosphericScatteringPS: {fileID: 4800000, guid: 32f724728cf19904291226f239ec16f0, + type: 3} hdriSkyPS: {fileID: 4800000, guid: 9bd32a6ece529fd4f9408b8d7e00c10d, type: 3} - integrateHdriSkyPS: {fileID: 4800000, guid: 48db2705cf2856d4e893eb30a6892d1b, type: 3} + integrateHdriSkyPS: {fileID: 4800000, guid: 48db2705cf2856d4e893eb30a6892d1b, + type: 3} skyboxCubemapPS: {fileID: 103, guid: 0000000000000000f000000000000000, type: 0} gradientSkyPS: {fileID: 4800000, guid: 2b5d4f1b26f03dc4a873b093e0c4adb1, type: 3} - ambientProbeConvolutionCS: {fileID: 7200000, guid: 6d048f7b1bd45e840b4e79ec92639fa8, type: 3} - groundIrradiancePrecomputationCS: {fileID: 7200000, guid: eb6ae6f326207ee4d987a3e5adddf63a, type: 3} - inScatteredRadiancePrecomputationCS: {fileID: 7200000, guid: 70c69d514688f8545855680760d77418, type: 3} - physicallyBasedSkyPS: {fileID: 4800000, guid: a06934a4863e778498be65d8f865b7a4, type: 3} - planarReflectionFilteringCS: {fileID: 7200000, guid: 9f3f8a01b8caaaa4595591dc96d43dd2, type: 3} + ambientProbeConvolutionCS: {fileID: 7200000, guid: 6d048f7b1bd45e840b4e79ec92639fa8, + type: 3} + groundIrradiancePrecomputationCS: {fileID: 7200000, guid: eb6ae6f326207ee4d987a3e5adddf63a, + type: 3} + inScatteredRadiancePrecomputationCS: {fileID: 7200000, guid: 70c69d514688f8545855680760d77418, + type: 3} + physicallyBasedSkyPS: {fileID: 4800000, guid: a06934a4863e778498be65d8f865b7a4, + type: 3} + planarReflectionFilteringCS: {fileID: 7200000, guid: 9f3f8a01b8caaaa4595591dc96d43dd2, + type: 3} cloudLayerPS: {fileID: 4800000, guid: 001a47fa123e95a4bba13ecb0442d944, type: 3} - bakeCloudTextureCS: {fileID: 7200000, guid: 09a7f6850ee9fb4439e5ebd632127da5, type: 3} - bakeCloudShadowsCS: {fileID: 7200000, guid: 3e7317e0800c066448ee07a3e47f102b, type: 3} - volumetricCloudsCS: {fileID: 7200000, guid: f911a8577fa9a4546a6b255bcf888baf, type: 3} - volumetricCloudMapGeneratorCS: {fileID: 7200000, guid: 6b22771f0aa98744cb09f455a5a818cb, type: 3} - volumetricCloudsCombinePS: {fileID: 4800000, guid: 12f1a69ddf916f042ae6ce8a994506f3, type: 3} - preIntegratedFGD_GGXDisneyDiffusePS: {fileID: 4800000, guid: 123f13d52852ef547b2962de4bd9eaad, type: 3} - preIntegratedFGD_CharlieFabricLambertPS: {fileID: 4800000, guid: 3b3bf235775cf8b4baae7f3306787ab0, type: 3} - preIntegratedFGD_WardPS: {fileID: 4800000, guid: d279c46a545b0af4f9f0c4fa82cd489e, type: 3} - preIntegratedFGD_CookTorrancePS: {fileID: 4800000, guid: a6402c19b020b4a4fb7073aaa2e26aba, type: 3} - preIntegratedFGD_MarschnerPS: {fileID: 4800000, guid: 31f36caf0a5e7f848a1b5328b6ad3eb8, type: 3} - preIntegratedFiberScatteringCS: {fileID: 7200000, guid: 4a087c9d074552c48aeb85184d56312e, type: 3} + bakeCloudTextureCS: {fileID: 7200000, guid: 09a7f6850ee9fb4439e5ebd632127da5, + type: 3} + bakeCloudShadowsCS: {fileID: 7200000, guid: 3e7317e0800c066448ee07a3e47f102b, + type: 3} + volumetricCloudsCS: {fileID: 7200000, guid: f911a8577fa9a4546a6b255bcf888baf, + type: 3} + volumetricCloudMapGeneratorCS: {fileID: 7200000, guid: 6b22771f0aa98744cb09f455a5a818cb, + type: 3} + volumetricCloudsCombinePS: {fileID: 4800000, guid: 12f1a69ddf916f042ae6ce8a994506f3, + type: 3} + preIntegratedFGD_GGXDisneyDiffusePS: {fileID: 4800000, guid: 123f13d52852ef547b2962de4bd9eaad, + type: 3} + preIntegratedFGD_CharlieFabricLambertPS: {fileID: 4800000, guid: 3b3bf235775cf8b4baae7f3306787ab0, + type: 3} + preIntegratedFGD_WardPS: {fileID: 4800000, guid: d279c46a545b0af4f9f0c4fa82cd489e, + type: 3} + preIntegratedFGD_CookTorrancePS: {fileID: 4800000, guid: a6402c19b020b4a4fb7073aaa2e26aba, + type: 3} + preIntegratedFGD_MarschnerPS: {fileID: 4800000, guid: 31f36caf0a5e7f848a1b5328b6ad3eb8, + type: 3} + preIntegratedFiberScatteringCS: {fileID: 7200000, guid: 4a087c9d074552c48aeb85184d56312e, + type: 3} encodeBC6HCS: {fileID: 7200000, guid: aa922d239de60304f964e24488559eeb, type: 3} cubeToPanoPS: {fileID: 4800000, guid: 595434cc3b6405246b6cd3086d0b6f7d, type: 3} - blitCubeTextureFacePS: {fileID: 4800000, guid: d850d0a2481878d4bbf17e5126b04163, type: 3} - filterAreaLightCookiesPS: {fileID: 4800000, guid: c243aac96dda5fa40bed693ed5ba02c4, type: 3} - clearUIntTextureCS: {fileID: 7200000, guid: d067ad4b88af51c498875426894aef76, type: 3} + blitCubeTextureFacePS: {fileID: 4800000, guid: d850d0a2481878d4bbf17e5126b04163, + type: 3} + filterAreaLightCookiesPS: {fileID: 4800000, guid: c243aac96dda5fa40bed693ed5ba02c4, + type: 3} + clearUIntTextureCS: {fileID: 7200000, guid: d067ad4b88af51c498875426894aef76, + type: 3} customPassUtils: {fileID: 4800000, guid: 7e3722d0388000848acb25fd3cc8c088, type: 3} - customPassRenderersUtils: {fileID: 4800000, guid: cef5ba33ee5063d4c8b495d2292e394d, type: 3} + customPassRenderersUtils: {fileID: 4800000, guid: cef5ba33ee5063d4c8b495d2292e394d, + type: 3} texture3DAtlasCS: {fileID: 7200000, guid: 81522e314a83afd4a8ed43bd00757051, type: 3} xrMirrorViewPS: {fileID: 4800000, guid: e6255f98cf405eb45ab6f9006cf11e1f, type: 3} xrOcclusionMeshPS: {fileID: 4800000, guid: 46a45b32bb110604fb36216b63bcdb81, type: 3} shadowClearPS: {fileID: 4800000, guid: e3cab24f27741f44d8af1e94d006267c, type: 3} evsmBlurCS: {fileID: 7200000, guid: fb36979473602464fa32deacb9630c08, type: 3} - debugHDShadowMapPS: {fileID: 4800000, guid: 93d40cc9a6e13994f86f576a624efa18, type: 3} + debugHDShadowMapPS: {fileID: 4800000, guid: 93d40cc9a6e13994f86f576a624efa18, + type: 3} momentShadowsCS: {fileID: 7200000, guid: 4dea53e2ff15ed0448817c2aa4246e53, type: 3} shadowBlitPS: {fileID: 4800000, guid: ca059f1af4587a24b9a9eed3b66cff0f, type: 3} - decalNormalBufferPS: {fileID: 4800000, guid: fd532bf1795188c4daaa66ea798b8b0a, type: 3} + decalNormalBufferPS: {fileID: 4800000, guid: fd532bf1795188c4daaa66ea798b8b0a, + type: 3} GTAOCS: {fileID: 7200000, guid: 6710b06492bd58c4bb8aec0fdc1fced3, type: 3} - GTAOSpatialDenoiseCS: {fileID: 7200000, guid: 2cb33c21587d12b4388d7866ab6c65f6, type: 3} - GTAOTemporalDenoiseCS: {fileID: 7200000, guid: 31e0ca4c210f97c468037d11a5b832bb, type: 3} + GTAOSpatialDenoiseCS: {fileID: 7200000, guid: 2cb33c21587d12b4388d7866ab6c65f6, + type: 3} + GTAOTemporalDenoiseCS: {fileID: 7200000, guid: 31e0ca4c210f97c468037d11a5b832bb, + type: 3} GTAOCopyHistoryCS: {fileID: 7200000, guid: 7f43be57ffd12ff469d4fc175c00c4b4, type: 3} - GTAOBlurAndUpsample: {fileID: 7200000, guid: 9eb1abde882538a4ea46fa23e49ab9fa, type: 3} - screenSpaceGlobalIlluminationCS: {fileID: 7200000, guid: 96170a954eb538b40a5ff369552c3629, type: 3} + GTAOBlurAndUpsample: {fileID: 7200000, guid: 9eb1abde882538a4ea46fa23e49ab9fa, + type: 3} + screenSpaceGlobalIlluminationCS: {fileID: 7200000, guid: 96170a954eb538b40a5ff369552c3629, + type: 3} depthValuesPS: {fileID: 4800000, guid: 6e6a4a3dbb788234594aa74f2d6aeb6f, type: 3} colorResolvePS: {fileID: 4800000, guid: dd7047092f3c82b40b3a07868f9c4de2, type: 3} - resolveMotionVecPS: {fileID: 4800000, guid: ea18ca9826385e943979c46cf98968cc, type: 3} + resolveMotionVecPS: {fileID: 4800000, guid: ea18ca9826385e943979c46cf98968cc, + type: 3} copyAlphaCS: {fileID: 7200000, guid: c2c7eb6611725264187721ef9df0354b, type: 3} nanKillerCS: {fileID: 7200000, guid: 83982f199acf927499576a99abc9bea9, type: 3} exposureCS: {fileID: 7200000, guid: 976d7bce54fae534fb9ec67e9c18570c, type: 3} - histogramExposureCS: {fileID: 7200000, guid: 222da48299136f34b8e3fb75ae9f8ac7, type: 3} + histogramExposureCS: {fileID: 7200000, guid: 222da48299136f34b8e3fb75ae9f8ac7, + type: 3} applyExposureCS: {fileID: 7200000, guid: 1a6fea1dc099b984d8f2b27d504dc096, type: 3} - debugImageHistogramCS: {fileID: 7200000, guid: 52cc17ef5a5ffc443a5c142f9b745a85, type: 3} + debugImageHistogramCS: {fileID: 7200000, guid: 52cc17ef5a5ffc443a5c142f9b745a85, + type: 3} uberPostCS: {fileID: 7200000, guid: f1bf52f7c71bffd4f91e6cd90d12a4f7, type: 3} lutBuilder3DCS: {fileID: 7200000, guid: 37f2b1b0ecd6f1c439e4c1b4f2fdb524, type: 3} - depthOfFieldKernelCS: {fileID: 7200000, guid: 7869415cc3e4eaa4d82ac21a752a2780, type: 3} + depthOfFieldKernelCS: {fileID: 7200000, guid: 7869415cc3e4eaa4d82ac21a752a2780, + type: 3} depthOfFieldCoCCS: {fileID: 7200000, guid: 048b235b54fbfaa4d80ec85ea847d4f8, type: 3} - depthOfFieldCoCReprojectCS: {fileID: 7200000, guid: 4980decaa3878d6448569489f5fc7931, type: 3} - depthOfFieldDilateCS: {fileID: 7200000, guid: 1c93af4338c0c1b42b92464992eebc10, type: 3} + depthOfFieldCoCReprojectCS: {fileID: 7200000, guid: 4980decaa3878d6448569489f5fc7931, + type: 3} + depthOfFieldDilateCS: {fileID: 7200000, guid: 1c93af4338c0c1b42b92464992eebc10, + type: 3} depthOfFieldMipCS: {fileID: 7200000, guid: d3ef53de069ded64e8377cba6eb951fa, type: 3} - depthOfFieldMipSafeCS: {fileID: 7200000, guid: 2d24ee7b2c804d947a5c371c12ed46bd, type: 3} - depthOfFieldPrefilterCS: {fileID: 7200000, guid: f2b89d19910854346b792fe7177ce634, type: 3} - depthOfFieldTileMaxCS: {fileID: 7200000, guid: 84f84585ea8a7a849bea4a581adb93a7, type: 3} - depthOfFieldGatherCS: {fileID: 7200000, guid: 486be52dddc4e054fb10a7b9b78788c2, type: 3} - depthOfFieldCombineCS: {fileID: 7200000, guid: c8049ca85c4c7d047ba28f34d800c663, type: 3} - depthOfFieldPreCombineFarCS: {fileID: 7200000, guid: 3b4a2acd03d1ce2438d93c325d588735, type: 3} - depthOfFieldClearIndirectArgsCS: {fileID: 7200000, guid: 69905045e1d0a65458b205d6ab55502b, type: 3} - paniniProjectionCS: {fileID: 7200000, guid: 0ddbf72c8fbb6e44b983f470c8384ef6, type: 3} - motionBlurMotionVecPrepCS: {fileID: 7200000, guid: ed9438fa777911d48933402087203b15, type: 3} - motionBlurGenTileCS: {fileID: 7200000, guid: 336e1fdbb3a1b8647b06208415f87804, type: 3} - motionBlurMergeTileCS: {fileID: 7200000, guid: cd14ddf849edeed43b0e3ccf66023038, type: 3} - motionBlurNeighborhoodTileCS: {fileID: 7200000, guid: 5ea9865df3e53b448856785b88f8e7b9, type: 3} + depthOfFieldMipSafeCS: {fileID: 7200000, guid: 2d24ee7b2c804d947a5c371c12ed46bd, + type: 3} + depthOfFieldPrefilterCS: {fileID: 7200000, guid: f2b89d19910854346b792fe7177ce634, + type: 3} + depthOfFieldTileMaxCS: {fileID: 7200000, guid: 84f84585ea8a7a849bea4a581adb93a7, + type: 3} + depthOfFieldGatherCS: {fileID: 7200000, guid: 486be52dddc4e054fb10a7b9b78788c2, + type: 3} + depthOfFieldCombineCS: {fileID: 7200000, guid: c8049ca85c4c7d047ba28f34d800c663, + type: 3} + depthOfFieldPreCombineFarCS: {fileID: 7200000, guid: 3b4a2acd03d1ce2438d93c325d588735, + type: 3} + depthOfFieldClearIndirectArgsCS: {fileID: 7200000, guid: 69905045e1d0a65458b205d6ab55502b, + type: 3} + paniniProjectionCS: {fileID: 7200000, guid: 0ddbf72c8fbb6e44b983f470c8384ef6, + type: 3} + motionBlurMotionVecPrepCS: {fileID: 7200000, guid: ed9438fa777911d48933402087203b15, + type: 3} + motionBlurGenTileCS: {fileID: 7200000, guid: 336e1fdbb3a1b8647b06208415f87804, + type: 3} + motionBlurMergeTileCS: {fileID: 7200000, guid: cd14ddf849edeed43b0e3ccf66023038, + type: 3} + motionBlurNeighborhoodTileCS: {fileID: 7200000, guid: 5ea9865df3e53b448856785b88f8e7b9, + type: 3} motionBlurCS: {fileID: 7200000, guid: 2af5c49c7865edb4b823826970ec176a, type: 3} bloomPrefilterCS: {fileID: 7200000, guid: 243b24008041aaa4a91800690f63c684, type: 3} bloomBlurCS: {fileID: 7200000, guid: 133a68380d324de4ea8d3ff8657b02d8, type: 3} @@ -154,17 +228,23 @@ MonoBehaviour: dofGatherCS: {fileID: 7200000, guid: 1e6b16a7970a1494db74b1d3d007d1cc, type: 3} dofCoCMinMaxCS: {fileID: 7200000, guid: c70dd492c3d2fe94589d6ca8d4e37915, type: 3} dofMinMaxDilateCS: {fileID: 7200000, guid: 757a3f81b35177b44b2b178909b49172, type: 3} - contrastAdaptiveSharpenCS: {fileID: 7200000, guid: 560896aec2f412c48995be35551a4ac6, type: 3} - robustContrastAdaptiveSharpenCS: {fileID: 7200000, guid: d907373e407ff65479c449a66c04443d, type: 3} - edgeAdaptiveSpatialUpsamplingCS: {fileID: 7200000, guid: f054fa9fe2c85bb42b9489e2f9ffb643, type: 3} - VTFeedbackDownsample: {fileID: 7200000, guid: 32d963548086c2c439aeb23a93e9a00a, type: 3} + contrastAdaptiveSharpenCS: {fileID: 7200000, guid: 560896aec2f412c48995be35551a4ac6, + type: 3} + robustContrastAdaptiveSharpenCS: {fileID: 7200000, guid: d907373e407ff65479c449a66c04443d, + type: 3} + edgeAdaptiveSpatialUpsamplingCS: {fileID: 7200000, guid: f054fa9fe2c85bb42b9489e2f9ffb643, + type: 3} + VTFeedbackDownsample: {fileID: 7200000, guid: 32d963548086c2c439aeb23a93e9a00a, + type: 3} accumulationCS: {fileID: 7200000, guid: ed80add7a217efa468d137d6f7c668f3, type: 3} alphaInjectionPS: {fileID: 4800000, guid: 4edd96259a5e8b44c90479928f0cd11e, type: 3} chromaKeyingPS: {fileID: 4800000, guid: 49feb6b111e82ec4eb6d3d08e4b6903e, type: 3} customClearPS: {fileID: 4800000, guid: 9cef3686fa32c8840947ed99b561195c, type: 3} - bilateralUpsampleCS: {fileID: 7200000, guid: 68e831c555284d741b985e05369f0e63, type: 3} + bilateralUpsampleCS: {fileID: 7200000, guid: 68e831c555284d741b985e05369f0e63, + type: 3} temporalFilterCS: {fileID: 7200000, guid: 741979ff70f7bd6489fbcb464280ecff, type: 3} diffuseDenoiserCS: {fileID: 7200000, guid: b4ed2382141619f40af1f743a84ccaea, type: 3} + furnaceTestCS: {fileID: 7200000, guid: 9c19385e40e70ea41811a942a46b04a3, type: 3} textures: debugFontTex: {fileID: 2800000, guid: a3ad2df0e49aaa341a3b3a80f93b3f66, type: 3} colorGradient: {fileID: 2800000, guid: 4ea52e665573c1644bf05dd9b11fd2a4, type: 3} @@ -235,18 +315,25 @@ MonoBehaviour: - {fileID: 2800000, guid: 7641a2b116fafd64d9c3d6459fdfe801, type: 3} - {fileID: 2800000, guid: c6a5e40e6746fef4fa486e8f620ee8d4, type: 3} - {fileID: 2800000, guid: fd4189357c6dfb94fa2d36afbce72086, type: 3} - owenScrambledRGBATex: {fileID: 2800000, guid: b0fe077c1ee7d80428f3d8dfa28a027d, type: 3} - owenScrambled256Tex: {fileID: 2800000, guid: 2a205358e67aa9e4a94a128ac9362f4e, type: 3} + owenScrambledRGBATex: {fileID: 2800000, guid: b0fe077c1ee7d80428f3d8dfa28a027d, + type: 3} + owenScrambled256Tex: {fileID: 2800000, guid: 2a205358e67aa9e4a94a128ac9362f4e, + type: 3} scramblingTex: {fileID: 2800000, guid: bf25cd6288e2c8d43854a61a8496a830, type: 3} rankingTile1SPP: {fileID: 2800000, guid: f2fe0251f704c4c478a8063775cffedb, type: 3} - scramblingTile1SPP: {fileID: 2800000, guid: 6185473f62ad3e74da4acac5d482917a, type: 3} + scramblingTile1SPP: {fileID: 2800000, guid: 6185473f62ad3e74da4acac5d482917a, + type: 3} rankingTile8SPP: {fileID: 2800000, guid: af4bd638a4b3eb14781e6441adcdfbb9, type: 3} - scramblingTile8SPP: {fileID: 2800000, guid: 152f8b933250a7b448fc2d4d301b9944, type: 3} + scramblingTile8SPP: {fileID: 2800000, guid: 152f8b933250a7b448fc2d4d301b9944, + type: 3} rankingTile256SPP: {fileID: 2800000, guid: 1e604a266c415cd46b36d97cd9220aa8, type: 3} - scramblingTile256SPP: {fileID: 2800000, guid: 882fb55d7b3e7c94598a318df9376e32, type: 3} - preintegratedAzimuthalScattering: {fileID: 2800000, guid: 4f022cc0bdd8db4428a8faae60acb3dc, type: 3} + scramblingTile256SPP: {fileID: 2800000, guid: 882fb55d7b3e7c94598a318df9376e32, + type: 3} + preintegratedAzimuthalScattering: {fileID: 2800000, guid: 4f022cc0bdd8db4428a8faae60acb3dc, + type: 3} cloudLutRainAO: {fileID: 2800000, guid: e0bcfddf26ed5584ba3d8b94d3200114, type: 3} - worleyNoise128RGBA: {fileID: 11700000, guid: 1fe54a721d0e2504e89f121c723404a8, type: 3} + worleyNoise128RGBA: {fileID: 11700000, guid: 1fe54a721d0e2504e89f121c723404a8, + type: 3} worleyNoise32RGB: {fileID: 11700000, guid: ec156c314a242914dbb706f73ad78cf2, type: 3} perlinNoise32RGB: {fileID: 11700000, guid: d1aae012f8a4f23478471851f17ff915, type: 3} filmGrainTex: @@ -265,11 +352,15 @@ MonoBehaviour: defaultHDRISky: {fileID: 8900000, guid: 8253d41e6e8b11a4cbe77a4f8f82934d, type: 3} defaultCloudMap: {fileID: 2800000, guid: 57a33fc2476a01644865bfde5f06e2f4, type: 3} shaderGraphs: - objectIDPS: {fileID: -6465566751694194690, guid: 89daf81f8e8f6634da726cbca859ca38, type: 3} + objectIDPS: {fileID: -6465566751694194690, guid: 89daf81f8e8f6634da726cbca859ca38, + type: 3} assets: - defaultDiffusionProfile: {fileID: 11400000, guid: 2b7005ba3a4d8474b8cdc34141ad766e, type: 2} - emissiveCylinderMesh: {fileID: 2534964839176971238, guid: accb6d90f0d50fe4ca0f68159b4323de, type: 3} + defaultDiffusionProfile: {fileID: 11400000, guid: 2b7005ba3a4d8474b8cdc34141ad766e, + type: 2} + emissiveCylinderMesh: {fileID: 2534964839176971238, guid: accb6d90f0d50fe4ca0f68159b4323de, + type: 3} emissiveQuadMesh: {fileID: 4300000, guid: 1d5a8595286f94f4bb54171d49f473c3, type: 3} sphereMesh: {fileID: 4300000, guid: 9e0af751bc36ea146940ba245193e28c, type: 3} - probeDebugSphere: {fileID: 532591105809680800, guid: b979359990531a2489ebd7a0ef0f9f01, type: 3} + probeDebugSphere: {fileID: 532591105809680800, guid: b979359990531a2489ebd7a0ef0f9f01, + type: 3} m_Version: 4 diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/Texture/PreintegratedAzimuthalScattering.exr b/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/Texture/PreintegratedAzimuthalScattering.exr deleted file mode 100644 index 0bae7814257..00000000000 Binary files a/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/Texture/PreintegratedAzimuthalScattering.exr and /dev/null differ diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/Texture/PreintegratedAzimuthalScattering.exr.meta b/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/Texture/PreintegratedAzimuthalScattering.exr.meta deleted file mode 100644 index b8ab3721de0..00000000000 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipelineResources/Texture/PreintegratedAzimuthalScattering.exr.meta +++ /dev/null @@ -1,122 +0,0 @@ -fileFormatVersion: 2 -guid: 4f022cc0bdd8db4428a8faae60acb3dc -TextureImporter: - internalIDToNameTable: [] - externalObjects: {} - serializedVersion: 11 - mipmaps: - mipMapMode: 0 - enableMipMap: 0 - sRGBTexture: 0 - linearTexture: 0 - fadeOut: 0 - borderMipMap: 0 - mipMapsPreserveCoverage: 0 - alphaTestReferenceValue: 0.5 - mipMapFadeDistanceStart: 1 - mipMapFadeDistanceEnd: 3 - bumpmap: - convertToNormalMap: 0 - externalNormalMap: 0 - heightScale: 0.25 - normalMapFilter: 0 - isReadable: 0 - streamingMipmaps: 0 - streamingMipmapsPriority: 0 - vTOnly: 0 - ignoreMasterTextureLimit: 0 - grayScaleToAlpha: 0 - generateCubemap: 6 - cubemapConvolution: 0 - seamlessCubemap: 0 - textureFormat: 1 - maxTextureSize: 2048 - textureSettings: - serializedVersion: 2 - filterMode: 1 - aniso: 1 - mipBias: 0 - wrapU: 1 - wrapV: 1 - wrapW: 1 - nPOTScale: 1 - lightmap: 0 - compressionQuality: 50 - spriteMode: 0 - spriteExtrude: 1 - spriteMeshType: 1 - alignment: 0 - spritePivot: {x: 0.5, y: 0.5} - spritePixelsToUnits: 100 - spriteBorder: {x: 0, y: 0, z: 0, w: 0} - spriteGenerateFallbackPhysicsShape: 1 - alphaUsage: 0 - alphaIsTransparency: 0 - spriteTessellationDetail: -1 - textureType: 0 - textureShape: 1 - singleChannelComponent: 0 - flipbookRows: 1 - flipbookColumns: 1 - maxTextureSizeSet: 0 - compressionQualitySet: 0 - textureFormatSet: 0 - ignorePngGamma: 0 - applyGammaDecoding: 0 - platformSettings: - - serializedVersion: 3 - buildTarget: DefaultTexturePlatform - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 0 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - - serializedVersion: 3 - buildTarget: Standalone - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: 72 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 1 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - - serializedVersion: 3 - buildTarget: Server - maxTextureSize: 2048 - resizeAlgorithm: 0 - textureFormat: -1 - textureCompression: 1 - compressionQuality: 50 - crunchedCompression: 0 - allowsAlphaSplitting: 0 - overridden: 0 - androidETC2FallbackOverride: 0 - forceMaximumCompressionQuality_BC6H_BC7: 0 - spriteSheet: - serializedVersion: 2 - sprites: [] - outline: [] - physicsShape: [] - bones: [] - spriteID: - internalID: 0 - vertices: [] - indices: - edges: [] - weights: [] - secondaryTextures: [] - nameFileIdTable: {} - spritePackingTag: - pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Tests/Editor/FurnaceTests.cs b/com.unity.render-pipelines.high-definition/Tests/Editor/FurnaceTests.cs new file mode 100644 index 00000000000..f3bcd47201d --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Tests/Editor/FurnaceTests.cs @@ -0,0 +1,88 @@ +using System; +using NUnit.Framework; +using UnityEditor.TestTools.TestRunner; +using UnityEditorInternal; + +namespace UnityEngine.Rendering.HighDefinition.Tests +{ + static class FurnaceTestIDs + { + public static readonly int _TestResult = Shader.PropertyToID("_TestResult"); + public static readonly int _OutgoingDirection = Shader.PropertyToID("_OutgoingDirection"); + public static readonly int _BetaM = Shader.PropertyToID("_BetaM"); + public static readonly int _BetaN = Shader.PropertyToID("_BetaN"); + } + + + class FurnaceTests + { + private const int SAMPLE_COUNT = 10000000; + private const int GROUP_SIZE = 512; + private const int NUM_GROUPS = (SAMPLE_COUNT + GROUP_SIZE - 1) / GROUP_SIZE; + + float InvFourPI() => 1 / (4f * Mathf.PI); + + // [Test] + public void FurnaceTestHairReference() + { + var hdrp = RenderPipelineManager.currentPipeline as HDRenderPipeline; + var furnaceTestCS = hdrp.defaultResources.shaders.furnaceTestCS; + + var cmd = CommandBufferPool.Get(); + var buffer = new ComputeBuffer(NUM_GROUPS, sizeof(float)); + + void Cleanup() + { + // Note: This won't be release on assertion failure. + CommandBufferPool.Release(cmd); + CoreUtils.SafeRelease(buffer); + } + + // TODO: Investigate why this is so unstable for low variances (even at 10 million samples). + for (float betaM = 0.4f; betaM < 1.0f; betaM += 0.2f) + { + for (float betaN = 0.4f; betaN < 1.0f; betaN += 0.2f) + { + cmd.SetComputeVectorParam(furnaceTestCS, FurnaceTestIDs._OutgoingDirection, Random.onUnitSphere.normalized); + cmd.SetComputeFloatParam(furnaceTestCS, FurnaceTestIDs._BetaM, betaM); + cmd.SetComputeFloatParam(furnaceTestCS, FurnaceTestIDs._BetaN, betaN); + cmd.SetComputeBufferParam(furnaceTestCS, 0, FurnaceTestIDs._TestResult, buffer); + + cmd.DispatchCompute(furnaceTestCS, 0, NUM_GROUPS, 1, 1); + Graphics.ExecuteCommandBuffer(cmd); + + // Read back the test result. + float[] F = new float[NUM_GROUPS]; + buffer.GetData(F); + + float F_Avg = 0; + + for (int i = 0; i < NUM_GROUPS; ++i) + { + F_Avg += F[i]; + } + + F_Avg /= SAMPLE_COUNT * InvFourPI(); + + // The reflected uniform radiance for a non-absorbing fiber should be within ~5% of energy conserving. + const float kErrorThreshold = 0.05f; + + try + { + Assert.That(F_Avg >= 1f - kErrorThreshold && + F_Avg <= 1f + kErrorThreshold, + $"Expected result within { kErrorThreshold * 100f }% error, got { Math.Round(Mathf.Abs(1f - F_Avg) * 100f, 2) }%" + + $" for Beta M: { betaM} Beta N { betaN }."); + } + catch (AssertionException) + { + Cleanup(); + throw; + } + } + } + + Cleanup(); + } + } +} diff --git a/com.unity.render-pipelines.high-definition/Tests/Editor/FurnaceTests.cs.meta b/com.unity.render-pipelines.high-definition/Tests/Editor/FurnaceTests.cs.meta new file mode 100644 index 00000000000..cd34eb010b0 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Tests/Editor/FurnaceTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4e4af621d8aa74b40a85a5894b022d4e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.render-pipelines.high-definition/Tests/Editor/Utilities/FurnaceTests.compute b/com.unity.render-pipelines.high-definition/Tests/Editor/Utilities/FurnaceTests.compute new file mode 100644 index 00000000000..b2b5557ec46 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Tests/Editor/Utilities/FurnaceTests.compute @@ -0,0 +1,109 @@ +#pragma kernel FurnaceTest +#pragma kernel FurnaceTestSampled + +// #pragma enable_d3d11_debug_symbols + +// HDRP generic includes +#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/Sampling/Hammersley.hlsl" +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Sampling.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/Lighting.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Material.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoopDef.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Hair.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Hair/Reference/HairReference.hlsl" +#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStencilUsage.cs.hlsl" + +RWStructuredBuffer _TestResult; + +float3 _OutgoingDirection; +float _BetaM; +float _BetaN; + +#define SAMPLE_COUNT 10000000 +#define GROUP_SIZE 512 + +// Local memory for parallel reduction. +groupshared float gs_F[GROUP_SIZE]; + +float SampleHair(uint2 seed) +{ + // Configure the theoretical fiber + BSDFData bsdfData; + ZERO_INITIALIZE(BSDFData, bsdfData); + + // Randomize H for this sample. + bsdfData.h = -1 + 2 * GenerateHashedRandomFloat(seed.y); + + // Override the absorption. (All light should reflect or transmit as no absorption occur). + bsdfData.absorption = 0; + + // Configure the roughnesses for the current test. + bsdfData.perceptualRoughness = _BetaM; + bsdfData.perceptualRoughnessRadial = _BetaN; + + float2 U = Hammersley2d(seed.x, SAMPLE_COUNT); + float3 wi = SampleSphereUniform(U.x, U.y); + float3 wo = _OutgoingDirection; + + // Invoke the reference fiber scattering function. + CBSDF cbsdf = EvaluateHairReference(wo, wi, bsdfData); + + return Luminance(cbsdf.specR) * abs(wi.z); +} + +[numthreads(GROUP_SIZE, 1, 1)] +void FurnaceTest(uint dispatchThreadId : SV_DispatchThreadID, + uint groupThreadId : SV_GroupThreadID, + uint groupId : SV_GroupID) +{ + // Sample the hair for this thread, generating a unique incident direction and H value. + gs_F[groupThreadId] = SampleHair(uint2(dispatchThreadId, groupThreadId)); + + GroupMemoryBarrierWithGroupSync(); + + // Simple parallel reduction on the group. + for (uint i = GROUP_SIZE / 2; i > 0; i >>= 1) + { + if (groupThreadId < i) + { + gs_F[groupThreadId] += gs_F[groupThreadId + i]; + } + + GroupMemoryBarrierWithGroupSync(); + } + + if (groupThreadId == 0) + { + _TestResult[groupId] = gs_F[0]; + } +} + +[numthreads(GROUP_SIZE, 1, 1)] +void FurnaceTestSampled(uint dispatchThreadId : SV_DispatchThreadID, + uint groupThreadId : SV_GroupThreadID, + uint groupId : SV_GroupID) +{ + // Sample the hair for this thread, generating a unique incident direction and H value. + gs_F[groupThreadId] = SampleHair(uint2(dispatchThreadId, groupThreadId)); + + GroupMemoryBarrierWithGroupSync(); + + // Simple parallel reduction on the group. + for (uint i = GROUP_SIZE / 2; i > 0; i >>= 1) + { + if (groupThreadId < i) + { + gs_F[groupThreadId] += gs_F[groupThreadId + i]; + } + + GroupMemoryBarrierWithGroupSync(); + } + + if (groupThreadId == 0) + { + _TestResult[groupId] = gs_F[0]; + } +} diff --git a/com.unity.render-pipelines.high-definition/Tests/Editor/Utilities/FurnaceTests.compute.meta b/com.unity.render-pipelines.high-definition/Tests/Editor/Utilities/FurnaceTests.compute.meta new file mode 100644 index 00000000000..d462c746526 --- /dev/null +++ b/com.unity.render-pipelines.high-definition/Tests/Editor/Utilities/FurnaceTests.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9c19385e40e70ea41811a942a46b04a3 +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: