From 65ed17a4b61a6ba3800a5c255e4c1f46bed3c36c Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 21 Oct 2026 19:02:51 -0700 Subject: [PATCH] insert: add an opt-in Traced mode to the Insert Hybrid shader With the _INSERT_TRACED keyword on, the body glow comes from an offline path-traced render of the insert (four view elevations, one per channel) instead of the optical response map. The bulb image and the facet glints are drawn live through a facet map from the same render, and an optional lamp-off render of the insert under the room replaces the live unlit body. With the keyword off, the material is unchanged. The controls and their tooltips live once in InsertTracedFields, for both pipelines' inspectors. The lamp-off brightness is the global _InsertTracedOffEv (stops), set by the table when it loads. Co-Authored-By: Claude Opus 5.5 --- .../Srp/Insert/Insert Hybrid.shadergraph | 1789 ++++++++++++++++- .../Shaders/Srp/Insert/InsertTraced.hlsl | 344 ++++ .../Shaders/Srp/Insert/InsertTraced.hlsl.meta | 7 + .../editor/inserts/traced-mode.md | 61 + .../Documentation~/creators-guide/toc.yml | 2 + .../VPT/Insert/InsertTracedFields.cs | 98 + .../VPT/Insert/InsertTracedFields.cs.meta | 2 + .../VPT/Insert/InsertPresetTests.cs | 6 + .../VPT/Insert/InsertMaterialPreset.cs | 10 +- 9 files changed, 2317 insertions(+), 2 deletions(-) create mode 100644 VisualPinball.Unity/Assets/Shaders/Srp/Insert/InsertTraced.hlsl create mode 100644 VisualPinball.Unity/Assets/Shaders/Srp/Insert/InsertTraced.hlsl.meta create mode 100644 VisualPinball.Unity/Documentation~/creators-guide/editor/inserts/traced-mode.md create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Insert/InsertTracedFields.cs create mode 100644 VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Insert/InsertTracedFields.cs.meta diff --git a/VisualPinball.Unity/Assets/Shaders/Srp/Insert/Insert Hybrid.shadergraph b/VisualPinball.Unity/Assets/Shaders/Srp/Insert/Insert Hybrid.shadergraph index c03e7a2142..aa36442eac 100644 --- a/VisualPinball.Unity/Assets/Shaders/Srp/Insert/Insert Hybrid.shadergraph +++ b/VisualPinball.Unity/Assets/Shaders/Srp/Insert/Insert Hybrid.shadergraph @@ -137,13 +137,86 @@ }, { "m_Id": "7b66c53699fb4e329b7b4ff523715868" + }, + { + "m_Id": "1ab3da5051a0d1c6e5f0519971b60ef6" + }, + { + "m_Id": "246a19b8617d6353878b042dba3edfa9" + }, + { + "m_Id": "2d8f58c882f2e08389909792c9f88d23" + }, + { + "m_Id": "c3444c3fc925497db883a15827a43589" + }, + { + "m_Id": "10e4bc09379ed79ddc0f6619b6d9d1c8" + }, + { + "m_Id": "d68f689dab76223a0dfebaaee8825756" + }, + { + "m_Id": "8cc0c881da45e9c5aad4fa94a3458314" + }, + { + "m_Id": "e685756294aebe0a2042a320ceaef1c7" + }, + { + "m_Id": "93493113bf60b372596ff9f87b19c39f" + }, + { + "m_Id": "a7e88bbf81ea878a5bfca0d53127868d" + }, + { + "m_Id": "ec2a73e27a54ba0ec440ee8d928f6d3d" + }, + { + "m_Id": "8e98c91ae76bd582369a6d906aff8030" + }, + { + "m_Id": "f800388fa7e319666ba126cc07c34a87" + }, + { + "m_Id": "c77f36232947c0fa1b50f51a037a2c8e" + }, + { + "m_Id": "5be65e4aa5f5cfeea6414ec5b226b082" + }, + { + "m_Id": "b9656fb097620e803efdb15cb2efc780" + }, + { + "m_Id": "8411184b546bf7d362d0d0c163ca46df" + }, + { + "m_Id": "0e95e1b96c13f33ae2721e85ecb9d530" + }, + { + "m_Id": "6ac5ec06804b45260359f4c1a0b29d4a" + }, + { + "m_Id": "1c5c03b10654a8a82a757f9188537160" + }, + { + "m_Id": "89e334fe03f678311fcd55cd21cea726" + }, + { + "m_Id": "f9813472e37bc7a9fd95a3f5b27dd8c8" + } + ], + "m_Keywords": [ + { + "m_Id": "01e3ad468bb024c540d12fb1d82e3f39" } ], - "m_Keywords": [], "m_Dropdowns": [], "m_CategoryData": [ { "m_Id": "efcb6c45f55553f89edd4456c2a91dad" + }, + { + "m_Id": "dfaf5502e7f3329878a55221a27171ef" } ], "m_Nodes": [ @@ -329,6 +402,12 @@ }, { "m_Id": "fb36717fa96f4d248390595746a431e8" + }, + { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" } ], "m_GroupDatas": [], @@ -341,6 +420,20 @@ }, "m_SlotId": 24 }, + "m_InputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 2 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 8 + }, "m_InputSlot": { "m_Node": { "m_Id": "f1c30112c2ac57528b85dd7db4a02b19" @@ -355,6 +448,20 @@ }, "m_SlotId": 25 }, + "m_InputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 3 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 9 + }, "m_InputSlot": { "m_Node": { "m_Id": "af8e062f44565d54b5f377c2829f4bf3" @@ -369,6 +476,20 @@ }, "m_SlotId": 26 }, + "m_InputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 4 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 10 + }, "m_InputSlot": { "m_Node": { "m_Id": "8b2ba8d4c746548e8f3a638475ef2a12" @@ -509,6 +630,20 @@ }, "m_SlotId": 0 }, + "m_InputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 3 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 10 + }, "m_InputSlot": { "m_Node": { "m_Id": "eb08b67e7dc45dba82c148f72e5dd340" @@ -523,6 +658,20 @@ }, "m_SlotId": 0 }, + "m_InputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 4 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 11 + }, "m_InputSlot": { "m_Node": { "m_Id": "eb08b67e7dc45dba82c148f72e5dd340" @@ -537,6 +686,20 @@ }, "m_SlotId": 0 }, + "m_InputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 5 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 12 + }, "m_InputSlot": { "m_Node": { "m_Id": "eb08b67e7dc45dba82c148f72e5dd340" @@ -551,6 +714,20 @@ }, "m_SlotId": 0 }, + "m_InputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 6 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 13 + }, "m_InputSlot": { "m_Node": { "m_Id": "eb08b67e7dc45dba82c148f72e5dd340" @@ -579,6 +756,20 @@ }, "m_SlotId": 0 }, + "m_InputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 0 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 7 + }, "m_InputSlot": { "m_Node": { "m_Id": "eb08b67e7dc45dba82c148f72e5dd340" @@ -593,6 +784,20 @@ }, "m_SlotId": 0 }, + "m_InputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 1 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 8 + }, "m_InputSlot": { "m_Node": { "m_Id": "eb08b67e7dc45dba82c148f72e5dd340" @@ -607,6 +812,20 @@ }, "m_SlotId": 0 }, + "m_InputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 2 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "02e57ae19a1515b27dafaf83b4ab8504" + }, + "m_SlotId": 9 + }, "m_InputSlot": { "m_Node": { "m_Id": "eb08b67e7dc45dba82c148f72e5dd340" @@ -1089,6 +1308,76 @@ }, "m_SlotId": 52 } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "b57bfa9c48e8548695944203dd7264fa" + }, + "m_SlotId": 0 + }, + "m_InputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 0 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "77adde5a503255bc8730b099ed03c298" + }, + "m_SlotId": 0 + }, + "m_InputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 1 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "5774dd3dc28e51158e12894808bccd68" + }, + "m_SlotId": 0 + }, + "m_InputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 5 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "181b1e1da978581b981f173bd21e7ed6" + }, + "m_SlotId": 0 + }, + "m_InputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 6 + } + }, + { + "m_OutputSlot": { + "m_Node": { + "m_Id": "6d910a0a0fbc5419b374f6d76193b271" + }, + "m_SlotId": 0 + }, + "m_InputSlot": { + "m_Node": { + "m_Id": "59fb352cd903dc1e10534321a8adf4d6" + }, + "m_SlotId": 7 + } } ], "m_VertexContext": { @@ -7352,3 +7641,1501 @@ "m_DefaultValue": 0.013020833333333334, "m_Labels": [] } + +{ + "m_SGVersion": 1, + "m_Type": "UnityEditor.ShaderGraph.ShaderKeyword", + "m_ObjectId": "01e3ad468bb024c540d12fb1d82e3f39", + "m_Guid": { + "m_GuidSerialized": "01e3ad46-8bb0-24c5-40d1-2fb1d82e3f39" + }, + "m_Name": "Traced Mode", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Traced Mode", + "m_DefaultReferenceName": "_TRACED_MODE", + "m_OverrideReferenceName": "_INSERT_TRACED", + "m_KeywordType": 0, + "m_KeywordDefinition": 1, + "m_KeywordScope": 0, + "m_KeywordStages": 2, + "m_Entries": [], + "m_Value": 0, + "m_IsEditable": true +} + +{ + "m_SGVersion": 0, + "m_ObjectId": "1ab3da5051a0d1c6e5f0519971b60ef6", + "m_Guid": { + "m_GuidSerialized": "1ab3da50-51a0-d1c6-e5f0-519971b60ef6" + }, + "m_Name": "Traced Light", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Traced Light", + "m_DefaultReferenceName": "_TracedLight", + "m_OverrideReferenceName": "_TracedLight", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Texture2DShaderProperty", + "m_Value": { + "m_SerializedTexture": "", + "m_Guid": "" + }, + "isMainTexture": false, + "useTilingAndOffset": false, + "useTexelSize": false, + "m_Modifiable": true, + "m_DefaultType": 1 +} + +{ + "m_SGVersion": 0, + "m_ObjectId": "246a19b8617d6353878b042dba3edfa9", + "m_Guid": { + "m_GuidSerialized": "246a19b8-617d-6353-878b-042dba3edfa9" + }, + "m_Name": "Traced Facets", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Traced Facets", + "m_DefaultReferenceName": "_TracedFacets", + "m_OverrideReferenceName": "_TracedFacets", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Texture2DShaderProperty", + "m_Value": { + "m_SerializedTexture": "", + "m_Guid": "" + }, + "isMainTexture": false, + "useTilingAndOffset": false, + "useTexelSize": true, + "m_Modifiable": true, + "m_DefaultType": 2 +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "2d8f58c882f2e08389909792c9f88d23", + "m_Guid": { + "m_GuidSerialized": "2d8f58c8-82f2-e083-8990-9792c9f88d23" + }, + "m_Name": "Traced Frame (centre x, z m, turn deg, size m)", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Traced Frame (centre x, z m, turn deg, size m)", + "m_DefaultReferenceName": "_TracedFrame", + "m_OverrideReferenceName": "_TracedFrame", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector4ShaderProperty", + "m_Value": { + "x": 0, + "y": 0, + "z": 0, + "w": 0.05 + } +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "c3444c3fc925497db883a15827a43589", + "m_Guid": { + "m_GuidSerialized": "c3444c3f-c925-497d-b883-a15827a43589" + }, + "m_Name": "Traced Elevations (deg)", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Traced Elevations (deg)", + "m_DefaultReferenceName": "_TracedAngles", + "m_OverrideReferenceName": "_TracedAngles", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector4ShaderProperty", + "m_Value": { + "x": 15, + "y": 45, + "z": 60, + "w": 70 + } +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "10e4bc09379ed79ddc0f6619b6d9d1c8", + "m_Guid": { + "m_GuidSerialized": "10e4bc09-379e-d79d-dc0f-6619b6d9d1c8" + }, + "m_Name": "Traced Geometry (cover, facets, interior, rim fade mm)", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Traced Geometry (cover, facets, interior, rim fade mm)", + "m_DefaultReferenceName": "_TracedGeometry", + "m_OverrideReferenceName": "_TracedGeometry", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector4ShaderProperty", + "m_Value": { + "x": 4, + "y": 2.5, + "z": 3.5, + "w": 1.5 + } +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "d68f689dab76223a0dfebaaee8825756", + "m_Guid": { + "m_GuidSerialized": "d68f689d-ab76-223a-0dfe-baaee8825756" + }, + "m_Name": "Traced Glow", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Traced Glow", + "m_DefaultReferenceName": "_TracedGlow", + "m_OverrideReferenceName": "_TracedGlow", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 1.0, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 4.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "8cc0c881da45e9c5aad4fa94a3458314", + "m_Guid": { + "m_GuidSerialized": "8cc0c881-da45-e9c5-aad4-fa94a3458314" + }, + "m_Name": "Glow Spread", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Glow Spread", + "m_DefaultReferenceName": "_TracedGlowSpread", + "m_OverrideReferenceName": "_TracedGlowSpread", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 1.0, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.25, + "y": 3.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "e685756294aebe0a2042a320ceaef1c7", + "m_Guid": { + "m_GuidSerialized": "e6857562-94ae-be0a-2042-a320ceaef1c7" + }, + "m_Name": "Live Glow Mix", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Live Glow Mix", + "m_DefaultReferenceName": "_TracedLiveMix", + "m_OverrideReferenceName": "_TracedLiveMix", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 0.0, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 1.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "93493113bf60b372596ff9f87b19c39f", + "m_Guid": { + "m_GuidSerialized": "93493113-bf60-b372-596f-f9f87b19c39f" + }, + "m_Name": "Glint", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Glint", + "m_DefaultReferenceName": "_TracedGlint", + "m_OverrideReferenceName": "_TracedGlint", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 1.5, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 40.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "a7e88bbf81ea878a5bfca0d53127868d", + "m_Guid": { + "m_GuidSerialized": "a7e88bbf-81ea-878a-5bfc-a0d53127868d" + }, + "m_Name": "Glint Shape (tilt deg, sharpness, keys, fringe deg)", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Glint Shape (tilt deg, sharpness, keys, fringe deg)", + "m_DefaultReferenceName": "_TracedGlintParams", + "m_OverrideReferenceName": "_TracedGlintParams", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector4ShaderProperty", + "m_Value": { + "x": 53, + "y": 80, + "z": 5, + "w": 2.5 + } +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "ec2a73e27a54ba0ec440ee8d928f6d3d", + "m_Guid": { + "m_GuidSerialized": "ec2a73e2-7a54-ba0e-c440-ee8d928f6d3d" + }, + "m_Name": "Glint Turn (deg)", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Glint Turn (deg)", + "m_DefaultReferenceName": "_TracedGlintRot", + "m_OverrideReferenceName": "_TracedGlintRot", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 0.0, + "m_FloatType": 0, + "m_RangeValues": { + "x": 0.0, + "y": 1.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "8e98c91ae76bd582369a6d906aff8030", + "m_Guid": { + "m_GuidSerialized": "8e98c91a-e76b-d582-369a-6d906aff8030" + }, + "m_Name": "Glint Organic (waviness deg, per-insert turn deg, wave mm, -)", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Glint Organic (waviness deg, per-insert turn deg, wave mm, -)", + "m_DefaultReferenceName": "_TracedGlintOrganic", + "m_OverrideReferenceName": "_TracedGlintOrganic", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector4ShaderProperty", + "m_Value": { + "x": 1.3, + "y": 17, + "z": 19, + "w": 0 + } +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "f800388fa7e319666ba126cc07c34a87", + "m_Guid": { + "m_GuidSerialized": "f800388f-a7e3-1966-6ba1-26cc07c34a87" + }, + "m_Name": "Bulb Through Traced Facets", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Bulb Through Traced Facets", + "m_DefaultReferenceName": "_TracedBulbSource", + "m_OverrideReferenceName": "_TracedBulbSource", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 1.0, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 1.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "c77f36232947c0fa1b50f51a037a2c8e", + "m_Guid": { + "m_GuidSerialized": "c77f3623-2947-c0fa-1b50-f51a037a2c8e" + }, + "m_Name": "Bulb Gain", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Bulb Gain", + "m_DefaultReferenceName": "_TracedBulbGain", + "m_OverrideReferenceName": "_TracedBulbGain", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 1.0, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 4.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "5be65e4aa5f5cfeea6414ec5b226b082", + "m_Guid": { + "m_GuidSerialized": "5be65e4a-a5f5-cfee-a641-4ec5b226b082" + }, + "m_Name": "Bulb Depth", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Bulb Depth", + "m_DefaultReferenceName": "_TracedBulbDepth", + "m_OverrideReferenceName": "_TracedBulbDepth", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 1.0, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 2.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "b9656fb097620e803efdb15cb2efc780", + "m_Guid": { + "m_GuidSerialized": "b9656fb0-9762-0e80-3efd-b15cb2efc780" + }, + "m_Name": "Bulb Halo", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Bulb Halo", + "m_DefaultReferenceName": "_TracedBulbHalo", + "m_OverrideReferenceName": "_TracedBulbHalo", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 0.5, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 1.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 0, + "m_ObjectId": "8411184b546bf7d362d0d0c163ca46df", + "m_Guid": { + "m_GuidSerialized": "8411184b-546b-f7d3-62d0-d0c163ca46df" + }, + "m_Name": "Lamp-Off Env", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Lamp-Off Env", + "m_DefaultReferenceName": "_TracedEnv", + "m_OverrideReferenceName": "_TracedEnv", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Texture2DShaderProperty", + "m_Value": { + "m_SerializedTexture": "", + "m_Guid": "" + }, + "isMainTexture": false, + "useTilingAndOffset": false, + "useTexelSize": true, + "m_Modifiable": true, + "m_DefaultType": 1 +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "0e95e1b96c13f33ae2721e85ecb9d530", + "m_Guid": { + "m_GuidSerialized": "0e95e1b9-6c13-f33a-e272-1e85ecb9d530" + }, + "m_Name": "Lamp-Off Look", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Lamp-Off Look", + "m_DefaultReferenceName": "_TracedEnvGain", + "m_OverrideReferenceName": "_TracedEnvGain", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 0.0, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 2.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "6ac5ec06804b45260359f4c1a0b29d4a", + "m_Guid": { + "m_GuidSerialized": "6ac5ec06-804b-4526-0359-f4c1a0b29d4a" + }, + "m_Name": "Lamp-Off Vary (elevation deg, brightness frac, wobble texels, -)", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Lamp-Off Vary (elevation deg, brightness frac, wobble texels, -)", + "m_DefaultReferenceName": "_TracedEnvVary", + "m_OverrideReferenceName": "_TracedEnvVary", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector4ShaderProperty", + "m_Value": { + "x": 4, + "y": 0.12, + "z": 1.5, + "w": 0 + } +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "1c5c03b10654a8a82a757f9188537160", + "m_Guid": { + "m_GuidSerialized": "1c5c03b1-0654-a8a8-2a75-7f9188537160" + }, + "m_Name": "Lamp-Off Glints", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Lamp-Off Glints", + "m_DefaultReferenceName": "_TracedOffGlint", + "m_OverrideReferenceName": "_TracedOffGlint", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 1.0, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 8.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "89e334fe03f678311fcd55cd21cea726", + "m_Guid": { + "m_GuidSerialized": "89e334fe-03f6-7831-1fcd-55cd21cea726" + }, + "m_Name": "Mesh Facet Normals", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Mesh Facet Normals", + "m_DefaultReferenceName": "_TracedMeshNormals", + "m_OverrideReferenceName": "_TracedMeshNormals", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 1.0, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 1.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 1, + "m_ObjectId": "f9813472e37bc7a9fd95a3f5b27dd8c8", + "m_Guid": { + "m_GuidSerialized": "f9813472-e37b-c7a9-fd95-a3f5b27dd8c8" + }, + "m_Name": "Traced Debug", + "m_DefaultRefNameVersion": 1, + "m_GeneratePropertyBlock": true, + "m_UseCustomSlotLabel": false, + "m_CustomSlotLabel": "", + "m_DismissedVersion": 0, + "m_RefNameGeneratedByDisplayName": "Traced Debug", + "m_DefaultReferenceName": "_TracedDebug", + "m_OverrideReferenceName": "_TracedDebug", + "m_Precision": 0, + "overrideHLSLDeclaration": false, + "hlslDeclarationOverride": 0, + "m_Hidden": false, + "m_Type": "UnityEditor.ShaderGraph.Internal.Vector1ShaderProperty", + "m_Value": 0.0, + "m_FloatType": 1, + "m_RangeValues": { + "x": 0.0, + "y": 1.0 + }, + "m_SliderType": 0, + "m_SliderPower": 3.0, + "m_EnumType": 0, + "m_CSharpEnumString": "", + "m_EnumNames": [], + "m_EnumValues": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "fe2aaf63423f57a4765fb2f37c9c0665", + "m_Id": 0, + "m_DisplayName": "Scatter", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Scatter", + "m_StageCapability": 3, + "m_Value": 0.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "83e7071405af1830bbf748a660d81f47", + "m_Id": 1, + "m_DisplayName": "EdgeGlow", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "EdgeGlow", + "m_StageCapability": 3, + "m_Value": 0.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "79a81c1fbf9c6492e7ad5eaa1b41f911", + "m_Id": 2, + "m_DisplayName": "LampGain", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "LampGain", + "m_StageCapability": 3, + "m_Value": 0.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector4MaterialSlot", + "m_ObjectId": "5a4d09befc7c165aa7a1297f5be9d304", + "m_Id": 3, + "m_DisplayName": "Lamp", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Lamp", + "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.Vector4MaterialSlot", + "m_ObjectId": "a98761b41752348cac69b0bca9e6baec", + "m_Id": 4, + "m_DisplayName": "LampB", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "LampB", + "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.Vector4MaterialSlot", + "m_ObjectId": "57f8d592e6530a30dab7389ded344713", + "m_Id": 5, + "m_DisplayName": "LampC", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "LampC", + "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.Vector4MaterialSlot", + "m_ObjectId": "9c1650cc3a3ee21757d5e86500a485be", + "m_Id": 6, + "m_DisplayName": "LampD", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "LampD", + "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.Vector1MaterialSlot", + "m_ObjectId": "314cd6f5e17aec3521a2d9a8bdf61d37", + "m_Id": 7, + "m_DisplayName": "ScatterOut", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "ScatterOut", + "m_StageCapability": 3, + "m_Value": 0.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "cf2d6e2612081e72d2a3c7014f150de6", + "m_Id": 8, + "m_DisplayName": "EdgeGlowOut", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "EdgeGlowOut", + "m_StageCapability": 3, + "m_Value": 0.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "591cd5781926862ef4ae7a0d324d9e1d", + "m_Id": 9, + "m_DisplayName": "LampGainOut", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "LampGainOut", + "m_StageCapability": 3, + "m_Value": 0.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector4MaterialSlot", + "m_ObjectId": "e316c99de6c680f70fcfabf063ee33b7", + "m_Id": 10, + "m_DisplayName": "LampOut", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "LampOut", + "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.Vector4MaterialSlot", + "m_ObjectId": "95943bb09c2e117db621e7f547789a51", + "m_Id": 11, + "m_DisplayName": "LampBOut", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "LampBOut", + "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.Vector4MaterialSlot", + "m_ObjectId": "20a2823cfb2139c4970051537a93d809", + "m_Id": 12, + "m_DisplayName": "LampCOut", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "LampCOut", + "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.Vector4MaterialSlot", + "m_ObjectId": "93981c87d8b3084c37c8de6d0aa80b3b", + "m_Id": 13, + "m_DisplayName": "LampDOut", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "LampDOut", + "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": 1, + "m_Type": "UnityEditor.ShaderGraph.CustomFunctionNode", + "m_ObjectId": "02e57ae19a1515b27dafaf83b4ab8504", + "m_Group": { + "m_Id": "" + }, + "m_Name": "Insert Traced Pre", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": -140, + "y": -180, + "width": 260, + "height": 200 + } + }, + "m_Slots": [ + { + "m_Id": "fe2aaf63423f57a4765fb2f37c9c0665" + }, + { + "m_Id": "83e7071405af1830bbf748a660d81f47" + }, + { + "m_Id": "79a81c1fbf9c6492e7ad5eaa1b41f911" + }, + { + "m_Id": "5a4d09befc7c165aa7a1297f5be9d304" + }, + { + "m_Id": "a98761b41752348cac69b0bca9e6baec" + }, + { + "m_Id": "57f8d592e6530a30dab7389ded344713" + }, + { + "m_Id": "9c1650cc3a3ee21757d5e86500a485be" + }, + { + "m_Id": "314cd6f5e17aec3521a2d9a8bdf61d37" + }, + { + "m_Id": "cf2d6e2612081e72d2a3c7014f150de6" + }, + { + "m_Id": "591cd5781926862ef4ae7a0d324d9e1d" + }, + { + "m_Id": "e316c99de6c680f70fcfabf063ee33b7" + }, + { + "m_Id": "95943bb09c2e117db621e7f547789a51" + }, + { + "m_Id": "20a2823cfb2139c4970051537a93d809" + }, + { + "m_Id": "93981c87d8b3084c37c8de6d0aa80b3b" + } + ], + "synonyms": [ + "code", + "HLSL" + ], + "m_Precision": 1, + "m_PreviewExpanded": false, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SourceType": 0, + "m_FunctionName": "InsertTracedPre", + "m_FunctionSource": "e4772f4ce883a7f5a91b32fd5726e178", + "m_FunctionBody": "" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "20d6427753cb4e0186916aa827785c77", + "m_Id": 0, + "m_DisplayName": "Position", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Position", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "69abcd71505e7ca8c2d9d2b4fdbd0734", + "m_Id": 1, + "m_DisplayName": "View", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "View", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "2f801b828d8b597965c9c1e84089b475", + "m_Id": 2, + "m_DisplayName": "BaseColorIn", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "BaseColorIn", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "c6c3b272efffe684a09c5b05032213e4", + "m_Id": 3, + "m_DisplayName": "NormalIn", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "NormalIn", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "794c067dd3ea6c114703d2798603ee34", + "m_Id": 4, + "m_DisplayName": "EmissionIn", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "EmissionIn", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "82b4b5d5ca887d545a93b2ebc5420a2f", + "m_Id": 5, + "m_DisplayName": "WorldNormal", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "WorldNormal", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "9dc1264025075d3f6c4edfb544ddffba", + "m_Id": 6, + "m_DisplayName": "WorldTangent", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "WorldTangent", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "d50151422f1f740cb7f05629b52105c6", + "m_Id": 7, + "m_DisplayName": "WorldBitangent", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "WorldBitangent", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "ba596df8592ff559a1656a8271092c00", + "m_Id": 8, + "m_DisplayName": "BaseColor", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "BaseColor", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "5f82df1a874a453cb6cc062b147667d1", + "m_Id": 9, + "m_DisplayName": "NormalTS", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "NormalTS", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot", + "m_ObjectId": "19ad4fbe0507355cec97d7c22e75e67f", + "m_Id": 10, + "m_DisplayName": "Emission", + "m_SlotType": 1, + "m_Hidden": false, + "m_ShaderOutputName": "Emission", + "m_StageCapability": 3, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [] +} + +{ + "m_SGVersion": 1, + "m_Type": "UnityEditor.ShaderGraph.CustomFunctionNode", + "m_ObjectId": "59fb352cd903dc1e10534321a8adf4d6", + "m_Group": { + "m_Id": "" + }, + "m_Name": "Insert Traced", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 600, + "y": 120, + "width": 260, + "height": 200 + } + }, + "m_Slots": [ + { + "m_Id": "20d6427753cb4e0186916aa827785c77" + }, + { + "m_Id": "69abcd71505e7ca8c2d9d2b4fdbd0734" + }, + { + "m_Id": "2f801b828d8b597965c9c1e84089b475" + }, + { + "m_Id": "c6c3b272efffe684a09c5b05032213e4" + }, + { + "m_Id": "794c067dd3ea6c114703d2798603ee34" + }, + { + "m_Id": "82b4b5d5ca887d545a93b2ebc5420a2f" + }, + { + "m_Id": "9dc1264025075d3f6c4edfb544ddffba" + }, + { + "m_Id": "d50151422f1f740cb7f05629b52105c6" + }, + { + "m_Id": "ba596df8592ff559a1656a8271092c00" + }, + { + "m_Id": "5f82df1a874a453cb6cc062b147667d1" + }, + { + "m_Id": "19ad4fbe0507355cec97d7c22e75e67f" + } + ], + "synonyms": [ + "code", + "HLSL" + ], + "m_Precision": 1, + "m_PreviewExpanded": false, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SourceType": 0, + "m_FunctionName": "InsertTraced", + "m_FunctionSource": "e4772f4ce883a7f5a91b32fd5726e178", + "m_FunctionBody": "" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.CategoryData", + "m_ObjectId": "dfaf5502e7f3329878a55221a27171ef", + "m_Name": "Traced Mode", + "m_ChildObjectList": [ + { + "m_Id": "01e3ad468bb024c540d12fb1d82e3f39" + }, + { + "m_Id": "1ab3da5051a0d1c6e5f0519971b60ef6" + }, + { + "m_Id": "246a19b8617d6353878b042dba3edfa9" + }, + { + "m_Id": "2d8f58c882f2e08389909792c9f88d23" + }, + { + "m_Id": "c3444c3fc925497db883a15827a43589" + }, + { + "m_Id": "10e4bc09379ed79ddc0f6619b6d9d1c8" + }, + { + "m_Id": "d68f689dab76223a0dfebaaee8825756" + }, + { + "m_Id": "8cc0c881da45e9c5aad4fa94a3458314" + }, + { + "m_Id": "e685756294aebe0a2042a320ceaef1c7" + }, + { + "m_Id": "93493113bf60b372596ff9f87b19c39f" + }, + { + "m_Id": "a7e88bbf81ea878a5bfca0d53127868d" + }, + { + "m_Id": "ec2a73e27a54ba0ec440ee8d928f6d3d" + }, + { + "m_Id": "8e98c91ae76bd582369a6d906aff8030" + }, + { + "m_Id": "f800388fa7e319666ba126cc07c34a87" + }, + { + "m_Id": "c77f36232947c0fa1b50f51a037a2c8e" + }, + { + "m_Id": "5be65e4aa5f5cfeea6414ec5b226b082" + }, + { + "m_Id": "b9656fb097620e803efdb15cb2efc780" + }, + { + "m_Id": "8411184b546bf7d362d0d0c163ca46df" + }, + { + "m_Id": "0e95e1b96c13f33ae2721e85ecb9d530" + }, + { + "m_Id": "6ac5ec06804b45260359f4c1a0b29d4a" + }, + { + "m_Id": "1c5c03b10654a8a82a757f9188537160" + }, + { + "m_Id": "89e334fe03f678311fcd55cd21cea726" + }, + { + "m_Id": "f9813472e37bc7a9fd95a3f5b27dd8c8" + } + ] +} diff --git a/VisualPinball.Unity/Assets/Shaders/Srp/Insert/InsertTraced.hlsl b/VisualPinball.Unity/Assets/Shaders/Srp/Insert/InsertTraced.hlsl new file mode 100644 index 0000000000..bc6a124e82 --- /dev/null +++ b/VisualPinball.Unity/Assets/Shaders/Srp/Insert/InsertTraced.hlsl @@ -0,0 +1,344 @@ +// Visual Pinball Engine +// Copyright (C) 2026 freezy and VPE Team +// SPDX-License-Identifier: GPL-3.0-or-later +#ifndef VPE_INSERT_TRACED_INCLUDED +#define VPE_INSERT_TRACED_INCLUDED + +// Traced mode of the hybrid insert, behind the material keyword _INSERT_TRACED. +// Without the keyword both functions here hand their inputs straight through, +// and the insert is the live one, as before. +// +// With it, the body's light comes from an offline render of the insert (a +// path-traced bulb under the real plastic, seen from four elevations and +// packed into one RGBA map, _TracedLight), instead of the soft and edge glow +// of the optical response map. Everything else stays the live material's: +// the colour, brightness and lamp drive, the scene-lit body and its room +// return, the cover. The bulb seen through the facets is still drawn live, +// because a render cannot know where the eye is: through a facet normal map +// traced with the light (_TracedFacets), or with the live material's own image. +// +// The traced light belongs to the top of the insert: each texel is what the +// eye sees through that point of the top from the traced elevation. So the +// body is shaded as a flat card at the top, whatever part of the mesh a +// pixel falls on. The cover refracts what lies under it as through a slab as +// thick as the insert is deep, which would carry a card at the top off by the +// slab's thickness times the difference of the view's tangent in air and in +// the plastic; the card is shaded where the cover will show it instead. +// The mesh is only what the card is drawn on, so nothing of the traced light +// moves with the facets the mesh happens to have: one parallax, the bake's. +// +// Insert space is the live material's: metres, y up, the top at y = 0, z +// along the length. The bake has its own square, _TracedFrame.w metres wide, +// placed in insert space by its centre (_TracedFrame.xy) and turned by +// _TracedFrame.z degrees about y; it was rendered from its own -z side. + +#include "InsertOptics.hlsl" + +// The lamp-off brightness, in stops (a global: 0, the default, is the bake as calibrated). The table sets it when it loads, +// to match the bake's room to its own, so the unlit inserts sit in the room instead of glowing on their own. +float _InsertTracedOffEv; + +float2 InsertTracedTurn(float2 p, float degrees) +{ + float s, c; + sincos(radians(degrees), s, c); + return float2(p.x * c - p.y * s, p.x * s + p.y * c); +} + +// Insert space to the bake's square, as a texture coordinate. +float2 InsertTracedUv(float2 positionXZ) +{ + float2 bake = InsertTracedTurn(positionXZ - _TracedFrame.xy, _TracedFrame.z); + return bake / max(_TracedFrame.w, 1e-4) + 0.5; +} + +// How much each of the four traced elevations counts at this one: linear +// between neighbours, held at the ends rather than guessed beyond them. +float4 InsertTracedAngleWeights(float elevation) +{ + float4 a = _TracedAngles; + float4 w = 0.0; + if (elevation <= a.x) { + w.x = 1.0; + } else if (elevation <= a.y) { + float t = (elevation - a.x) / max(a.y - a.x, 1e-3); + w.xy = float2(1.0 - t, t); + } else if (elevation <= a.z) { + float t = (elevation - a.y) / max(a.z - a.y, 1e-3); + w.yz = float2(1.0 - t, t); + } else if (elevation <= a.w) { + float t = (elevation - a.z) / max(a.w - a.z, 1e-3); + w.zw = float2(1.0 - t, t); + } else { + w.w = 1.0; + } + return w; +} + +// A facet normal of the bake, in insert space. The map holds the normal's x +// and z in red and green, in the bake's square, and in alpha whether the +// point is on the faceted floor at all. +float4 InsertTracedFacet(float2 positionXZ) +{ + float2 uv = InsertTracedUv(positionXZ); + float4 f = SAMPLE_TEXTURE2D_GRAD(_TracedFacets, sampler_TracedFacets, uv, ddx(uv), ddy(uv)); // filtered: unfiltered facets crawl on a small insert + float2 nxz = f.rg * 2.0 - 1.0; + nxz = InsertTracedTurn(nxz, -_TracedFrame.z); + return float4(normalize(float3(nxz.x, sqrt(saturate(1.0 - dot(nxz, nxz))), nxz.y)), f.a); +} + +// The traced glints: the facets catching a few fixed highlights, each colour +// tilted a little apart, so a spoke flashes with a fringe as the eye moves. +// The highlights turn with the eye's bearing, read per pixel so a glint slides +// along a facet instead of switching whole facets. Straight under the eye the +// bearing is undefined and spins round that point (a radial seam, Ben +// 2026-10-20), so the glints fade out over the last ~15 degrees to vertical. +float InsertTracedGlint(float3 n, float3 v, float turn, float sharp, int channel) +{ + float4 p = _TracedGlintParams; + int keys = (int)clamp(p.z, 1.0, 8.0); + float tilt = radians(p.x + (channel - 1) * p.w); + float st, ct; + sincos(tilt, st, ct); + float azimuth = atan2(v.z, v.x) + radians(_TracedGlintRot + turn); + float sum = 0.0; + for (int j = 0; j < keys; j++) { + float sa, ca; + sincos(azimuth + 6.28318530718 * j / keys, sa, ca); + sum += pow(saturate(dot(n, float3(st * ca, ct, st * sa))), sharp); + } + // Energy kept as the lobe widens (Phong normalisation, against the default + // sharpness of 600): a wide glint is a dim one, not a flood into the bloom. + return sum * ((sharp + 2.0) / 602.0) * smoothstep(0.05, 0.25, length(v.xz)); +} + +// The traced lamp-off look: the bake's env pass, the insert under the room with the lamp off (refraction through the facets, +// the room in them, the coloured plastic), packed four elevations to a 2x2 atlas (_TracedEnv: 15 deg top left, 45 top right, +// 60 bottom left, 70 bottom right, as _TracedAngles), already multiplied by its body mask and calibrated. Read at the same card +// point and weights as the traced light, so it holds still against the plastic and crossfades with the view. +float3 InsertTracedEnv(float2 uv, float4 weights) +{ + // The mip level the insert's screen size asks for (with mipmaps imported), read by hand since the four slices share one + // texture: half a texel of that level in from each slice's edge, so no slice bleeds into its neighbour. A slice is a + // power-of-two block, so its mip texels never straddle two slices. + float2 slice = _TracedEnv_TexelSize.zw * 0.5; + float2 dx = ddx(uv) * slice, dy = ddy(uv) * slice; + float lod = clamp(0.5 * log2(max(max(dot(dx, dx), dot(dy, dy)), 1e-8)), 0.0, 4.0); + float2 margin = _TracedEnv_TexelSize.xy * exp2(lod); + float2 q = clamp(uv, margin, 1.0 - margin) * 0.5; + return SAMPLE_TEXTURE2D_LOD(_TracedEnv, sampler_TracedEnv, q + float2(0.0, 0.5), lod).rgb * weights.x + + SAMPLE_TEXTURE2D_LOD(_TracedEnv, sampler_TracedEnv, q + float2(0.5, 0.5), lod).rgb * weights.y + + SAMPLE_TEXTURE2D_LOD(_TracedEnv, sampler_TracedEnv, q, lod).rgb * weights.z + + SAMPLE_TEXTURE2D_LOD(_TracedEnv, sampler_TracedEnv, q + float2(0.5, 0.0), lod).rgb * weights.w; +} + +float3 InsertTracedEmission(float3 position, float3 view) +{ + float3 v = normalize(view + float3(0.0, 1e-5, 0.0)); + float eta = max(_OpticalIor, 1.001); + float3 inside = refract(-v, float3(0.0, 1.0, 0.0), rcp(eta)); + float steep = max(v.y, 0.05); + float steepInside = max(-inside.y, 0.05); + // Where the eye's ray crosses the top, then where the cover will show it. + float2 top = position.xz + v.xz * (-position.y / steep); + float cover = _TracedGeometry.x * 0.001; + float2 card = top - cover * (v.xz / steep + inside.xz / steepInside); + + // The elevation in the bake's frame, and how far the inside of the + // plastic slides against the top away from the traced direction. + float3 vb = float3(InsertTracedTurn(v.xz, _TracedFrame.z), v.y).xzy; + float elevation = degrees(acos(saturate(vb.y))); + float4 weights = InsertTracedAngleWeights(elevation); + float3 tracedView = float3(0.0, cos(radians(elevation)), -sin(radians(elevation))); + float3 tracedInside = refract(-tracedView, float3(0.0, 1.0, 0.0), rcp(eta)); + float3 vbInside = refract(-vb, float3(0.0, 1.0, 0.0), rcp(eta)); + float2 slide = (vbInside.xz / max(-vbInside.y, 0.25) - tracedInside.xz / max(-tracedInside.y, 0.25)) + * (_TracedGeometry.z * 0.001 / max(_TracedFrame.w, 1e-4)); + // Filtered by the screen size of the insert, so a small one doesn't shimmer (with mipmaps imported). + float2 lightUv = InsertTracedUv(card) + slide; + float light = dot(SAMPLE_TEXTURE2D_GRAD(_TracedLight, sampler_TracedLight, lightUv, ddx(lightUv), ddy(lightUv)), weights); + // Glow Spread: one is the render as baked. The traced light is normalised to its brightest texels, so a higher spread + // lifts the dim reaches toward the core (the glow fills the insert instead of pooling over the bulb), a lower one tightens it. + light = pow(max(light, 0.0), rcp(max(_TracedGlowSpread, 0.05))); + + // Seen at a slant, the render shows the pocket's far wall and the skirt + // as a bright band just inside the outline, which a flat card puts on the + // top. Fade the light out over the last _TracedGeometry.w millimetres + // inside the outline: the floor mask, blurred over that width, is a half + // on the outline and whole about a width in. + if (_TracedGeometry.w > 0.0) { + float texel = max(_TracedFrame.w, 1e-4) * _TracedFacets_TexelSize.x; + float mip = log2(max(2.0 * _TracedGeometry.w * 0.001 / texel, 1.0)); + float inside = SAMPLE_TEXTURE2D_LOD(_TracedFacets, sampler_TracedFacets, InsertTracedUv(card), mip).a; + light *= smoothstep(0.5, 0.95, inside); + } + + // The live material's drive: each bulb its colour and level, reaching as + // far as its spread lets it, as the live glow sums them. + float output = max(_InsertLampOutput, 0.0); + float4 lamps[4] = { _OpticalLamp, _OpticalLampB, _OpticalLampC, _OpticalLampD }; + float3 drives[4] = { max(_InsertLampColor.rgb, 0.0) * saturate(_InsertLampLevel) * output, + max(_InsertLampColorB.rgb, 0.0) * saturate(_InsertLampColorB.w) * output, + max(_InsertLampColorC.rgb, 0.0) * saturate(_InsertLampColorC.w) * output, + max(_InsertLampColorD.rgb, 0.0) * saturate(_InsertLampColorD.w) * output }; + + // The bulb through the facets, as the live material draws it, from the + // card: down through the plastic to the facets, out through the facet + // the bake found there, on to the bulb. + float facetDepth = _TracedGeometry.y * 0.001; + float3 floorPoint = float3(card.x, 0.0, card.y) + inside * (facetDepth / steepInside); + float4 facet = InsertTracedFacet(floorPoint.xz); + float3 n = normalize(float3(facet.x, max(abs(facet.y), 0.25), facet.z)); + float reflectance0 = (eta - 1.0) / (eta + 1.0); + reflectance0 *= reflectance0; + float cosInside = saturate(dot(-inside, n)); + float sinFacet = eta * sqrt(saturate(1.0 - cosInside * cosInside)); + float passing = 1.0 - smoothstep(0.75, 1.0, sinFacet); + float3 facetRay = passing > 0.0 ? refract(inside, n, eta) : -v; + float cosFacet = saturate(dot(facetRay, -n)); + float facetExit = 1.0 - (reflectance0 + (1.0 - reflectance0) * pow(1.0 - cosFacet, 5.0)); + float flatExit = 1.0 - (reflectance0 + (1.0 - reflectance0) * pow(1.0 - saturate(v.y), 5.0)); + float3 ray = normalize(lerp(-v, facetRay, passing)); + float exit = lerp(flatExit, facetExit, passing); + float fresnel = 0.04 + 0.96 * pow(1.0 - saturate(dot(v, n)), 5.0); + bool ownImage = _TracedBulbSource >= 0.5; // the complement of InsertTracedPre's liveImage + float depth = max(_TracedBulbDepth, 0.0); + + float3 glowDrive = 0.0; + float3 bulbs = 0.0; + [unroll] for (int i = 0; i < 4; i++) { + if (i > 0 && lamps[i].w <= 0.0) { + continue; + } + float4 lamp = float4(lamps[i].x, lamps[i].y * depth, lamps[i].z, lamps[i].w); + float4 b = InsertOpticsBulb(lamp, floorPoint, ray, 1.0, fresnel, exit, facet.w, _InsertCavitySpread, _OpticalSpread); + // The glow is driven from where the bulb really is: Bulb Depth only moves its image. + float reach = depth == 1.0 ? b.z + : InsertOpticsBulb(lamps[i], floorPoint, ray, 1.0, fresnel, exit, facet.w, _InsertCavitySpread, _OpticalSpread).z; + glowDrive += reach * drives[i]; + // The tight image of the bulb, and as much of the broad halo the live + // material draws through each facet as asked: the halo is what moves + // with the eye, but part of its light is in the traced render already. + bulbs += (b.w + saturate(_TracedBulbHalo) * b.x) * b.z * drives[i]; + } + + float wear = saturate(_InsertRoughness); + float3 grainAt = float3(card.x, 0.0, card.y) * 4000.0 + _OpticalSeed * float3(13.1, 17.7, 11.3); + float finish = 1.0 + (InsertGrain(grainAt) - 0.5) * InsertDetailVisibility(grainAt) * _OpticalGrain; + + // No two inserts glint alike: a seed from where the insert sits on the + // table turns its highlights a little (Glint Organic y, degrees either way) + // and shifts the moulding's waviness (x degrees of tilt over z millimetres), + // so a row of the same insert catches the light each its own way. + float3 originWS = TransformObjectToWorld(float3(0.0, 0.0, 0.0)); +#if defined(SHADEROPTIONS_CAMERA_RELATIVE_RENDERING) && (SHADEROPTIONS_CAMERA_RELATIVE_RENDERING != 0) + originWS = GetAbsolutePositionWS(originWS); // HDRP: world space is camera-relative +#endif + float seed = frac(sin(dot(originWS.xz, float2(127.1, 311.7))) * 43758.5453); + float seedB = frac(seed * 17.13 + 0.37); + float turn = (seed * 2.0 - 1.0) * _TracedGlintOrganic.y; + float2 wave = card * (6.28318530718 / max(_TracedGlintOrganic.z * 0.001, 1e-4)); + float2 wobble = float2(sin(wave.x * 0.83 + wave.y * 0.57 + seed * 6.283), sin(wave.y * 0.91 - wave.x * 0.41 + seedB * 6.283)); + float2 lean = wobble * tan(radians(clamp(_TracedGlintOrganic.x, 0.0, 30.0))); + float3 wavy = normalize(facet.xyz + float3(lean.x, 0.0, lean.y)); + // Specular anti-aliasing: where facets are smaller than a pixel the + // highlight widens by how much the unfiltered facet normal changes across + // the pixel, so glints soften with distance instead of crawling. + float2 facetUv = InsertTracedUv(floorPoint.xz); + float2 rawRg = SAMPLE_TEXTURE2D_LOD(_TracedFacets, sampler_TracedFacets, facetUv, 0).rg * 2.0 - 1.0; + float variance = max(dot(ddx(rawRg), ddx(rawRg)), dot(ddy(rawRg), ddy(rawRg))); + float alpha2 = 2.0 / (max(_TracedGlintParams.y, 1.0) + 2.0) + min(0.5 * variance, 0.18); + float sharp = max(2.0 / alpha2 - 2.0, 1.0); + float3 glint = float3(InsertTracedGlint(wavy, v, turn, sharp, 0), InsertTracedGlint(wavy, v, turn, sharp, 1), + InsertTracedGlint(wavy, v, turn, sharp, 2)) * facet.w; + + // The traced light is normalised to its own brightest texels; twenty + // times it, in units of the live Scatter, is about as bright as the + // live look on a typical insert, so Traced Glow starts near one. + float glow = 20.0 * max(_OpticalScatter, 0.0) * max(_TracedGlow, 0.0) * (1.0 - saturate(_TracedLiveMix)); + float3 emission = max(_InsertLuminosity, 0.0) * max(_EmissiveColor.rgb, 0.0) * finish + * (glowDrive * ((1.0 + 0.5 * wear) * glow * light + 10.0 * max(_TracedGlint, 0.0) * glint) + + (ownImage ? max(_OpticalLampGain, 0.0) * max(_TracedBulbGain, 0.0) * (1.0 - wear) * bulbs : 0.0)); + + // Alignment view: the bake's square in a 2 mm grid, red outside it, + // the traced light in green. + if (_TracedDebug > 0.5) { + float2 uv = InsertTracedUv(card); + float2 mm = frac(uv * max(_TracedFrame.w, 1e-4) * 500.0); + float grid = step(0.9, max(mm.x, mm.y)); + float outside = any(uv < 0.0) || any(uv > 1.0) ? 1.0 : 0.0; + emission = float3(outside, light, grid) * 1000.0; + } + // The lamp-off look is always there, under the lamp, as the bake's two passes add: it replaces the live body's unlit colour + // (InsertTraced below) as far as Lamp-Off Look goes, and the lamp's glow adds over it. + // The lamp-off render is one image per insert type, so on its own a row of + // them is identical (Ben, 2026-10-20). Each insert reads it from where it + // sits: its own few degrees of elevation (Lamp-Off Vary x), its own + // brightness (y, a fraction either way) and a wobble of the refraction + // with the moulding's waviness (z, in texels). And the room glints in the + // facets as the lamp does, with the same per-insert turn and waviness, as + // strong as the room light the render shows there (Lamp-Off Glints). + float look = max(_TracedEnvGain, 0.0); + if (look > 0.0 && _TracedDebug < 0.5) { + float4 vary = _TracedEnvVary; + float4 envWeights = InsertTracedAngleWeights(elevation + (seed * 2.0 - 1.0) * vary.x); + float2 envUv = InsertTracedUv(card) + slide + wobble * vary.z * 2.0 * _TracedEnv_TexelSize.xy; + float3 env = InsertTracedEnv(envUv, envWeights) * max(1.0 + (seedB * 2.0 - 1.0) * vary.y, 0.0); + emission += look * exp2(_InsertTracedOffEv) * (env + 10.0 * max(_TracedGlint, 0.0) * max(_TracedOffGlint, 0.0) * env * glint); + } +#if defined(UNIVERSAL_PIPELINE_CORE_INCLUDED) + emission *= _InsertUrpEmissionScale; +#endif + return emission; +} + +// Before the live optics: in traced mode the live glow is kept only as far as +// the mix asks, and the live bulb image only when it is the one drawn, with +// the bulbs as deep as asked. +void InsertTracedPre_float(float Scatter, float EdgeGlow, float LampGain, float4 Lamp, float4 LampB, float4 LampC, float4 LampD, + out float ScatterOut, out float EdgeGlowOut, out float LampGainOut, + out float4 LampOut, out float4 LampBOut, out float4 LampCOut, out float4 LampDOut) +{ +#if defined(_INSERT_TRACED) + float mix = saturate(_TracedLiveMix); + bool liveImage = _TracedBulbSource < 0.5; + float depth = liveImage ? max(_TracedBulbDepth, 0.0) : 1.0; + ScatterOut = Scatter * mix; + EdgeGlowOut = EdgeGlow * mix; + LampGainOut = liveImage ? LampGain * max(_TracedBulbGain, 0.0) : 0.0; + LampOut = float4(Lamp.x, Lamp.y * depth, Lamp.zw); + LampBOut = float4(LampB.x, LampB.y * depth, LampB.zw); + LampCOut = float4(LampC.x, LampC.y * depth, LampC.zw); + LampDOut = float4(LampD.x, LampD.y * depth, LampD.zw); +#else + ScatterOut = Scatter; + EdgeGlowOut = EdgeGlow; + LampGainOut = LampGain; + LampOut = Lamp; + LampBOut = LampB; + LampCOut = LampC; + LampDOut = LampD; +#endif +} + +// After the live optics: the traced light, the glints and the bulb through the +// traced facets, added to what the live optics still give. +void InsertTraced_float(float3 Position, float3 View, float3 BaseColorIn, float3 NormalIn, float3 EmissionIn, + float3 WorldNormal, float3 WorldTangent, float3 WorldBitangent, + out float3 BaseColor, out float3 NormalTS, out float3 Emission) +{ + BaseColor = BaseColorIn; + NormalTS = NormalIn; + Emission = EmissionIn; +#if defined(_INSERT_TRACED) && !defined(SHADERGRAPH_PREVIEW) + BaseColor *= 1.0 - saturate(_TracedEnvGain); // the traced lamp-off look takes the live unlit body's place + // Mesh Facet Normals: one keeps the live mesh's facet normals for the scene's light on the body; zero gives the body the + // flat polished top a real insert has (its facets are underneath, and the traced maps already draw them). The top is the + // insert's up, written in each face's own tangent frame, as InsertOptics flattens towards it. + float3 upWS = TransformObjectToWorldNormal(float3(0.0, 1.0, 0.0)); + float3 upTS = TransformWorldToTangent(upWS, float3x3(WorldTangent, WorldBitangent, WorldNormal)); + NormalTS = normalize(lerp(upTS, NormalIn, saturate(_TracedMeshNormals))); + Emission += InsertTracedEmission(Position, View); +#endif +} +#endif diff --git a/VisualPinball.Unity/Assets/Shaders/Srp/Insert/InsertTraced.hlsl.meta b/VisualPinball.Unity/Assets/Shaders/Srp/Insert/InsertTraced.hlsl.meta new file mode 100644 index 0000000000..7b073dc248 --- /dev/null +++ b/VisualPinball.Unity/Assets/Shaders/Srp/Insert/InsertTraced.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e4772f4ce883a7f5a91b32fd5726e178 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/inserts/traced-mode.md b/VisualPinball.Unity/Documentation~/creators-guide/editor/inserts/traced-mode.md new file mode 100644 index 0000000000..17b5d24618 --- /dev/null +++ b/VisualPinball.Unity/Documentation~/creators-guide/editor/inserts/traced-mode.md @@ -0,0 +1,61 @@ +--- +uid: insert_traced_mode +title: Insert Traced Mode +description: The Insert Hybrid shader's opt-in Traced mode, which lights the body from an offline path-traced render of the insert. +--- + +# Insert Traced Mode + +*Traced Mode* is an opt-in mode of the *Pinball/Insert Hybrid* body material. With it on, the body's glow comes from an offline, path-traced render of the insert instead of the optical response map. The bulb image and the facets' glints are still drawn live, through a facet map from the same render. With the lamp off, a second render of the insert under the room replaces the live unlit body. Off, the material is the live one, unchanged. + +Everything in the [body material](body-material.md)'s *Lamp* section stays in charge: color, brightness, the lamp drive and the Insert Light component work as without Traced Mode. Traced Mode only changes where the body's light comes from. + +Both pipelines draw it from the same material. HDRP shows a *Traced Mode* section after *Baked Data*, and URP shows a *Traced Mode* foldout after the graph's controls. Its controls only show while the mode is on. + +## What it needs + +The maps come from rendering one insert on its own, outside Unity. VPE doesn't make them yet: the [insert baker](baking.md) bakes the live mode's maps only. Import all three as linear (no sRGB), *Traced Light* and *Lamp-Off Env* as HDR (EXR), all with mipmaps so a small insert doesn't shimmer. + +- *Traced Light* (`_TracedLight`): what the eye sees through each point of the top with the lamp lit, rendered from four view elevations, one per channel. A linear HDR texture. +- *Traced Facets* (`_TracedFacets`): the facet normals of the same render, X and Z in red and green, the faceted floor in alpha. It needs mipmaps. +- *Lamp-Off Env* (`_TracedEnv`): the same insert with the lamp off under the room, the four elevations in a 2×2 atlas (15°, 45° on top, 60°, 70° below), multiplied by the body mask. Optional: without it, leave *Lamp-Off Look* at zero and the unlit body stays live. + +The body is shaded as a flat card at the top of the insert: the cover's refraction is undone, so the traced light doesn't move with the mesh's facets. + +## Bake + +- *Frame* places the render's square in insert space: its center X and Z in meters, its turn about Y in degrees, its width in meters. +- *Elevations (deg)* are the four view elevations the channels of *Traced Light* were rendered from, off vertical. +- *Depths (mm)*: X is the cover's slab thickness, whose refraction the card undoes; Y is how deep the facets sit under the top; Z is how deep the glow's parallax reaches; W is how far in from the rim the traced light fades out. + +*Alignment View* (in *Bulb*) shows the render's square on the insert as a 2 mm grid, red outside it and the traced light in green. Use it to set *Frame*. + +## Look + +- *Glow* is the traced glow's strength, in units of *Baked Scatter*: at one, it is as bright as the live glow would be. +- *Glow Spread* at one is the render as baked. Higher values lift the dim reaches toward the bright core, so the glow fills the insert instead of pooling over the bulb; lower values tighten it. +- *Live Glow Mix* is how much of the live soft and edge glow comes back in place of the traced one. +- *Glints* is the strength of the facets' highlights. A wide highlight is a dim one, so this sets how bright they are whatever their shape. +- *Glint Shape*: X is the facet tilt in degrees; Y is the angle sensitivity, how small a turn of the eye moves a highlight across a facet; Z is the number of highlights; W is the color fringe in degrees. +- *Glint Turn (deg)* turns the highlights about the insert. +- *Glint Organic*: X is how many degrees the molding's facets waver, so glints slide instead of switching whole facets; Y is how far each insert's highlights turn either way, seeded from where it sits, so a row of the same insert doesn't flash in step; Z is the waviness' wavelength in millimeters. + +## Lamp Off + +- *Lamp-Off Look* is how far the lamp-off render takes the place of the live unlit body: zero keeps the live body, one is the render as baked. The lamp's glow adds over it. +- *Lamp-Off Vary* lets each insert read the render a little differently, seeded from where it sits: X degrees of elevation, Y brightness as a fraction either way, Z a refraction wobble in texels. Zero is the render exactly, the same on every insert. +- *Lamp-Off Glints* is the room glinting in the facets with the lamp off. It is as strong as the room light the render shows there, with the glints' own shape and variation. + +> [!NOTE] +> **The lamp-off brightness is a global.** `_InsertTracedOffEv`, in stops, scales every traced insert's lamp-off render to the table's room. Unset, it is 0, which shows the render at the brightness of the room it was rendered in. Set it when the table loads with `Shader.SetGlobalFloat("_InsertTracedOffEv", stops)`. + +## Bulb + +- *Through Traced Facets* at one draws the bulb through the traced facets, from the card; zero keeps the live material's own bulb image. +- *Gain* is the bulb's strength through the facets, on top of *Strength*. +- *Halo* is how much of the bulb's broad halo, the part that moves with the eye, comes through with its tight image. +- *Depth* scales how deep the bulbs sit for their image: more depth, more parallax. +- *Mesh Facet Normals* at one lights the body with the live mesh's facet normals. Zero gives it a flat polished top, as a real insert has, and leaves the facets to the traced maps. + +> [!NOTE] +> **Shader properties.** The mode is the `_INSERT_TRACED` keyword. The controls use `_TracedLight`, `_TracedFacets`, `_TracedFrame`, `_TracedAngles`, `_TracedGeometry`, `_TracedGlow`, `_TracedGlowSpread`, `_TracedLiveMix`, `_TracedGlint`, `_TracedGlintParams`, `_TracedGlintRot`, `_TracedGlintOrganic`, `_TracedEnv`, `_TracedEnvGain`, `_TracedEnvVary`, `_TracedOffGlint`, `_TracedBulbSource`, `_TracedBulbGain`, `_TracedBulbHalo`, `_TracedBulbDepth`, `_TracedMeshNormals` and `_TracedDebug` (*Alignment View*). The shader code is `InsertTraced.hlsl`, next to the graph. diff --git a/VisualPinball.Unity/Documentation~/creators-guide/toc.yml b/VisualPinball.Unity/Documentation~/creators-guide/toc.yml index 73d594329b..54c7bc2379 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/toc.yml +++ b/VisualPinball.Unity/Documentation~/creators-guide/toc.yml @@ -37,6 +37,8 @@ href: editor/inserts/presets.md - name: Baking href: editor/inserts/baking.md + - name: Traced Mode + href: editor/inserts/traced-mode.md - name: Wire Rails href: editor/wire-rails/index.md items: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Insert/InsertTracedFields.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Insert/InsertTracedFields.cs new file mode 100644 index 0000000000..1a69786957 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Insert/InsertTracedFields.cs @@ -0,0 +1,98 @@ +// Visual Pinball Engine +// Copyright (C) 2026 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace VisualPinball.Unity.Editor +{ + /// + /// The controls of the Insert Hybrid shader's opt-in Traced mode, in the + /// order the inspectors show them, with what each one does. HDRP and URP + /// draw them with their own controls; the words live here once. + /// + public static class InsertTracedFields + { + public const string Keyword = "_INSERT_TRACED"; + public const string Title = "Traced Mode"; + + public static readonly GUIContent KeywordLabel = new("Traced Mode", + "Lights the body from a traced render instead of the optical response map. Off, the material is the live one, unchanged."); + + public const string Note = "The body is shaded as a flat card at the top: the cover's refraction is undone, so nothing of the traced light moves with the mesh's facets. Colour, brightness and the lamp drive stay the Lamp section's."; + + public readonly struct Entry + { + public readonly string Name; + public readonly GUIContent Label; + public readonly bool IsTexture; + + public Entry(string name, string label, string tooltip, bool isTexture = false) + { + Name = name; + Label = new GUIContent(label, tooltip); + IsTexture = isTexture; + } + } + + public readonly struct Section + { + public readonly string Title; + public readonly Entry[] Entries; + + public Section(string title, params Entry[] entries) + { + Title = title; + Entries = entries; + } + } + + public static readonly Section[] Sections = { + new("Bake", + new Entry("_TracedLight", "Traced Light", "The offline render of the lit insert: what the eye sees through each point of the top from four elevations, one per channel (Elevations below).", true), + new Entry("_TracedFacets", "Traced Facets", "The facet normals of the same render, X and Z in red and green, the faceted floor in alpha. The bulb and the glints refract through them.", true), + new Entry("_TracedFrame", "Frame", "Where the bake's square sits in insert space: its centre X and Z in metres, its turn about Y in degrees, its width in metres."), + new Entry("_TracedAngles", "Elevations (deg)", "The view elevations the four channels of the traced light were rendered from, off vertical."), + new Entry("_TracedGeometry", "Depths (mm)", "X: the cover's slab thickness, whose refraction the card undoes. Y: how deep the facets sit under the top. Z: how deep the glow's parallax reaches. W: how far in from the rim the traced light fades out.")), + new("Look", + new Entry("_TracedGlow", "Glow", "Strength of the traced glow, in units of Baked Scatter: one is as bright as the live glow would be."), + new Entry("_TracedGlowSpread", "Glow Spread", "One is the render as baked. More lifts the dim reaches toward the bright core, so the glow fills the insert instead of pooling over the bulb; less tightens it."), + new Entry("_TracedLiveMix", "Live Glow Mix", "How much of the live soft and edge glow comes back in place of the traced one."), + new Entry("_TracedGlint", "Glints", "Strength of the facets' highlights. A wide highlight is a dim one, so this sets how bright they are whatever their shape."), + new Entry("_TracedGlintParams", "Glint Shape", "X: facet tilt in degrees. Y: angle sensitivity, how small a turn of the eye moves a highlight across a facet (more is tighter and sharper). Z: number of highlights. W: colour fringe in degrees."), + new Entry("_TracedGlintRot", "Glint Turn (deg)", "Turns the highlights about the insert."), + new Entry("_TracedGlintOrganic", "Glint Organic", "X: how many degrees the moulding's facets waver, so glints slide instead of switching whole facets. Y: how far each insert's highlights turn either way, from where it sits, so a row of the same insert doesn't flash in step. Z: the waviness' wavelength in millimetres.")), + new("Lamp Off", + new Entry("_TracedEnv", "Lamp-Off Env", "The same render with the lamp off under the room: four elevations in a 2x2 atlas (15, 45 / 60, 70 degrees), multiplied by the body mask.", true), + new Entry("_TracedEnvGain", "Lamp-Off Look", "How far the traced lamp-off render takes the place of the live unlit body. Zero keeps the live body, one is the render as baked. The lamp's glow adds over it. Players dim or raise all of them together with the room brightness."), + new Entry("_TracedEnvVary", "Lamp-Off Vary", "Each insert reads the lamp-off render a little differently, from where it sits: X degrees of elevation, Y brightness (a fraction either way), Z a refraction wobble in texels. Zero is the render exactly, the same on every insert."), + new Entry("_TracedOffGlint", "Lamp-Off Glints", "The room glinting in the facets with the lamp off, as strong as the room light the render shows there, with the glints' own shape and per-insert variation.")), + new("Bulb", + new Entry("_TracedBulbSource", "Through Traced Facets", "One draws the bulb through the traced facets, from the card; zero keeps the live material's own image."), + new Entry("_TracedBulbGain", "Gain", "Strength of the bulb seen through the facets, on top of Lamp Strength."), + new Entry("_TracedBulbHalo", "Halo", "How much of the bulb's broad halo, the part that moves with the eye, comes through with its tight image."), + new Entry("_TracedBulbDepth", "Depth", "Scales how deep the bulbs sit for their image: more depth, more parallax."), + new Entry("_TracedMeshNormals", "Mesh Facet Normals", "One lights the body with the live mesh's facet normals; zero gives it a flat polished top, as a real insert has, and leaves the facets to the traced maps."), + new Entry("_TracedDebug", "Alignment View", "Shows the bake's square on the insert: a 2 mm grid, red outside it, the traced light in green.")), + }; + + private static readonly HashSet Names = new(Sections.SelectMany(section => section.Entries).Select(entry => entry.Name).Append(Keyword)); + + /// Whether a property is one of the Traced controls, so a generic list can leave it to them. + public static bool Owns(string property) => Names.Contains(property); + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Insert/InsertTracedFields.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Insert/InsertTracedFields.cs.meta new file mode 100644 index 0000000000..a38a252c77 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/VPT/Insert/InsertTracedFields.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9196a74ee3674a689dd6969352bd1381 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/Insert/InsertPresetTests.cs b/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/Insert/InsertPresetTests.cs index b17198c214..a0a5bbdaf6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/Insert/InsertPresetTests.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Test/VPT/Insert/InsertPresetTests.cs @@ -41,6 +41,9 @@ public void ShouldApplyBodySettingsWithoutChangingColorsMapsOrCover() source.SetVector("_OpticalLamp", new Vector4(0.01f, -0.02f, 0.03f, 0.009f)); source.SetVector("_OpticalStarburst", new Vector4(1f, 2f, 3f, 4f)); source.SetFloat("_InsertLampLevel", 0.7f); + source.SetFloat("_INSERT_TRACED", 1f); + source.SetFloat("_TracedGlow", 0.5f); + source.SetVector("_TracedFrame", new Vector4(0.001f, 0.002f, 90f, 0.034f)); source.SetTexture("_NormalMap", sourceMap); sourceCover.SetColor("_HazeColor", new Color(0.8f, 0.8f, 0.8f)); sourceCover.SetFloat("_CoverOpacity", 0.365f); @@ -57,6 +60,7 @@ public void ShouldApplyBodySettingsWithoutChangingColorsMapsOrCover() Assert.That(preset.Body, Has.None.Matches(e => e.Name == "_NormalMap"), "new presets exclude texture maps"); Assert.That(preset.Body, Has.None.Matches(e => e.Name is "_OpticalLamp" or "_OpticalLampB" or "_OpticalLampC" or "_OpticalLampD"), "the virtual lamps are the insert's own"); Assert.That(preset.Body, Has.None.Matches(e => e.Name == "_OpticalStarburst" || e.Name == "_InsertLampLevel"), "hidden bake and runtime values stay out"); + Assert.That(preset.Body, Has.None.Matches(e => e.Name == "_INSERT_TRACED" || e.Name.StartsWith("_Traced")), "the Traced mode and its bake's values are the insert's own"); Assert.That(preset.Cover, Is.Empty, "new presets exclude the cover material"); Assert.That(preset.CoverShader, Is.Empty); @@ -75,6 +79,7 @@ public void ShouldApplyBodySettingsWithoutChangingColorsMapsOrCover() target.SetColor("_EmissiveColor", Color.magenta); target.SetVector("_OpticalLamp", new Vector4(0f, -0.015f, 0f, 0.007f)); target.SetVector("_OpticalStarburst", new Vector4(9f, 9f, 9f, 9f)); + target.SetVector("_TracedFrame", new Vector4(0f, 0f, 0f, 0.05f)); target.SetTexture("_NormalMap", targetMap); target.SetTextureScale("_NormalMap", new Vector2(2f, 3f)); targetCover.SetColor("_HazeColor", Color.red); @@ -91,6 +96,7 @@ public void ShouldApplyBodySettingsWithoutChangingColorsMapsOrCover() Assert.That(target.GetColor("_EmissiveColor"), Is.EqualTo(Color.magenta)); Assert.That(target.GetVector("_OpticalLamp"), Is.EqualTo(new Vector4(0f, -0.015f, 0f, 0.007f)), "the target keeps its own lamp"); Assert.That(target.GetVector("_OpticalStarburst"), Is.EqualTo(new Vector4(9f, 9f, 9f, 9f)), "and its own bake"); + Assert.That(target.GetVector("_TracedFrame"), Is.EqualTo(new Vector4(0f, 0f, 0f, 0.05f)), "and its own traced bake"); Assert.That(target.GetTexture("_NormalMap"), Is.SameAs(targetMap), "and its own maps"); Assert.That(target.GetTextureScale("_NormalMap"), Is.EqualTo(new Vector2(2f, 3f))); Assert.That(targetCover.GetColor("_HazeColor"), Is.EqualTo(Color.red)); diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Insert/InsertMaterialPreset.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Insert/InsertMaterialPreset.cs index 2b8ac9d4fa..adec77479f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Insert/InsertMaterialPreset.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Insert/InsertMaterialPreset.cs @@ -57,6 +57,13 @@ public sealed class Entry /// private static readonly HashSet Excluded = new() { "_OpticalLamp", "_OpticalLampB", "_OpticalLampC", "_OpticalLampD", "_InsertLuminosity" }; + /// + /// The opt-in Traced mode and its values are the insert type's and its + /// bake's (where the render sits, its elevations, the look tuned to it), + /// not a look to share between inserts. + /// + private static bool IsTraced(string name) => name == "_INSERT_TRACED" || name.StartsWith("_Traced", StringComparison.Ordinal); + /// The preset's value for the property, or null when it has none. public Entry Find(string name) => Body?.Find(entry => entry != null && entry.Name == name && entry.Kind != Kind.Color && !Excluded.Contains(name)); @@ -121,7 +128,8 @@ public static bool IsStyle(Shader shader, int index) if ((shader.GetPropertyFlags(index) & ShaderPropertyFlags.HideInInspector) != 0) { return false; } - return !Excluded.Contains(shader.GetPropertyName(index)); + var name = shader.GetPropertyName(index); + return !Excluded.Contains(name) && !IsTraced(name); } private static List Read(Material material)