├── Runtime ├── AssemblyReference.asmref ├── AssemblyInfo.cs ├── Lighting.meta ├── PostProcessing.meta ├── AssemblyReference.asmref.meta ├── PostProcessing │ ├── Components.meta │ └── Components │ │ ├── Exposure.cs.meta │ │ └── Exposure.cs ├── AssemblyInfo.cs.meta ├── Documentation.cs.meta ├── Lighting │ ├── LightUtils.cs.meta │ ├── AdditionalLightData.cs.meta │ ├── AdditionalLightData.Types.cs.meta │ ├── AdditionalLightData.Types.cs │ ├── LightUtils.cs │ └── AdditionalLightData.cs └── Documentation.cs ├── Editor ├── RenderPipeline │ ├── EditorAssemblyReference.asmref │ ├── AssemblyInfo.cs │ ├── EditorAssemblyReference.asmref.meta │ └── AssemblyInfo.cs.meta ├── AssemblyInfo.cs ├── Lighting.meta ├── PostProcessing.meta ├── RenderPipeline.meta ├── Lighting │ ├── LightUnit.meta │ ├── URPLightUI.Skin.cs.meta │ ├── AdditionalDataEditor.cs.meta │ ├── SerializedHDLight.cs.meta │ ├── URPLightUI.Drawers.cs.meta │ ├── URPLightUI.PhysicalUnit.cs.meta │ ├── URPLightUI.PresetInspector.cs.meta │ ├── LightUnit │ │ ├── LightUnitSliderUIDrawer.cs.meta │ │ ├── PiecewiseLightUnitSlider.cs.meta │ │ ├── PunctualLightUnitSlider.cs.meta │ │ ├── LightUnitSliderUIDrawer.cs │ │ ├── PunctualLightUnitSlider.cs │ │ └── PiecewiseLightUnitSlider.cs │ ├── PhysicalLightEditor.cs.meta │ ├── AdditionalDataEditor.cs │ ├── URPLightUI.PresetInspector.cs │ ├── PhysicalLightEditor.cs │ ├── SerializedHDLight.cs │ ├── URPLightUI.PhysicalUnit.cs │ ├── URPLightUI.Drawers.cs │ └── URPLightUI.Skin.cs ├── PhysicalLightUnits.Editor.asmdef.meta ├── AssemblyInfo.cs.meta ├── PostProcessing │ ├── ExposureEditor.cs.meta │ └── ExposureEditor.cs └── PhysicalLightUnits.Editor.asmdef ├── Images~ ├── Screenshots.jpg ├── PackageManager.jpg ├── PackageManager.jpg.meta └── Screenshots.jpg.meta ├── LICENSE.md.meta ├── README.md.meta ├── package.json.meta ├── Editor.meta ├── Runtime.meta ├── LICENSE.md ├── package.json ├── README.md └── .gitignore /Runtime/AssemblyReference.asmref: -------------------------------------------------------------------------------- 1 | { 2 | "reference": "GUID:15fc0a57446b3144c949da3e2b9737a9" 3 | } -------------------------------------------------------------------------------- /Editor/RenderPipeline/EditorAssemblyReference.asmref: -------------------------------------------------------------------------------- 1 | { 2 | "reference": "GUID:c579267770062bf448e75eb160330b7f" 3 | } -------------------------------------------------------------------------------- /Images~/Screenshots.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unity-China/cn.unity.physical-light-unit/HEAD/Images~/Screenshots.jpg -------------------------------------------------------------------------------- /Images~/PackageManager.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unity-China/cn.unity.physical-light-unit/HEAD/Images~/PackageManager.jpg -------------------------------------------------------------------------------- /Editor/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.Editor")] 4 | -------------------------------------------------------------------------------- /Editor/RenderPipeline/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("PhysicalLightUnits.Editor")] -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b669b0a725a787c429632a09d86027d2 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 22198abbc88acab409071fb17f37515f 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3b4c0456ebc6f964b92f26c6345ed5b7 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.Editor")] 4 | [assembly: InternalsVisibleTo("PhysicalLightUnits.Editor")] 5 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f09c06d63fe14f941b8d28efaba2efff 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6aa924f8ed9eaf94b874eb97334cf0d2 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Lighting.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2d6ad0798496b4f4f980861117db0a34 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Lighting.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 46d6cf4a708d5774f82615832ebf8388 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/PostProcessing.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aa2a46c68b2297244a55f3c5416349b2 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/RenderPipeline.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9fc95598beddd27488ad1c6bd1d86f4c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/PostProcessing.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c1565e0a83c697740a241ed30e149d2c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Lighting/LightUnit.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1562439d8dacc2b42a5f5bfb2538659f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/PhysicalLightUnits.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 761f003c9bfffad4c84c53c7a71e0891 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/AssemblyReference.asmref.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b016fef46439db64ba54d9f9c0afe623 3 | AssemblyDefinitionReferenceImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/PostProcessing/Components.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 35d5c0036ec5f1d42996399b4c1a641b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/RenderPipeline/EditorAssemblyReference.asmref.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c75080a91bf77f64885c4392ebc8b92c 3 | AssemblyDefinitionReferenceImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/AssemblyInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1a9a4afdc99a99446a587b2e23178305 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/AssemblyInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3b164b15ea11040438ed527df7eedb5d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Documentation.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b5059008729ec64aa51bd8630f730b7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Lighting/URPLightUI.Skin.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c09d6b7010366ed4780fc173d2275e3e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Lighting/LightUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 09470b804f539be409371a55305b7066 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Lighting/AdditionalDataEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e20a0928b47a93648ad7132b3162c3fc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Lighting/SerializedHDLight.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4580a0bded100d9489dd3b8121d889ff 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Lighting/URPLightUI.Drawers.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e79a2ef4cdac5074f9dd22a4345bd1cb 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/PostProcessing/ExposureEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 41e1dd1ffab68244a95ee8fd15a8f07a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/RenderPipeline/AssemblyInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de18de09609f3d54eb9198b4b276d32d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Lighting/AdditionalLightData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 947d7d5740aaf064a83626d29bc454d8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Lighting/URPLightUI.PhysicalUnit.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b1b848bef848a7544b79bf3629db0805 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Lighting/URPLightUI.PresetInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0f3ba2e95e99df24d9451f8140ab6e25 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Lighting/AdditionalLightData.Types.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d0dab75e621981343914f698c8ee748a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/PostProcessing/Components/Exposure.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5eb1e76f9b2e49443b85e607bd7f8df1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Lighting/LightUnit/LightUnitSliderUIDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ce0fcf5c60413724b82a80e3edac16e5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Lighting/LightUnit/PiecewiseLightUnitSlider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b8fc83ea0d49a954784f99ef4bb9772c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Lighting/LightUnit/PunctualLightUnitSlider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a7af08bfc1716d74aa53debe81018244 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Lighting/PhysicalLightEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f75744affeb5394c94ba196699da67b 3 | timeCreated: 1509642715 4 | licenseType: Pro 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Editor/Lighting/AdditionalDataEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Rendering.Universal; 3 | 4 | 5 | namespace UnityEditor.Rendering.Universal 6 | { 7 | [CanEditMultipleObjects] 8 | [CustomEditor(typeof(AdditionalLightData))] 9 | public class AdditionalLightDataEditor : Editor 10 | { 11 | public override void OnInspectorGUI() 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | cn.unity.physical-light-units copyright © 2025 Unity Technologies ApS 2 | 3 | Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License). 4 | 5 | Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions. 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cn.unity.physical-light-unit", 3 | "description": "Physical Light Units for Universal Render Pipeline (URP).", 4 | "version": "14.0.11", 5 | "unity": "2022.3", 6 | "displayName": "Physical Light Unit", 7 | "author": { 8 | "name": "michael.lam @ Unity Technologies" 9 | }, 10 | "license": "Unity Companion License", 11 | "dependencies": { 12 | "com.unity.render-pipelines.universal": "14.0.11" 13 | }, 14 | "keywords": [ 15 | "graphics", 16 | "performance", 17 | "rendering", 18 | "mobile", 19 | "render", 20 | "pipeline", 21 | "Physical", 22 | "Light", 23 | "Units" 24 | ] 25 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cn.unity.physical-light-unit 2 | ## Physical Light Unit for (URP) Universal Render Pipeline 3 | 4 | ### Version support: 5 | Universal RP version: 14.0.11+ 6 | 7 | Unity Editor version: 2022.3.55(LTS)+ 8 | 9 | ![Screenshot](Images~/Screenshots.jpg) 10 | 11 | ### Note: The Exposure feature is not complete, currently it will just do the simple multiplier of the final Light Intensity output! Only Fixed Mode avaliable at the moment! 12 | 13 | ### Installation: 14 | Add this package via Package Manager: 15 | 16 | - Copy this Repository HTTPS URL to clipboard 17 | - Open via Package Manager Window 18 | 19 | ![Screenshot](Images~/PackageManager.jpg) 20 | 21 | -------------------------------------------------------------------------------- /Editor/PhysicalLightUnits.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PhysicalLightUnits.Editor", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:3eae0364be2026648bf74846acb8a731", 6 | "GUID:df380645f10b7bc4b97d4f5eb6303d95", 7 | "GUID:c579267770062bf448e75eb160330b7f", 8 | "GUID:15fc0a57446b3144c949da3e2b9737a9" 9 | ], 10 | "includePlatforms": [ 11 | "Editor" 12 | ], 13 | "excludePlatforms": [], 14 | "allowUnsafeCode": false, 15 | "overrideReferences": false, 16 | "precompiledReferences": [], 17 | "autoReferenced": true, 18 | "defineConstraints": [], 19 | "versionDefines": [], 20 | "noEngineReferences": false 21 | } -------------------------------------------------------------------------------- /Runtime/Documentation.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace UnityEngine.Rendering.HighDefinition 4 | { 5 | [Conditional("UNITY_EDITOR")] 6 | internal class HDRPHelpURLAttribute : CoreRPHelpURLAttribute 7 | { 8 | public HDRPHelpURLAttribute(string pageName) 9 | : base(pageName, Documentation.packageName) 10 | { 11 | } 12 | } 13 | 14 | internal class Documentation : DocumentationInfo 15 | { 16 | /// 17 | /// The name of the package 18 | /// 19 | public const string packageName = "com.unity.render-pipelines.high-definition"; 20 | /// 21 | /// Generates a help url for the given package and page name 22 | /// 23 | /// The page name 24 | /// The full url page 25 | public static string GetPageLink(string pageName) => GetPageLink(packageName, pageName); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Editor/Lighting/URPLightUI.PresetInspector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using UnityEngine.Rendering; 4 | using UnityEngine.Rendering.Universal; 5 | 6 | namespace UnityEditor.Rendering.Universal 7 | { 8 | using CED = CoreEditorDrawer; 9 | 10 | internal partial class URPLightUI 11 | { 12 | static readonly ExpandedState k_ExpandedStatePreset = new(0, "URP-preset"); 13 | 14 | public static readonly CED.IDrawer PresetInspector = CED.Group( 15 | CED.Group((serialized, owner) => 16 | EditorGUILayout.HelpBox(Rendering.LightUI.Styles.unsupportedPresetPropertiesMessage, MessageType.Info)), 17 | CED.Group((serialized, owner) => EditorGUILayout.Space()), 18 | CED.FoldoutGroup(Rendering.LightUI.Styles.generalHeader, 19 | Expandable.General, 20 | k_ExpandedStatePreset, 21 | DrawGeneralContentPreset), 22 | CED.FoldoutGroup(Rendering.LightUI.Styles.emissionHeader, 23 | Expandable.Emission, 24 | k_ExpandedStatePreset, 25 | CED.Group( 26 | Rendering.LightUI.DrawColor, 27 | DrawEmissionContent)) 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore 4 | # 5 | .utmp/ 6 | /[Ll]ibrary/ 7 | /[Tt]emp/ 8 | /[Oo]bj/ 9 | /[Bb]uild/ 10 | /[Bb]uilds/ 11 | /[Ll]ogs/ 12 | /[Uu]ser[Ss]ettings/ 13 | 14 | # MemoryCaptures can get excessive in size. 15 | # They also could contain extremely sensitive data 16 | /[Mm]emoryCaptures/ 17 | 18 | # Recordings can get excessive in size 19 | /[Rr]ecordings/ 20 | 21 | # Uncomment this line if you wish to ignore the asset store tools plugin 22 | # /[Aa]ssets/AssetStoreTools* 23 | 24 | # Autogenerated Jetbrains Rider plugin 25 | /[Aa]ssets/Plugins/Editor/JetBrains* 26 | 27 | # Visual Studio cache directory 28 | .vs/ 29 | 30 | # Gradle cache directory 31 | .gradle/ 32 | 33 | # Autogenerated VS/MD/Consulo solution and project files 34 | ExportedObj/ 35 | .consulo/ 36 | *.csproj 37 | *.unityproj 38 | *.sln 39 | *.suo 40 | *.tmp 41 | *.user 42 | *.userprefs 43 | *.pidb 44 | *.booproj 45 | *.svd 46 | *.pdb 47 | *.mdb 48 | *.opendb 49 | *.VC.db 50 | 51 | # Unity3D generated meta files 52 | *.pidb.meta 53 | *.pdb.meta 54 | *.mdb.meta 55 | 56 | # Unity3D generated file on crash reports 57 | sysinfo.txt 58 | 59 | # Builds 60 | *.apk 61 | *.aab 62 | *.unitypackage 63 | *.unitypackage.meta 64 | *.app 65 | 66 | # Crashlytics generated file 67 | crashlytics-build.properties 68 | 69 | # Packed Addressables 70 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* 71 | 72 | # Temporary auto-generated Android Assets 73 | /[Aa]ssets/[Ss]treamingAssets/aa.meta 74 | /[Aa]ssets/[Ss]treamingAssets/aa/* 75 | -------------------------------------------------------------------------------- /Images~/PackageManager.jpg.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 785ba664b6cb61f44bd9cf0e54048feb 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 13 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 0 41 | wrapV: 0 42 | wrapW: 0 43 | nPOTScale: 1 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 0 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 100 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 0 56 | spriteTessellationDetail: -1 57 | textureType: 0 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 2048 73 | resizeAlgorithm: 0 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | spriteSheet: 97 | serializedVersion: 2 98 | sprites: [] 99 | outline: [] 100 | physicsShape: [] 101 | bones: [] 102 | spriteID: 103 | internalID: 0 104 | vertices: [] 105 | indices: 106 | edges: [] 107 | weights: [] 108 | secondaryTextures: [] 109 | nameFileIdTable: {} 110 | mipmapLimitGroupName: 111 | pSDRemoveMatte: 0 112 | userData: 113 | assetBundleName: 114 | assetBundleVariant: 115 | -------------------------------------------------------------------------------- /Images~/Screenshots.jpg.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2007d692b6f383140bd9e3e334bf9577 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 13 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 0 41 | wrapV: 0 42 | wrapW: 0 43 | nPOTScale: 1 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 0 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 100 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 0 56 | spriteTessellationDetail: -1 57 | textureType: 0 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 2048 73 | resizeAlgorithm: 0 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | spriteSheet: 97 | serializedVersion: 2 98 | sprites: [] 99 | outline: [] 100 | physicsShape: [] 101 | bones: [] 102 | spriteID: 103 | internalID: 0 104 | vertices: [] 105 | indices: 106 | edges: [] 107 | weights: [] 108 | secondaryTextures: [] 109 | nameFileIdTable: {} 110 | mipmapLimitGroupName: 111 | pSDRemoveMatte: 0 112 | userData: 113 | assetBundleName: 114 | assetBundleVariant: 115 | -------------------------------------------------------------------------------- /Editor/Lighting/LightUnit/LightUnitSliderUIDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Rendering.Universal; 3 | 4 | namespace UnityEditor.Rendering.Universal 5 | { 6 | internal class LightUnitSliderUIDrawer 7 | { 8 | static PiecewiseLightUnitSlider k_DirectionalLightUnitSlider; 9 | static PunctualLightUnitSlider k_PunctualLightUnitSlider; 10 | static PiecewiseLightUnitSlider k_ExposureSlider; 11 | 12 | static LightUnitSliderUIDrawer() 13 | { 14 | // Maintain a unique slider for directional/lux. 15 | k_DirectionalLightUnitSlider = new PiecewiseLightUnitSlider(LightUnitSliderDescriptors.LuxDescriptor); 16 | 17 | // Internally, slider is always in terms of lumens, so that the slider is uniform for all light units. 18 | k_PunctualLightUnitSlider = new PunctualLightUnitSlider(LightUnitSliderDescriptors.LumenDescriptor); 19 | 20 | // Exposure is in EV100, but we load a separate due to the different icon set. 21 | k_ExposureSlider = new PiecewiseLightUnitSlider(LightUnitSliderDescriptors.ExposureDescriptor); 22 | } 23 | 24 | // Need to cache the serialized object on the slider, to add support for the preset selection context menu (need to apply changes to serialized) 25 | // TODO: This slider drawer is getting kind of bloated. Break up the implementation into where it is actually used? 26 | public void SetSerializedObject(SerializedObject serializedObject) 27 | { 28 | k_DirectionalLightUnitSlider.SetSerializedObject(serializedObject); 29 | k_PunctualLightUnitSlider.SetSerializedObject(serializedObject); 30 | k_ExposureSlider.SetSerializedObject(serializedObject); 31 | } 32 | 33 | public void Draw(LightType type, LightUnit lightUnit, SerializedProperty value, Rect rect, SerializedHDLight light, Editor owner) 34 | { 35 | using (new EditorGUI.IndentLevelScope(-EditorGUI.indentLevel)) 36 | { 37 | if (type == LightType.Directional) 38 | DrawDirectionalUnitSlider(value, rect); 39 | else 40 | DrawPunctualLightUnitSlider(lightUnit, value, rect, light, owner); 41 | } 42 | } 43 | 44 | void DrawDirectionalUnitSlider(SerializedProperty value, Rect rect) 45 | { 46 | float val = value.floatValue; 47 | k_DirectionalLightUnitSlider.Draw(rect, value, ref val); 48 | if (val != value.floatValue) 49 | value.floatValue = val; 50 | } 51 | 52 | void DrawPunctualLightUnitSlider(LightUnit lightUnit, SerializedProperty value, Rect rect, SerializedHDLight light, Editor owner) 53 | { 54 | k_PunctualLightUnitSlider.Setup(lightUnit, light, owner); 55 | 56 | float val = value.floatValue; 57 | k_PunctualLightUnitSlider.Draw(rect, value, ref val); 58 | if (val != value.floatValue) 59 | value.floatValue = val; 60 | } 61 | 62 | public void DrawExposureSlider(SerializedProperty value, Rect rect) 63 | { 64 | using (new EditorGUI.IndentLevelScope(-EditorGUI.indentLevel)) 65 | { 66 | float val = value.floatValue; 67 | k_ExposureSlider.Draw(rect, value, ref val); 68 | if (val != value.floatValue) 69 | value.floatValue = val; 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Editor/Lighting/LightUnit/PunctualLightUnitSlider.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Rendering.Universal; 3 | 4 | namespace UnityEditor.Rendering.Universal 5 | { 6 | /// 7 | /// Formats the provided descriptor into a punctual light unit slider with contextual slider markers, tooltips, and icons. 8 | /// 9 | class PunctualLightUnitSlider : PiecewiseLightUnitSlider 10 | { 11 | public PunctualLightUnitSlider(LightUnitSliderUIDescriptor descriptor) : base(descriptor) { } 12 | 13 | private SerializedHDLight m_Light; 14 | private Editor m_Editor; 15 | private LightUnit m_Unit; 16 | private bool m_SpotReflectorEnabled; 17 | 18 | // Note: these should be in sync with LightUnit 19 | private static string[] k_UnitNames = 20 | { 21 | "Lumen", 22 | "Candela", 23 | "Lux", 24 | "Nits", 25 | "EV", 26 | }; 27 | 28 | public void Setup(LightUnit unit, SerializedHDLight light, Editor owner) 29 | { 30 | m_Unit = unit; 31 | m_Light = light; 32 | m_Editor = owner; 33 | 34 | // Cache the spot reflector state as we will need to revert back to it after treating the slider as point light. 35 | m_SpotReflectorEnabled = light.enableSpotReflector.boolValue; 36 | } 37 | 38 | public override void Draw(Rect rect, SerializedProperty value, ref float floatValue) 39 | { 40 | // Convert the incoming unit value into Lumen as the punctual slider is always in these terms (internally) 41 | float convertedValue = UnitToLumen(floatValue); 42 | 43 | EditorGUI.BeginChangeCheck(); 44 | base.Draw(rect, value, ref convertedValue); 45 | if (EditorGUI.EndChangeCheck()) 46 | floatValue = LumenToUnit(convertedValue); 47 | } 48 | 49 | protected override GUIContent GetLightUnitTooltip(string baseTooltip, float value, string unit) 50 | { 51 | // Convert the internal lumens into the actual light unit value 52 | value = LumenToUnit(value); 53 | unit = k_UnitNames[(int)m_Unit]; 54 | 55 | return base.GetLightUnitTooltip(baseTooltip, value, unit); 56 | } 57 | 58 | float UnitToLumen(float value) 59 | { 60 | if (m_Unit == LightUnit.Lumen) 61 | return value; 62 | 63 | // Punctual slider currently does not have any regard for spot shape/reflector. 64 | // Conversions need to happen as if light is a point, and this is the only setting that influences that. 65 | m_Light.enableSpotReflector.boolValue = false; 66 | 67 | return URPLightUI.ConvertLightIntensity(m_Unit, LightUnit.Lumen, m_Light, m_Editor, value); 68 | } 69 | 70 | float LumenToUnit(float value) 71 | { 72 | if (m_Unit == LightUnit.Lumen) 73 | return value; 74 | 75 | // Once again temporarily disable reflector in case we called this for tooltip or context menu preset. 76 | m_Light.enableSpotReflector.boolValue = false; 77 | 78 | value = URPLightUI.ConvertLightIntensity(LightUnit.Lumen, m_Unit, m_Light, m_Editor, value); 79 | 80 | // Restore the state of spot reflector on the light. 81 | m_Light.enableSpotReflector.boolValue = m_SpotReflectorEnabled; 82 | 83 | return value; 84 | } 85 | 86 | protected override void SetValueToPreset(SerializedProperty value, LightUnitSliderUIRange preset) 87 | { 88 | m_Light?.Update(); 89 | 90 | // Convert to the actual unit value. 91 | value.floatValue = LumenToUnit(preset.presetValue); 92 | 93 | m_Light?.Apply(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Editor/PostProcessing/ExposureEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Rendering.Universal; 3 | 4 | namespace UnityEditor.Rendering.Universal 5 | { 6 | [CustomEditor(typeof(Exposure))] 7 | sealed class ExposureEditor : VolumeComponentEditor 8 | { 9 | SerializedDataParameter m_Mode; 10 | 11 | SerializedDataParameter m_FixedExposure; 12 | SerializedDataParameter m_Compensation; 13 | Material skyMat; 14 | Exposure exposureVolumeComponent; 15 | 16 | private static LightUnitSliderUIDrawer k_LightUnitSlider; 17 | 18 | public override void OnEnable() 19 | { 20 | var o = new PropertyFetcher(serializedObject); 21 | 22 | exposureVolumeComponent = serializedObject.targetObject as Exposure; 23 | 24 | m_Mode = Unpack(o.Find(x => x.mode)); 25 | 26 | m_FixedExposure = Unpack(o.Find(x => x.fixedExposure)); 27 | m_Compensation = Unpack(o.Find(x => x.compensation)); 28 | 29 | k_LightUnitSlider = new LightUnitSliderUIDrawer(); 30 | } 31 | 32 | public override void OnInspectorGUI() 33 | { 34 | PropertyField(m_Mode); 35 | 36 | int mode = m_Mode.value.intValue; 37 | if (mode == (int)ExposureMode.UsePhysicalCamera) 38 | { 39 | PropertyField(m_Compensation); 40 | } 41 | else if (mode == (int)ExposureMode.Fixed) 42 | { 43 | DoExposurePropertyField(m_FixedExposure); 44 | PropertyField(m_Compensation); 45 | } 46 | 47 | // Skybox 48 | skyMat = RenderSettings.skybox; 49 | EditorGUI.BeginChangeCheck(); 50 | if(skyMat != null) 51 | { 52 | float ev100 = m_FixedExposure.value.floatValue; 53 | float comp = m_Compensation.value.floatValue; //TODO: for exposure not tfor skybox 54 | exposureVolumeComponent.SetSkyboxExposure(skyMat, ev100, comp + 15f); 55 | } 56 | if(EditorGUI.EndChangeCheck()) 57 | { 58 | m_FixedExposure.value.floatValue += m_Compensation.value.floatValue; 59 | } 60 | 61 | } 62 | 63 | // TODO: See if this can be refactored into a custom VolumeParameterDrawer 64 | void DoExposurePropertyField(SerializedDataParameter exposureProperty) 65 | { 66 | using (var scope = new OverridablePropertyScope(exposureProperty, exposureProperty.displayName, this)) 67 | { 68 | if (!scope.displayed) 69 | return; 70 | 71 | using (new EditorGUILayout.VerticalScope()) 72 | { 73 | EditorGUILayout.LabelField(scope.label); 74 | 75 | var xOffset = EditorGUIUtility.labelWidth + 2; 76 | 77 | var lineRect = EditorGUILayout.GetControlRect(); 78 | lineRect.x += xOffset; 79 | lineRect.width -= xOffset; 80 | 81 | var sliderRect = lineRect; 82 | sliderRect.y -= EditorGUIUtility.singleLineHeight; 83 | k_LightUnitSlider.SetSerializedObject(serializedObject); 84 | k_LightUnitSlider.DrawExposureSlider(exposureProperty.value, sliderRect); 85 | 86 | // GUIContent.none disables horizontal scrolling, use TrTextContent and adjust the rect to make it work. 87 | lineRect.x -= EditorGUIUtility.labelWidth + 2; 88 | lineRect.y += EditorGUIUtility.standardVerticalSpacing; 89 | lineRect.width += EditorGUIUtility.labelWidth + 2; 90 | EditorGUI.PropertyField(lineRect, exposureProperty.value, EditorGUIUtility.TrTextContent(" ")); 91 | } 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Runtime/PostProcessing/Components/Exposure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine.Rendering.HighDefinition; 3 | 4 | namespace UnityEngine.Rendering.Universal 5 | { 6 | /// 7 | /// A volume component that holds settings for the Exposure effect. 8 | /// 9 | [Serializable, VolumeComponentMenuForRenderPipeline("Exposure", typeof(UniversalRenderPipeline))] 10 | [HDRPHelpURLAttribute("Override-Exposure")] 11 | public sealed class Exposure : VolumeComponent, IPostProcessComponent 12 | { 13 | /// 14 | /// Specifies the method that HDRP uses to process exposure. 15 | /// 16 | /// 17 | [Tooltip("Specifies the method that HDRP uses to process exposure.")] 18 | public ExposureModeParameter mode = new ExposureModeParameter(ExposureMode.Fixed); 19 | 20 | /// 21 | /// Sets a static exposure value for Cameras in this Volume. 22 | /// This parameter is only used when is set. 23 | /// 24 | [Tooltip("Sets a static exposure value for Cameras in this Volume.")] 25 | public FloatParameter fixedExposure = new FloatParameter(0f); 26 | /// 27 | /// Sets the compensation that the Camera applies to the calculated exposure value. 28 | /// This parameter is only used when any mode but is set. 29 | /// 30 | [Tooltip("Sets the compensation that the Camera applies to the calculated exposure value.")] 31 | public FloatParameter compensation = new FloatParameter(0f); 32 | 33 | 34 | 35 | public float exposure 36 | { 37 | get => ColorUtils.ConvertEV100ToExposure(fixedExposure.value); 38 | } 39 | 40 | public void SetSkyboxExposure(Material skybox, float ev100, float skyEv) 41 | { 42 | if (skybox == null) return; 43 | 44 | float skyIntensity = ColorUtils.ConvertEV100ToExposure(ev100) * ColorUtils.ConvertEV100ToExposure(-skyEv); 45 | skybox.SetFloat("_Exposure", skyIntensity); 46 | } 47 | 48 | /// 49 | /// Tells if the effect needs to be rendered or not. 50 | /// 51 | /// true if the effect should be rendered, false otherwise. 52 | public bool IsActive() 53 | { 54 | return true; 55 | } 56 | 57 | public bool IsTileCompatible() => true; 58 | } 59 | 60 | /// 61 | /// Methods that HDRP uses to process exposure. 62 | /// 63 | /// 64 | public enum ExposureMode 65 | { 66 | /// 67 | /// Allows you to manually sets the Scene exposure. 68 | /// 69 | Fixed = 0, 70 | 71 | /// 72 | /// Automatically sets the exposure depending on what is on screen. 73 | /// 74 | // Automatic = 1, 75 | 76 | /// 77 | /// Automatically sets the exposure depending on what is on screen and can filter out outliers based on provided settings. 78 | /// 79 | // AutomaticHistogram = 4, 80 | 81 | /// 82 | /// Maps the current Scene exposure to a custom curve. 83 | /// 84 | // CurveMapping = 2, 85 | 86 | /// 87 | /// Uses the current physical Camera settings to set the Scene exposure. 88 | /// 89 | [InspectorName("Physical Camera")] 90 | UsePhysicalCamera = 3 91 | } 92 | 93 | 94 | /// 95 | /// A that holds a value. 96 | /// 97 | [Serializable] 98 | public sealed class ExposureModeParameter : VolumeParameter 99 | { 100 | /// 101 | /// Creates a new instance. 102 | /// 103 | /// The initial value to store in the parameter. 104 | /// The initial override state for the parameter. 105 | public ExposureModeParameter(ExposureMode value, bool overrideState = false) : base(value, overrideState) { } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Editor/Lighting/LightUnit/PiecewiseLightUnitSlider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using UnityEngine; 4 | 5 | namespace UnityEditor.Rendering.Universal 6 | { 7 | /// 8 | /// Formats the provided descriptor into a piece-wise linear slider with contextual slider markers, tooltips, and icons. 9 | /// 10 | class PiecewiseLightUnitSlider : LightUnitSlider 11 | { 12 | struct Piece 13 | { 14 | public Vector2 domain; 15 | public Vector2 range; 16 | 17 | public float directM; 18 | public float directB; 19 | public float inverseM; 20 | public float inverseB; 21 | } 22 | 23 | // Piecewise function indexed by value ranges. 24 | private readonly Dictionary m_PiecewiseFunctionMap = new Dictionary(); 25 | 26 | static void ComputeTransformationParameters(float x0, float x1, float y0, float y1, out float m, out float b) 27 | { 28 | m = (y0 - y1) / (x0 - x1); 29 | b = (m * -x0) + y0; 30 | } 31 | 32 | static float DoTransformation(in float x, in float m, in float b) => (m * x) + b; 33 | 34 | // Ensure clamping to (0,1) as sometimes the function evaluates to slightly below 0 (breaking the handle). 35 | static float ValueToSlider(Piece p, float x) => Mathf.Clamp01(DoTransformation(x, p.inverseM, p.inverseB)); 36 | static float SliderToValue(Piece p, float x) => DoTransformation(x, p.directM, p.directB); 37 | 38 | // Ideally we want a continuous, monotonically increasing function, but this is useful as we can easily fit a 39 | // distribution to a set of (huge) value ranges onto a slider. 40 | public PiecewiseLightUnitSlider(LightUnitSliderUIDescriptor descriptor) : base(descriptor) 41 | { 42 | // Sort the ranges into ascending order 43 | var sortedRanges = m_Descriptor.valueRanges.OrderBy(x => x.value.x).ToArray(); 44 | var sliderDistribution = m_Descriptor.sliderDistribution; 45 | 46 | // Compute the transformation for each value range. 47 | for (int i = 0; i < sortedRanges.Length; i++) 48 | { 49 | var r = sortedRanges[i].value; 50 | 51 | var x0 = sliderDistribution[i + 0]; 52 | var x1 = sliderDistribution[i + 1]; 53 | var y0 = r.x; 54 | var y1 = r.y; 55 | 56 | Piece piece; 57 | piece.domain = new Vector2(x0, x1); 58 | piece.range = new Vector2(y0, y1); 59 | 60 | ComputeTransformationParameters(x0, x1, y0, y1, out piece.directM, out piece.directB); 61 | 62 | // Compute the inverse 63 | ComputeTransformationParameters(y0, y1, x0, x1, out piece.inverseM, out piece.inverseB); 64 | 65 | m_PiecewiseFunctionMap.Add(sortedRanges[i].value, piece); 66 | } 67 | } 68 | 69 | protected override float GetPositionOnSlider(float value, Vector2 valueRange) 70 | { 71 | if (!m_PiecewiseFunctionMap.TryGetValue(valueRange, out var piecewise)) 72 | return -1f; 73 | 74 | return ValueToSlider(piecewise, value); 75 | } 76 | 77 | // Search for the corresponding piece-wise function to a value on the domain and update the input piece to it. 78 | // Returns true if search was successful and an update was made, false otherwise. 79 | bool UpdatePiece(ref Piece piece, float x) 80 | { 81 | foreach (var pair in m_PiecewiseFunctionMap) 82 | { 83 | var p = pair.Value; 84 | 85 | if (x >= p.domain.x && x <= p.domain.y) 86 | { 87 | piece = p; 88 | 89 | return true; 90 | } 91 | } 92 | 93 | return false; 94 | } 95 | 96 | void SliderOutOfBounds(Rect rect, ref float value) 97 | { 98 | EditorGUI.BeginChangeCheck(); 99 | var internalValue = GUI.HorizontalSlider(rect, value, 0f, 1f); 100 | if (EditorGUI.EndChangeCheck()) 101 | { 102 | Piece p = new Piece(); 103 | UpdatePiece(ref p, internalValue); 104 | value = SliderToValue(p, internalValue); 105 | } 106 | } 107 | 108 | protected override void DoSlider(Rect rect, ref float value, Vector2 sliderRange, Vector2 valueRange) 109 | { 110 | // Map the internal slider value to the current piecewise function 111 | if (!m_PiecewiseFunctionMap.TryGetValue(valueRange, out var piece)) 112 | { 113 | // Assume that if the piece is not found, that means the unit value is out of bounds. 114 | SliderOutOfBounds(rect, ref value); 115 | return; 116 | } 117 | 118 | // Maintain an internal value to support a single linear continuous function 119 | EditorGUI.BeginChangeCheck(); 120 | var internalValue = GUI.HorizontalSlider(rect, ValueToSlider(piece, value), 0f, 1f); 121 | if (EditorGUI.EndChangeCheck()) 122 | { 123 | // Ensure that the current function piece is being used to transform the value 124 | UpdatePiece(ref piece, internalValue); 125 | value = SliderToValue(piece, internalValue); 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Editor/Lighting/PhysicalLightEditor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using UnityEngine.Rendering; 4 | using UnityEngine.Rendering.Universal; 5 | 6 | namespace UnityEditor.Rendering.Universal 7 | { 8 | [CanEditMultipleObjects] 9 | [CustomEditorForRenderPipeline(typeof(Light), typeof(UniversalRenderPipelineAsset))] 10 | class PhysicalLightEditor : LightEditor 11 | { 12 | SerializedHDLight m_SerializedHDLight; 13 | 14 | AdditionalLightData[] m_AdditionalLightDatas; 15 | 16 | protected override void OnEnable() 17 | { 18 | base.OnEnable(); 19 | 20 | // Get & automatically add additional HD data if not present 21 | m_AdditionalLightDatas = CoreEditorUtils.GetAdditionalData(targets, AdditionalLightData.InitDefaultHDAdditionalLightData); 22 | m_SerializedHDLight = new SerializedHDLight(m_AdditionalLightDatas, settings); 23 | 24 | ApplyAdditionalComponentsVisibility(true); 25 | 26 | Undo.undoRedoPerformed += ReconstructReferenceToAdditionalDataSO; 27 | } 28 | 29 | internal void ReconstructReferenceToAdditionalDataSO() 30 | { 31 | OnDisable(); 32 | OnEnable(); 33 | 34 | // Serialized object is lossing references after an undo 35 | if (m_SerializedHDLight.serializedObject.targetObject != null) 36 | m_SerializedHDLight.serializedObject.Update(); 37 | } 38 | 39 | void OnDisable() 40 | { 41 | Undo.undoRedoPerformed -= ReconstructReferenceToAdditionalDataSO; 42 | } 43 | 44 | // IsPreset is an internal API - lets reuse the usable part of this function 45 | // 93 is a "magic number" and does not represent a combination of other flags here 46 | internal static bool IsPresetEditor(UnityEditor.Editor editor) 47 | { 48 | return (int)((editor.target as Component).gameObject.hideFlags) == 93; 49 | } 50 | 51 | public override void OnInspectorGUI() 52 | { 53 | m_SerializedHDLight.Update(); 54 | // Remove space before the first collapsible area 55 | EditorGUILayout.Space(-5); 56 | 57 | EditorGUI.BeginChangeCheck(); 58 | 59 | if (IsPresetEditor(this)) 60 | { 61 | URPLightUI.PresetInspector.Draw(m_SerializedHDLight, this); 62 | } 63 | else 64 | { 65 | using (new EditorGUILayout.VerticalScope()) 66 | URPLightUI.Inspector.Draw(m_SerializedHDLight, this); 67 | } 68 | if (EditorGUI.EndChangeCheck()) 69 | { 70 | m_SerializedHDLight.Apply(); 71 | 72 | foreach (var hdLightData in m_AdditionalLightDatas) 73 | { 74 | hdLightData.UpdateAllLightValues(); 75 | } 76 | } 77 | } 78 | 79 | // Internal utilities 80 | void ApplyAdditionalComponentsVisibility(bool hide) 81 | { 82 | // Force to hide the UniversalAdditionalLightData component 83 | var data = CoreEditorUtils.GetAdditionalData(targets); 84 | if (data != null && hide) 85 | { 86 | foreach (var d in data) 87 | d.hideFlags = HideFlags.HideInInspector; 88 | } 89 | } 90 | 91 | protected override void OnSceneGUI() 92 | { 93 | if (!(GraphicsSettings.currentRenderPipeline is UniversalRenderPipelineAsset)) 94 | return; 95 | 96 | if (!(target is Light light) || light == null) 97 | return; 98 | 99 | switch (light.type) 100 | { 101 | case LightType.Spot: 102 | using (new Handles.DrawingScope(Matrix4x4.TRS(light.transform.position, light.transform.rotation, Vector3.one))) 103 | { 104 | CoreLightEditorUtilities.DrawSpotLightGizmo(light); 105 | } 106 | break; 107 | 108 | case LightType.Point: 109 | using (new Handles.DrawingScope(Matrix4x4.TRS(light.transform.position, Quaternion.identity, Vector3.one))) 110 | { 111 | CoreLightEditorUtilities.DrawPointLightGizmo(light); 112 | } 113 | break; 114 | 115 | case LightType.Rectangle: 116 | using (new Handles.DrawingScope(Matrix4x4.TRS(light.transform.position, light.transform.rotation, Vector3.one))) 117 | { 118 | CoreLightEditorUtilities.DrawRectangleLightGizmo(light); 119 | } 120 | break; 121 | 122 | case LightType.Disc: 123 | using (new Handles.DrawingScope(Matrix4x4.TRS(light.transform.position, light.transform.rotation, Vector3.one))) 124 | { 125 | CoreLightEditorUtilities.DrawDiscLightGizmo(light); 126 | } 127 | break; 128 | 129 | case LightType.Directional: 130 | using (new Handles.DrawingScope(Matrix4x4.TRS(light.transform.position, light.transform.rotation, Vector3.one))) 131 | { 132 | CoreLightEditorUtilities.DrawDirectionalLightGizmo(light); 133 | } 134 | break; 135 | 136 | default: 137 | base.OnSceneGUI(); 138 | break; 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Runtime/Lighting/AdditionalLightData.Types.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using UnityEngine.Serialization; 5 | 6 | namespace UnityEngine.Rendering.Universal 7 | { 8 | /// 9 | /// Unit of the lights supported in HDRP 10 | /// 11 | public enum LightUnit 12 | { 13 | /// Total power/flux emitted by the light. 14 | Lumen, 15 | /// Flux per steradian. 16 | Candela, // lm/sr 17 | /// Flux per unit area. 18 | Lux, // lm/m² 19 | /// Flux per unit area and per steradian. 20 | Nits, // lm/m²/sr 21 | /// ISO 100 Exposure Value (https://en.wikipedia.org/wiki/Exposure_value). 22 | Ev100, 23 | } 24 | 25 | internal enum DirectionalLightUnit 26 | { 27 | Lux = LightUnit.Lux, 28 | } 29 | 30 | internal enum AreaLightUnit 31 | { 32 | Lumen = LightUnit.Lumen, 33 | Nits = LightUnit.Nits, 34 | Ev100 = LightUnit.Ev100, 35 | } 36 | 37 | internal enum PunctualLightUnit 38 | { 39 | Lumen = LightUnit.Lumen, 40 | Candela = LightUnit.Candela, 41 | Lux = LightUnit.Lux, 42 | Ev100 = LightUnit.Ev100 43 | } 44 | 45 | public partial class AdditionalLightData 46 | { 47 | [System.NonSerialized] 48 | static Dictionary supportedLightTypeCache = new Dictionary(); 49 | 50 | /// 51 | /// The type of light used. 52 | /// This handle some internal conversion in Light component for culling purpose. 53 | /// 54 | public LightType type 55 | { 56 | get => legacyLight.type; 57 | set 58 | { 59 | if (type != value) 60 | { 61 | switch (value) 62 | { 63 | case LightType.Directional: 64 | legacyLight.type = LightType.Directional; 65 | break; 66 | case LightType.Spot: 67 | legacyLight.type = LightType.Spot; 68 | break; 69 | case LightType.Point: 70 | legacyLight.type = LightType.Point; 71 | break; 72 | case LightType.Area: 73 | legacyLight.type = LightType.Area; // default is Rectangle type, Disc type can be changed via light shape 74 | break; 75 | default: 76 | Debug.Assert(false, $"Unknown {typeof(LightType).Name} {value}."); 77 | break; 78 | } 79 | 80 | // If the current light unit is not supported by the new light type, we change it 81 | var supportedUnits = GetSupportedLightUnits(value); 82 | if (!supportedUnits.Any(u => u == lightUnit)) 83 | lightUnit = supportedUnits.First(); 84 | UpdateAllLightValues(); 85 | } 86 | } 87 | } 88 | 89 | string GetLightTypeName() 90 | { 91 | if (legacyLight.type == LightType.Area) 92 | return $"{legacyLight.type}AreaLight"; 93 | else 94 | { 95 | if (legacyLight.type == LightType.Spot) 96 | return $"{legacyLight.type}SpotLight"; 97 | else 98 | return $"{legacyLight.type}Light"; 99 | } 100 | } 101 | 102 | /// 103 | /// Give the supported lights unit for the given parameters 104 | /// 105 | /// The type of the light 106 | /// the shape of the spot.You can put anything in case it is not a spot light. 107 | /// Array of supported units 108 | public static LightUnit[] GetSupportedLightUnits(LightType type) 109 | { 110 | LightUnit[] supportedTypes; 111 | 112 | // Combine the two light types to access the dictionary 113 | int cacheKey = ((int)type & 0xFF) << 0; 114 | cacheKey |= ((int)LightType.Spot & 0xFF) << 8; 115 | 116 | // We cache the result once they are computed, it avoid garbage generated by Enum.GetValues and Linq. 117 | if (supportedLightTypeCache.TryGetValue(cacheKey, out supportedTypes)) 118 | return supportedTypes; 119 | 120 | if (type == LightType.Area) 121 | supportedTypes = Enum.GetValues(typeof(AreaLightUnit)).Cast().ToArray(); 122 | else if (type == LightType.Directional)// || (type == LightType.Spot && spotLightShape == SpotLightShape.Box)) 123 | supportedTypes = Enum.GetValues(typeof(DirectionalLightUnit)).Cast().ToArray(); 124 | else 125 | supportedTypes = Enum.GetValues(typeof(PunctualLightUnit)).Cast().ToArray(); 126 | 127 | supportedLightTypeCache[cacheKey] = supportedTypes; 128 | 129 | return supportedTypes; 130 | } 131 | 132 | /// 133 | /// Check if the given type is supported by this type and shape. 134 | /// 135 | /// The type of the light 136 | /// the shape of the spot.You can put anything in case it is not a spot light. 137 | /// The unit to check 138 | /// True: this unit is supported 139 | public static bool IsValidLightUnitForType(LightType type, LightUnit unit) 140 | { 141 | LightUnit[] allowedUnits = GetSupportedLightUnits(type); 142 | 143 | return allowedUnits.Any(u => u == unit); 144 | } 145 | 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Editor/Lighting/SerializedHDLight.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using UnityEngine.Rendering.Universal; 5 | 6 | namespace UnityEditor.Rendering.Universal 7 | { 8 | internal class SerializedHDLight : ISerializedLight 9 | { 10 | // URP specific properties 11 | // Combines the UniversalAdditionalLightData SerializedProperties to HighDefinitionLightData 12 | public UniversalAdditionalLightData additionalLightData => lightsAdditionalData[0]; 13 | public UniversalAdditionalLightData[] lightsAdditionalData { get; } 14 | 15 | // URP Light Properties 16 | public SerializedProperty useAdditionalDataProp { get; } // Does light use shadow bias settings defined in UniversalRP asset file? 17 | public SerializedProperty additionalLightsShadowResolutionTierProp { get; } // Index of the AdditionalLights ShadowResolution Tier 18 | public SerializedProperty softShadowQualityProp { get; } // Per light soft shadow filtering quality. 19 | public SerializedProperty lightCookieSizeProp { get; } // Multi dimensional light cookie size replacing `cookieSize` in legacy light. 20 | public SerializedProperty lightCookieOffsetProp { get; } // Multi dimensional light cookie offset. 21 | 22 | // Light layers related 23 | public SerializedProperty renderingLayers { get; } 24 | public SerializedProperty customShadowLayers { get; } 25 | public SerializedProperty shadowRenderingLayers { get; } 26 | 27 | // Common properties 28 | public SerializedProperty intensity { get; } 29 | 30 | // HDRP specific properties 31 | public SerializedProperty enableSpotReflector; 32 | public SerializedProperty luxAtDistance; 33 | 34 | public SerializedProperty aspectRatio; 35 | 36 | /// TODO: Add Volumetric UI if we has Volumetric fog 37 | public SerializedProperty volumetricDimmer; 38 | public SerializedProperty volumetricFadeDistance; 39 | public SerializedProperty lightUnit; 40 | 41 | /// TODO: Add Celestial Body UI if we has Physical Sky 42 | // Celestial Body 43 | public SerializedProperty interactsWithSky; 44 | public SerializedProperty angularDiameter; 45 | public SerializedProperty flareSize; 46 | public SerializedProperty flareTint; 47 | public SerializedProperty flareFalloff; 48 | public SerializedProperty surfaceTexture; 49 | public SerializedProperty surfaceTint; 50 | public SerializedProperty distance; 51 | 52 | // Editor stuff 53 | public SerializedProperty useVolumetric; 54 | 55 | // Shadow datas 56 | public SerializedProperty shadowDimmer; 57 | public SerializedProperty volumetricShadowDimmer; 58 | public SerializedProperty shadowFadeDistance; 59 | 60 | public SerializedObject serializedObject { get; } 61 | // This is Serialized UniversalAdditionalLightData for URP only, not in use for HighDefinition Pipeline, 62 | // However we will also use it here to include that necessary SerializedLight data in SerializedHDLight 63 | public SerializedObject serializedAdditionalDataObject { get; private set;} 64 | 65 | private SerializedObject lightGameObject; 66 | 67 | //contain serialized property that are mainly used to draw inspector 68 | public LightEditor.Settings settings { get; } 69 | 70 | //type is converted on the fly each time so we cannot have SerializedProperty on it 71 | public LightType type 72 | { 73 | get => haveMultipleTypeValue 74 | ? (LightType)(-1) //as serialize property on enum when mixed value state happens 75 | : (serializedObject.targetObjects[0] as AdditionalLightData).type; 76 | set 77 | { 78 | //Note: type is split in both component 79 | var undoObjects = serializedObject.targetObjects.SelectMany((Object x) => new Object[] { x, (x as AdditionalLightData).legacyLight }).ToArray(); 80 | Undo.RecordObjects(undoObjects, "Change light type"); 81 | var objects = serializedObject.targetObjects; 82 | for (int index = 0; index < objects.Length; ++index) 83 | (objects[index] as AdditionalLightData).type = value; 84 | serializedObject.Update(); 85 | } 86 | } 87 | 88 | bool haveMultipleTypeValue 89 | { 90 | get 91 | { 92 | var objects = serializedObject.targetObjects; 93 | LightType value = (objects[0] as AdditionalLightData).type; 94 | for (int index = 1; index < objects.Length; ++index) 95 | if (value != (objects[index] as AdditionalLightData).type) 96 | return true; 97 | return false; 98 | } 99 | } 100 | 101 | public SerializedHDLight(AdditionalLightData[] lightDatas, LightEditor.Settings settings) 102 | { 103 | serializedObject = new SerializedObject(lightDatas); 104 | this.settings = settings; 105 | 106 | // Include the UniversalAdditionalLightData properties 107 | lightsAdditionalData = CoreEditorUtils.GetAdditionalData(serializedObject.targetObjects); 108 | serializedAdditionalDataObject = new SerializedObject(lightsAdditionalData); 109 | 110 | useAdditionalDataProp = serializedAdditionalDataObject.FindProperty("m_UsePipelineSettings"); 111 | additionalLightsShadowResolutionTierProp = serializedAdditionalDataObject.FindProperty("m_AdditionalLightsShadowResolutionTier"); 112 | softShadowQualityProp = serializedAdditionalDataObject.FindProperty("m_SoftShadowQuality"); 113 | lightCookieSizeProp = serializedAdditionalDataObject.FindProperty("m_LightCookieSize"); 114 | lightCookieOffsetProp = serializedAdditionalDataObject.FindProperty("m_LightCookieOffset"); 115 | 116 | renderingLayers = serializedAdditionalDataObject.FindProperty("m_RenderingLayers"); 117 | customShadowLayers = serializedAdditionalDataObject.FindProperty("m_CustomShadowLayers"); 118 | shadowRenderingLayers = serializedAdditionalDataObject.FindProperty("m_ShadowRenderingLayers"); 119 | 120 | settings.ApplyModifiedProperties(); // end of the UniversalAdditionalLightData 121 | 122 | using (var o = new PropertyFetcher(serializedObject)) 123 | { 124 | intensity = o.Find("m_Intensity"); 125 | enableSpotReflector = o.Find("m_EnableSpotReflector"); 126 | luxAtDistance = o.Find("m_LuxAtDistance"); 127 | 128 | volumetricDimmer = o.Find("m_VolumetricDimmer"); 129 | volumetricFadeDistance = o.Find("m_VolumetricFadeDistance"); 130 | lightUnit = o.Find("m_LightUnit"); 131 | 132 | aspectRatio = o.Find("m_AspectRatio"); 133 | 134 | interactsWithSky = o.Find("m_InteractsWithSky"); 135 | angularDiameter = o.Find("m_AngularDiameter"); 136 | flareSize = o.Find("m_FlareSize"); 137 | flareFalloff = o.Find("m_FlareFalloff"); 138 | flareTint = o.Find("m_FlareTint"); 139 | surfaceTexture = o.Find("m_SurfaceTexture"); 140 | surfaceTint = o.Find("m_SurfaceTint"); 141 | distance = o.Find("m_Distance"); 142 | 143 | useVolumetric = o.Find("useVolumetric"); 144 | 145 | // Shadow datas: 146 | shadowDimmer = o.Find("m_ShadowDimmer"); 147 | volumetricShadowDimmer = o.Find("m_VolumetricShadowDimmer"); 148 | shadowFadeDistance = o.Find("m_ShadowFadeDistance"); 149 | } 150 | 151 | lightGameObject = new SerializedObject(serializedObject.targetObjects.Select(ld => ((AdditionalLightData)ld).gameObject).ToArray()); 152 | } 153 | 154 | public void Update() 155 | { 156 | // Case 1182968 157 | // For some reasons, the is different cache is not updated while we actually have different 158 | // values for shadowResolution.level 159 | // So we force the update here as a workaround 160 | serializedObject.SetIsDifferentCacheDirty(); 161 | 162 | serializedObject.Update(); 163 | settings.Update(); 164 | 165 | lightGameObject.Update(); 166 | 167 | serializedAdditionalDataObject.Update(); // URP 168 | } 169 | 170 | public void Apply() 171 | { 172 | serializedObject.ApplyModifiedProperties(); 173 | settings.ApplyModifiedProperties(); 174 | 175 | serializedAdditionalDataObject.ApplyModifiedProperties(); // URP 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /Editor/Lighting/URPLightUI.PhysicalUnit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using UnityEngine; 5 | using UnityEngine.Rendering.Universal; 6 | 7 | namespace UnityEditor.Rendering.Universal 8 | { 9 | internal partial class URPLightUI 10 | { 11 | enum AdditionalProperties 12 | { 13 | General = 1 << 0, 14 | Shape = 1 << 1, 15 | Emission = 1 << 2, 16 | Shadow = 1 << 3, 17 | } 18 | 19 | readonly static LightUnitSliderUIDrawer k_LightUnitSliderUIDrawer = new LightUnitSliderUIDrawer(); 20 | 21 | static void UpdateLightIntensityUnit(SerializedHDLight serialized, Editor owner) 22 | { 23 | LightType lightType = serialized.type; 24 | // Box are local directional light 25 | if (lightType == LightType.Directional) 26 | { 27 | serialized.lightUnit.SetEnumValue((LightUnit)DirectionalLightUnit.Lux); 28 | // We need to reset luxAtDistance to neutral when changing to (local) directional light, otherwise first display value ins't correct 29 | serialized.luxAtDistance.floatValue = 1.0f; 30 | } 31 | } 32 | 33 | internal static LightUnit DrawLightIntensityUnitPopup(Rect rect, LightUnit value, LightType type) 34 | { 35 | switch (type) 36 | { 37 | case LightType.Directional: 38 | return (LightUnit)EditorGUI.EnumPopup(rect, (DirectionalLightUnit)value); 39 | case LightType.Point: 40 | return (LightUnit)EditorGUI.EnumPopup(rect, (PunctualLightUnit)value); 41 | case LightType.Spot: 42 | // if (spotLightShape == SpotLightShape.Box) 43 | // return (LightUnit)EditorGUI.EnumPopup(rect, (DirectionalLightUnit)value); 44 | // else 45 | return (LightUnit)EditorGUI.EnumPopup(rect, (PunctualLightUnit)value); 46 | default: 47 | return (LightUnit)EditorGUI.EnumPopup(rect, (AreaLightUnit)value); 48 | } 49 | } 50 | 51 | static void DrawLightIntensityUnitPopup(Rect rect, SerializedHDLight serialized, Editor owner) 52 | { 53 | LightUnit oldLigthUnit = serialized.lightUnit.GetEnumValue(); 54 | 55 | EditorGUI.BeginChangeCheck(); 56 | 57 | EditorGUI.BeginProperty(rect, GUIContent.none, serialized.lightUnit); 58 | EditorGUI.showMixedValue = serialized.lightUnit.hasMultipleDifferentValues; 59 | var selectedLightUnit = DrawLightIntensityUnitPopup(rect, serialized.lightUnit.GetEnumValue(), serialized.type); 60 | EditorGUI.showMixedValue = false; 61 | EditorGUI.EndProperty(); 62 | 63 | if (EditorGUI.EndChangeCheck()) 64 | { 65 | ConvertLightIntensity(oldLigthUnit, selectedLightUnit, serialized, owner); 66 | serialized.lightUnit.SetEnumValue(selectedLightUnit); 67 | } 68 | } 69 | 70 | internal static void ConvertLightIntensity(LightUnit oldLightUnit, LightUnit newLightUnit, SerializedHDLight serialized, Editor owner) 71 | { 72 | serialized.intensity.floatValue = ConvertLightIntensity(oldLightUnit, newLightUnit, serialized, owner, serialized.intensity.floatValue); 73 | } 74 | 75 | internal static float ConvertLightIntensity(LightUnit oldLightUnit, LightUnit newLightUnit, SerializedHDLight serialized, Editor owner, float intensity) 76 | { 77 | Light light = (Light)owner.target; 78 | 79 | // For punctual lights 80 | LightType lightType = serialized.type; 81 | switch (lightType) 82 | { 83 | case LightType.Directional: 84 | case LightType.Point: 85 | case LightType.Spot: 86 | // Lumen -> 87 | if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Candela) 88 | intensity = LightUtils.ConvertPunctualLightLumenToCandela(lightType, intensity, light.intensity, serialized.enableSpotReflector.boolValue); 89 | else if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Lux) 90 | intensity = LightUtils.ConvertPunctualLightLumenToLux(lightType, intensity, light.intensity, serialized.enableSpotReflector.boolValue, 91 | serialized.luxAtDistance.floatValue); 92 | else if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Ev100) 93 | intensity = LightUtils.ConvertPunctualLightLumenToEv(lightType, intensity, light.intensity, serialized.enableSpotReflector.boolValue); 94 | // Candela -> 95 | else if (oldLightUnit == LightUnit.Candela && newLightUnit == LightUnit.Lumen) 96 | intensity = LightUtils.ConvertPunctualLightCandelaToLumen(lightType, intensity, serialized.enableSpotReflector.boolValue, 97 | light.spotAngle, serialized.aspectRatio.floatValue); 98 | else if (oldLightUnit == LightUnit.Candela && newLightUnit == LightUnit.Lux) 99 | intensity = LightUtils.ConvertCandelaToLux(intensity, serialized.luxAtDistance.floatValue); 100 | else if (oldLightUnit == LightUnit.Candela && newLightUnit == LightUnit.Ev100) 101 | intensity = LightUtils.ConvertCandelaToEv(intensity); 102 | // Lux -> 103 | else if (oldLightUnit == LightUnit.Lux && newLightUnit == LightUnit.Lumen) 104 | intensity = LightUtils.ConvertPunctualLightLuxToLumen(lightType, intensity, serialized.enableSpotReflector.boolValue, 105 | light.spotAngle, serialized.aspectRatio.floatValue, serialized.luxAtDistance.floatValue); 106 | else if (oldLightUnit == LightUnit.Lux && newLightUnit == LightUnit.Candela) 107 | intensity = LightUtils.ConvertLuxToCandela(intensity, serialized.luxAtDistance.floatValue); 108 | else if (oldLightUnit == LightUnit.Lux && newLightUnit == LightUnit.Ev100) 109 | intensity = LightUtils.ConvertLuxToEv(intensity, serialized.luxAtDistance.floatValue); 110 | // EV100 -> 111 | else if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Lumen) 112 | intensity = LightUtils.ConvertPunctualLightEvToLumen(lightType, intensity, serialized.enableSpotReflector.boolValue, 113 | light.spotAngle, serialized.aspectRatio.floatValue); 114 | else if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Candela) 115 | intensity = LightUtils.ConvertEvToCandela(intensity); 116 | else if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Lux) 117 | intensity = LightUtils.ConvertEvToLux(intensity, serialized.luxAtDistance.floatValue); 118 | break; 119 | 120 | case LightType.Area: 121 | if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Nits) 122 | intensity = LightUtils.ConvertAreaLightLumenToLuminance(serialized.type, intensity, serialized.settings.areaSizeX.floatValue, serialized.settings.areaSizeY.floatValue); 123 | if (oldLightUnit == LightUnit.Nits && newLightUnit == LightUnit.Lumen) 124 | intensity = LightUtils.ConvertAreaLightLuminanceToLumen(serialized.type, intensity, serialized.settings.areaSizeX.floatValue, serialized.settings.areaSizeY.floatValue); 125 | if (oldLightUnit == LightUnit.Nits && newLightUnit == LightUnit.Ev100) 126 | intensity = LightUtils.ConvertLuminanceToEv(intensity); 127 | if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Nits) 128 | intensity = LightUtils.ConvertEvToLuminance(intensity); 129 | if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Lumen) 130 | intensity = LightUtils.ConvertAreaLightEvToLumen(serialized.type, intensity, serialized.settings.areaSizeX.floatValue, serialized.settings.areaSizeY.floatValue); 131 | if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Ev100) 132 | intensity = LightUtils.ConvertAreaLightLumenToEv(serialized.type, intensity, serialized.settings.areaSizeX.floatValue, serialized.settings.areaSizeY.floatValue); 133 | break; 134 | 135 | default: 136 | case (LightType)(-1): // multiple different values 137 | break; // do nothing 138 | } 139 | 140 | return intensity; 141 | } 142 | 143 | static void DrawLightIntensityGUILayout(SerializedHDLight serialized, Editor owner) 144 | { 145 | // Match const defined in EditorGUI.cs 146 | const int k_IndentPerLevel = 15; 147 | 148 | const int k_ValueUnitSeparator = 2; 149 | const int k_UnitWidth = 100; 150 | 151 | float indent = k_IndentPerLevel * EditorGUI.indentLevel; 152 | 153 | Rect lineRect = EditorGUILayout.GetControlRect(); 154 | Rect labelRect = lineRect; 155 | labelRect.width = EditorGUIUtility.labelWidth; 156 | 157 | // Expand to reach both lines of the intensity field. 158 | var interlineOffset = EditorGUIUtility.singleLineHeight + 2f; 159 | labelRect.height += interlineOffset; 160 | 161 | //handling of prefab overrides in a parent label 162 | GUIContent parentLabel = s_Styles.lightIntensity; 163 | parentLabel = EditorGUI.BeginProperty(labelRect, parentLabel, serialized.lightUnit); 164 | parentLabel = EditorGUI.BeginProperty(labelRect, parentLabel, serialized.intensity); 165 | { 166 | // Restore the original rect for actually drawing the label. 167 | labelRect.height -= interlineOffset; 168 | 169 | EditorGUI.LabelField(labelRect, parentLabel); 170 | } 171 | EditorGUI.EndProperty(); 172 | EditorGUI.EndProperty(); 173 | 174 | // Draw the light unit slider + icon + tooltip 175 | Rect lightUnitSliderRect = lineRect; // TODO: Move the value and unit rects to new line 176 | lightUnitSliderRect.x += EditorGUIUtility.labelWidth + k_ValueUnitSeparator; 177 | lightUnitSliderRect.width -= EditorGUIUtility.labelWidth + k_ValueUnitSeparator; 178 | 179 | var lightType = serialized.type; 180 | var lightUnit = serialized.lightUnit.GetEnumValue(); 181 | k_LightUnitSliderUIDrawer.SetSerializedObject(serialized.serializedObject); 182 | k_LightUnitSliderUIDrawer.Draw(lightType, lightUnit, serialized.intensity, lightUnitSliderRect, serialized, owner); 183 | 184 | // We use PropertyField to draw the value to keep the handle at left of the field 185 | // This will apply the indent again thus we need to remove it time for alignment 186 | Rect valueRect = EditorGUILayout.GetControlRect(); 187 | labelRect.width = EditorGUIUtility.labelWidth; 188 | valueRect.width += indent - k_ValueUnitSeparator - k_UnitWidth; 189 | Rect unitRect = valueRect; 190 | unitRect.x += valueRect.width - indent + k_ValueUnitSeparator; 191 | unitRect.width = k_UnitWidth + .5f; 192 | 193 | // Draw the unit textfield 194 | EditorGUI.BeginChangeCheck(); 195 | EditorGUI.PropertyField(valueRect, serialized.intensity, CoreEditorStyles.empty); 196 | DrawLightIntensityUnitPopup(unitRect, serialized, owner); 197 | 198 | if (EditorGUI.EndChangeCheck()) 199 | { 200 | serialized.intensity.floatValue = Mathf.Max(serialized.intensity.floatValue, 0.0f); 201 | } 202 | } 203 | 204 | static void DrawEmissionContent(SerializedHDLight serialized, Editor owner) 205 | { 206 | LightType lightType = serialized.type; 207 | // SpotLightShape spotLightShape = serialized.spotLightShape.GetEnumValue(); 208 | LightUnit lightUnit = serialized.lightUnit.GetEnumValue(); 209 | 210 | if (lightType != LightType.Directional 211 | // Box are local directional light and shouldn't display the Lux At widget. It use only lux 212 | // && !(lightType == LightType.Spot && (spotLightShape == SpotLightShape.Box)) 213 | && lightUnit == (LightUnit)PunctualLightUnit.Lux) 214 | { 215 | EditorGUI.indentLevel++; 216 | EditorGUI.BeginChangeCheck(); 217 | EditorGUILayout.PropertyField(serialized.luxAtDistance, s_Styles.luxAtDistance); 218 | if (EditorGUI.EndChangeCheck()) 219 | { 220 | serialized.luxAtDistance.floatValue = Mathf.Max(serialized.luxAtDistance.floatValue, 0.01f); 221 | } 222 | EditorGUI.indentLevel--; 223 | } 224 | 225 | if (lightType == LightType.Spot 226 | // Display reflector only when showing additional properties. 227 | && (lightUnit == (int)PunctualLightUnit.Lumen && k_AdditionalPropertiesState[AdditionalProperties.Emission])) 228 | { 229 | EditorGUI.indentLevel++; 230 | EditorGUILayout.PropertyField(serialized.enableSpotReflector, s_Styles.enableSpotReflector); 231 | EditorGUI.indentLevel--; 232 | } 233 | 234 | if (lightType != LightType.Directional) 235 | { 236 | EditorGUI.BeginChangeCheck(); 237 | #if UNITY_2020_1_OR_NEWER 238 | serialized.settings.DrawRange(); 239 | #else 240 | serialized.settings.DrawRange(false); 241 | #endif 242 | // Make sure the range is not 0.0 243 | serialized.settings.range.floatValue = Mathf.Max(0.001f, serialized.settings.range.floatValue); 244 | 245 | if (EditorGUI.EndChangeCheck()) 246 | { 247 | // For GI we need to detect any change on additional data and call SetLightDirty + For intensity we need to detect light shape change 248 | SetLightsDirty(owner); // Should be apply only to parameter that's affect GI, but make the code cleaner 249 | } 250 | } 251 | 252 | serialized.settings.DrawBounceIntensity(); 253 | 254 | // EditorGUI.BeginChangeCheck(); // For GI we need to detect any change on additional data and call SetLightDirty 255 | 256 | DrawLightCookieContent(serialized, owner); // URP Cookie 257 | 258 | // if (EditorGUI.EndChangeCheck()) 259 | // { 260 | // SetLightsDirty(owner); // Should be apply only to parameter that's affect GI, but make the code cleaner 261 | // } 262 | } 263 | 264 | // Display reflector only when showing additional properties. 265 | static void DrawEmissionAdditionalContent(SerializedHDLight serialized, Editor owner){} 266 | 267 | static void SetLightsDirty(Editor owner) 268 | { 269 | foreach (Light light in owner.targets) 270 | light.SetLightDirty(); // Should be apply only to parameter that's affect GI, but make the code cleaner 271 | } 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /Editor/Lighting/URPLightUI.Drawers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | using UnityEngine; 6 | using UnityEngine.Rendering; 7 | using UnityEngine.Rendering.Universal; 8 | #if XR_MANAGEMENT_4_0_1_OR_NEWER 9 | using UnityEditor.XR.Management; 10 | #endif 11 | 12 | namespace UnityEditor.Rendering.Universal 13 | { 14 | using CED = CoreEditorDrawer; 15 | 16 | internal partial class URPLightUI 17 | { 18 | [URPHelpURL("light-component")] 19 | enum Expandable 20 | { 21 | General = 1 << 0, 22 | Shape = 1 << 1, 23 | Emission = 1 << 2, 24 | Rendering = 1 << 3, 25 | Shadows = 1 << 4, 26 | LightCookie = 1 << 5, 27 | 28 | /// TODO 29 | // add 30 | Volumetric = 1 << 6, 31 | CelestialBody = 1 << 7, 32 | } 33 | 34 | static readonly ExpandedState k_ExpandedState = new(~-1, "URP"); 35 | readonly static AdditionalPropertiesState k_AdditionalPropertiesState = new (0, "URP"); 36 | 37 | public static readonly CED.IDrawer Inspector = CED.Group( 38 | CED.Conditional( 39 | (_, __) => 40 | { 41 | if (SceneView.lastActiveSceneView == null) 42 | return false; 43 | 44 | #if UNITY_2019_1_OR_NEWER 45 | var sceneLighting = SceneView.lastActiveSceneView.sceneLighting; 46 | #else 47 | var sceneLighting = SceneView.lastActiveSceneView.m_SceneLighting; 48 | #endif 49 | return !sceneLighting; 50 | }, 51 | (_, __) => EditorGUILayout.HelpBox(Styles.DisabledLightWarning.text, MessageType.Warning)), 52 | CED.FoldoutGroup(LightUI.Styles.generalHeader, 53 | Expandable.General, 54 | k_ExpandedState, 55 | DrawGeneralContent), 56 | CED.Conditional( 57 | (serializedLight, editor) => !serializedLight.settings.lightType.hasMultipleDifferentValues && serializedLight.settings.light.type == LightType.Spot, 58 | CED.FoldoutGroup(LightUI.Styles.shapeHeader, Expandable.Shape, k_ExpandedState, DrawSpotShapeContent)), 59 | CED.Conditional( 60 | (serializedLight, editor) => 61 | { 62 | if (serializedLight.settings.lightType.hasMultipleDifferentValues) 63 | return false; 64 | var lightType = serializedLight.settings.light.type; 65 | return lightType == LightType.Rectangle || lightType == LightType.Disc; 66 | }, 67 | CED.FoldoutGroup(LightUI.Styles.shapeHeader, Expandable.Shape, k_ExpandedState, DrawAreaShapeContent)), 68 | CED.AdditionalPropertiesFoldoutGroup(LightUI.Styles.emissionHeader, Expandable.Emission, k_ExpandedState, 69 | AdditionalProperties.Emission, k_AdditionalPropertiesState, 70 | CED.Group( 71 | LightUI.DrawColor, 72 | DrawLightIntensityGUILayout, 73 | DrawEmissionContent), 74 | DrawEmissionAdditionalContent), 75 | CED.FoldoutGroup(LightUI.Styles.renderingHeader, 76 | Expandable.Rendering, 77 | k_ExpandedState, 78 | DrawRenderingContent), 79 | CED.FoldoutGroup(LightUI.Styles.shadowHeader, 80 | Expandable.Shadows, 81 | k_ExpandedState, 82 | DrawShadowsContent) 83 | ); 84 | 85 | static Func s_SetGizmosDirty = SetGizmosDirty(); 86 | static Func SetGizmosDirty() 87 | { 88 | var type = Type.GetType("UnityEditor.AnnotationUtility,UnityEditor"); 89 | var method = type.GetMethod("SetGizmosDirty", BindingFlags.Static | BindingFlags.NonPublic); 90 | var lambda = Expression.Lambda>(Expression.Call(method)); 91 | return lambda.Compile(); 92 | } 93 | 94 | static Action k_SliderWithTexture = GetSliderWithTexture(); 95 | static Action GetSliderWithTexture() 96 | { 97 | //quicker than standard reflection as it is compiled 98 | var paramLabel = Expression.Parameter(typeof(GUIContent), "label"); 99 | var paramProperty = Expression.Parameter(typeof(SerializedProperty), "property"); 100 | var paramSettings = Expression.Parameter(typeof(LightEditor.Settings), "settings"); 101 | System.Reflection.MethodInfo sliderWithTextureInfo = typeof(EditorGUILayout) 102 | .GetMethod( 103 | "SliderWithTexture", 104 | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static, 105 | null, 106 | System.Reflection.CallingConventions.Any, 107 | new[] { typeof(GUIContent), typeof(SerializedProperty), typeof(float), typeof(float), typeof(float), typeof(Texture2D), typeof(GUILayoutOption[]) }, 108 | null); 109 | var sliderWithTextureCall = Expression.Call( 110 | sliderWithTextureInfo, 111 | paramLabel, 112 | paramProperty, 113 | Expression.Constant((float)typeof(LightEditor.Settings).GetField("kMinKelvin", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetRawConstantValue()), 114 | Expression.Constant((float)typeof(LightEditor.Settings).GetField("kMaxKelvin", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetRawConstantValue()), 115 | Expression.Constant((float)typeof(LightEditor.Settings).GetField("kSliderPower", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetRawConstantValue()), 116 | Expression.Field(paramSettings, typeof(LightEditor.Settings).GetField("m_KelvinGradientTexture", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)), 117 | Expression.Constant(null, typeof(GUILayoutOption[]))); 118 | var lambda = Expression.Lambda>(sliderWithTextureCall, paramLabel, paramProperty, paramSettings); 119 | return lambda.Compile(); 120 | } 121 | 122 | static void DrawGeneralContent(SerializedHDLight serializedLight, Editor owner) 123 | { 124 | DrawGeneralContentInternal(serializedLight, owner, isInPreset: false); 125 | } 126 | 127 | static void DrawGeneralContentPreset(SerializedHDLight serializedLight, Editor owner) 128 | { 129 | DrawGeneralContentInternal(serializedLight, owner, isInPreset: true); 130 | } 131 | 132 | static void DrawGeneralContentInternal(SerializedHDLight serializedLight, Editor owner, bool isInPreset) 133 | { 134 | // To the user, we will only display it as a area light, but under the hood, we have Rectangle and Disc. This is not to confuse people 135 | // who still use our legacy light inspector. 136 | 137 | int selectedLightType = serializedLight.settings.lightType.intValue; 138 | 139 | // Handle all lights that are not in the default set 140 | if (!Styles.LightTypeValues.Contains(serializedLight.settings.lightType.intValue)) 141 | { 142 | if (serializedLight.settings.lightType.intValue == (int)LightType.Disc) 143 | { 144 | selectedLightType = (int)LightType.Rectangle; 145 | } 146 | } 147 | 148 | var rect = EditorGUILayout.GetControlRect(); 149 | EditorGUI.BeginProperty(rect, Styles.Type, serializedLight.settings.lightType); 150 | EditorGUI.BeginChangeCheck(); 151 | int type = EditorGUI.IntPopup(rect, Styles.Type, selectedLightType, Styles.LightTypeTitles, Styles.LightTypeValues); 152 | 153 | if (EditorGUI.EndChangeCheck()) 154 | { 155 | // Check for Supported LightUnits when set the new light type 156 | serializedLight.type = (LightType)type; //also register undo 157 | 158 | UpdateLightIntensityUnit(serializedLight, owner); 159 | 160 | SetLightsDirty(owner); 161 | // s_SetGizmosDirty(); 162 | // serializedLight.settings.lightType.intValue = type; 163 | } 164 | EditorGUI.EndProperty(); 165 | 166 | Light light = serializedLight.settings.light; 167 | var lightType = light.type; 168 | if (LightType.Directional != lightType && light == RenderSettings.sun) 169 | { 170 | EditorGUILayout.HelpBox(Styles.SunSourceWarning.text, MessageType.Warning); 171 | } 172 | 173 | if (!serializedLight.settings.lightType.hasMultipleDifferentValues) 174 | { 175 | using (new EditorGUI.DisabledScope(serializedLight.settings.isAreaLightType)) 176 | serializedLight.settings.DrawLightmapping(); 177 | 178 | if (serializedLight.settings.isAreaLightType && serializedLight.settings.lightmapping.intValue != (int)LightmapBakeType.Baked) 179 | { 180 | serializedLight.settings.lightmapping.intValue = (int)LightmapBakeType.Baked; 181 | serializedLight.Apply(); 182 | } 183 | } 184 | 185 | 186 | } 187 | 188 | internal static void SyncLightAndShadowLayers(SerializedHDLight serializedHdLight, SerializedProperty serialized) 189 | { 190 | // If we're not in decoupled mode for light layers, we sync light with shadow layers. 191 | // In mixed state, it makes sense to do it only on Light that links the mode. 192 | foreach (var lightTarget in serializedHdLight.serializedObject.targetObjects) 193 | { 194 | var additionData = (lightTarget as Component).gameObject.GetComponent(); 195 | if (additionData.customShadowLayers) 196 | continue; 197 | 198 | // Light target = lightTarget as Light; 199 | Light target = additionData.light; 200 | if (target.renderingLayerMask != serialized.intValue) 201 | target.renderingLayerMask = serialized.intValue; 202 | } 203 | } 204 | 205 | static void DrawSpotShapeContent(SerializedHDLight serializedHdLight, Editor owner) 206 | { 207 | EditorGUI.indentLevel--; 208 | serializedHdLight.settings.DrawInnerAndOuterSpotAngle(); 209 | EditorGUI.indentLevel++; 210 | } 211 | 212 | static void DrawAreaShapeContent(SerializedHDLight serializedHdLight, Editor owner) 213 | { 214 | int selectedShape = serializedHdLight.settings.isAreaLightType ? serializedHdLight.settings.lightType.intValue : 0; 215 | 216 | // Handle all lights that are not in the default set 217 | if (!Styles.LightTypeValues.Contains(serializedHdLight.settings.lightType.intValue)) 218 | { 219 | if (serializedHdLight.settings.lightType.intValue == (int)LightType.Disc) 220 | { 221 | selectedShape = (int)LightType.Disc; 222 | } 223 | } 224 | 225 | var rect = EditorGUILayout.GetControlRect(); 226 | EditorGUI.BeginProperty(rect, Styles.AreaLightShapeContent, serializedHdLight.settings.lightType); 227 | EditorGUI.BeginChangeCheck(); 228 | int shape = EditorGUI.IntPopup(rect, Styles.AreaLightShapeContent, selectedShape, Styles.AreaLightShapeTitles, Styles.AreaLightShapeValues); 229 | 230 | if (EditorGUI.EndChangeCheck()) 231 | { 232 | Undo.RecordObject(serializedHdLight.settings.light, "Adjust Light Shape"); 233 | serializedHdLight.settings.lightType.intValue = shape; 234 | } 235 | EditorGUI.EndProperty(); 236 | 237 | using (new EditorGUI.IndentLevelScope()) 238 | serializedHdLight.settings.DrawArea(); 239 | } 240 | 241 | // Override by URPLightUI.PhysicalUnit ! 242 | /*static void DrawEmissionContent(SerializedLight serializedLight, Editor owner) 243 | { 244 | serializedLight.settings.DrawIntensity(); 245 | serializedLight.settings.DrawBounceIntensity(); 246 | 247 | if (!serializedLight.settings.lightType.hasMultipleDifferentValues) 248 | { 249 | var lightType = serializedLight.settings.light.type; 250 | if (lightType != LightType.Directional) 251 | { 252 | #if UNITY_2020_1_OR_NEWER 253 | serializedLight.settings.DrawRange(); 254 | #else 255 | serializedLight.settings.DrawRange(false); 256 | #endif 257 | } 258 | } 259 | 260 | DrawLightCookieContent(serializedLight, owner); 261 | }*/ 262 | 263 | static void DrawRenderingContent(SerializedHDLight serializedLight, Editor owner) 264 | { 265 | if (serializedLight.settings.light.type != LightType.Rectangle && 266 | !serializedLight.settings.isCompletelyBaked) 267 | { 268 | EditorGUI.BeginChangeCheck(); 269 | GUI.enabled = UniversalRenderPipeline.asset.useRenderingLayers; 270 | EditorUtils.DrawRenderingLayerMask( 271 | serializedLight.renderingLayers, 272 | UniversalRenderPipeline.asset.useRenderingLayers ? Styles.RenderingLayers : Styles.RenderingLayersDisabled 273 | ); 274 | GUI.enabled = true; 275 | if (EditorGUI.EndChangeCheck()) 276 | { 277 | if (!serializedLight.customShadowLayers.boolValue) 278 | SyncLightAndShadowLayers(serializedLight, serializedLight.renderingLayers); 279 | } 280 | } 281 | EditorGUILayout.PropertyField(serializedLight.settings.cullingMask, Styles.CullingMask); 282 | if (serializedLight.settings.cullingMask.intValue != -1) 283 | { 284 | EditorGUILayout.HelpBox(Styles.CullingMaskWarning.text, MessageType.Info); 285 | } 286 | } 287 | 288 | static void DrawShadowsContent(SerializedHDLight serializedLight, Editor owner) 289 | { 290 | if (serializedLight.settings.lightType.hasMultipleDifferentValues) 291 | { 292 | EditorGUILayout.HelpBox("Cannot multi edit shadows from different light types.", MessageType.Info); 293 | return; 294 | } 295 | 296 | serializedLight.settings.DrawShadowsType(); 297 | 298 | if (serializedLight.settings.shadowsType.hasMultipleDifferentValues) 299 | { 300 | EditorGUILayout.HelpBox("Cannot multi edit different shadow types", MessageType.Info); 301 | return; 302 | } 303 | 304 | if (serializedLight.settings.light.shadows == LightShadows.None) 305 | return; 306 | 307 | var lightType = serializedLight.settings.light.type; 308 | 309 | using (new EditorGUI.IndentLevelScope()) 310 | { 311 | if (serializedLight.settings.isBakedOrMixed) 312 | { 313 | switch (lightType) 314 | { 315 | // Baked Shadow radius 316 | case LightType.Point: 317 | case LightType.Spot: 318 | serializedLight.settings.DrawBakedShadowRadius(); 319 | break; 320 | case LightType.Directional: 321 | serializedLight.settings.DrawBakedShadowAngle(); 322 | break; 323 | } 324 | } 325 | 326 | if (lightType != LightType.Rectangle && !serializedLight.settings.isCompletelyBaked) 327 | { 328 | EditorGUILayout.LabelField(Styles.ShadowRealtimeSettings, EditorStyles.boldLabel); 329 | using (new EditorGUI.IndentLevelScope()) 330 | { 331 | // Resolution 332 | if (lightType == LightType.Point || lightType == LightType.Spot) 333 | DrawShadowsResolutionGUI(serializedLight); 334 | 335 | EditorGUILayout.Slider(serializedLight.settings.shadowsStrength, 0f, 1f, Styles.ShadowStrength); 336 | 337 | // Bias 338 | DrawAdditionalShadowData(serializedLight, owner); 339 | 340 | // this min bound should match the calculation in SharedLightData::GetNearPlaneMinBound() 341 | float nearPlaneMinBound = Mathf.Min(0.01f * serializedLight.settings.range.floatValue, 0.1f); 342 | EditorGUILayout.Slider(serializedLight.settings.shadowsNearPlane, nearPlaneMinBound, 10.0f, Styles.ShadowNearPlane); 343 | var isHololens = false; 344 | var isQuest = false; 345 | #if XR_MANAGEMENT_4_0_1_OR_NEWER 346 | var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); 347 | var buildTargetSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(buildTargetGroup); 348 | if (buildTargetSettings != null && buildTargetSettings.AssignedSettings != null && buildTargetSettings.AssignedSettings.activeLoaders.Count > 0) 349 | { 350 | isHololens = buildTargetGroup == BuildTargetGroup.WSA; 351 | isQuest = buildTargetGroup == BuildTargetGroup.Android; 352 | } 353 | 354 | #endif 355 | // Soft Shadow Quality 356 | if (serializedLight.settings.light.shadows == LightShadows.Soft) 357 | EditorGUILayout.PropertyField(serializedLight.softShadowQualityProp, Styles.SoftShadowQuality); 358 | 359 | if (isHololens || isQuest) 360 | { 361 | EditorGUILayout.HelpBox( 362 | "Per-light soft shadow quality level is not supported on untethered XR platforms. Use the Soft Shadow Quality setting in the URP Asset instead", 363 | MessageType.Warning 364 | ); 365 | } 366 | 367 | } 368 | 369 | if (UniversalRenderPipeline.asset.useRenderingLayers) 370 | { 371 | EditorGUI.BeginChangeCheck(); 372 | EditorGUILayout.PropertyField(serializedLight.customShadowLayers, Styles.customShadowLayers); 373 | // Undo the changes in the light component because the SyncLightAndShadowLayers will change the value automatically when link is ticked 374 | if (EditorGUI.EndChangeCheck()) 375 | { 376 | if (serializedLight.customShadowLayers.boolValue) 377 | { 378 | serializedLight.settings.light.renderingLayerMask = serializedLight.shadowRenderingLayers.intValue; 379 | } 380 | else 381 | { 382 | serializedLight.serializedAdditionalDataObject.ApplyModifiedProperties(); // we need to push above modification the modification on object as it is used to sync 383 | SyncLightAndShadowLayers(serializedLight, serializedLight.renderingLayers); 384 | } 385 | } 386 | 387 | if (serializedLight.customShadowLayers.boolValue) 388 | { 389 | using (new EditorGUI.IndentLevelScope()) 390 | { 391 | EditorGUI.BeginChangeCheck(); 392 | EditorUtils.DrawRenderingLayerMask(serializedLight.shadowRenderingLayers, Styles.ShadowLayer); 393 | if (EditorGUI.EndChangeCheck()) 394 | { 395 | serializedLight.settings.light.renderingLayerMask = serializedLight.shadowRenderingLayers.intValue; 396 | serializedLight.Apply(); 397 | } 398 | } 399 | } 400 | } 401 | } 402 | } 403 | 404 | if (!UnityEditor.Lightmapping.bakedGI && !serializedLight.settings.lightmapping.hasMultipleDifferentValues && serializedLight.settings.isBakedOrMixed) 405 | EditorGUILayout.HelpBox(Styles.BakingWarning.text, MessageType.Warning); 406 | } 407 | 408 | static void DrawAdditionalShadowData(SerializedHDLight serializedHdLight, Editor editor) 409 | { 410 | // 0: Custom bias - 1: Bias values defined in Pipeline settings 411 | int selectedUseAdditionalData = serializedHdLight.additionalLightData.usePipelineSettings ? 1 : 0; 412 | Rect r = EditorGUILayout.GetControlRect(true); 413 | EditorGUI.BeginProperty(r, Styles.shadowBias, serializedHdLight.useAdditionalDataProp); 414 | { 415 | using (var checkScope = new EditorGUI.ChangeCheckScope()) 416 | { 417 | selectedUseAdditionalData = EditorGUI.IntPopup(r, Styles.shadowBias, selectedUseAdditionalData, Styles.displayedDefaultOptions, Styles.optionDefaultValues); 418 | if (checkScope.changed) 419 | { 420 | Undo.RecordObjects(serializedHdLight.lightsAdditionalData, "Modified light additional data"); 421 | foreach (var additionData in serializedHdLight.lightsAdditionalData) 422 | additionData.usePipelineSettings = selectedUseAdditionalData != 0; 423 | 424 | serializedHdLight.Apply(); 425 | (editor as UniversalRenderPipelineLightEditor)?.ReconstructReferenceToAdditionalDataSO(); 426 | } 427 | } 428 | } 429 | EditorGUI.EndProperty(); 430 | 431 | if (!serializedHdLight.useAdditionalDataProp.hasMultipleDifferentValues) 432 | { 433 | if (selectedUseAdditionalData != 1) // Custom Bias 434 | { 435 | using (new EditorGUI.IndentLevelScope()) 436 | { 437 | using (var checkScope = new EditorGUI.ChangeCheckScope()) 438 | { 439 | EditorGUILayout.Slider(serializedHdLight.settings.shadowsBias, 0f, 10f, Styles.ShadowDepthBias); 440 | EditorGUILayout.Slider(serializedHdLight.settings.shadowsNormalBias, 0f, 10f, Styles.ShadowNormalBias); 441 | if (checkScope.changed) 442 | serializedHdLight.Apply(); 443 | } 444 | } 445 | } 446 | } 447 | } 448 | 449 | static void DrawShadowsResolutionGUI(SerializedHDLight serializedHdLight) 450 | { 451 | int shadowResolutionTier = serializedHdLight.additionalLightData.additionalLightsShadowResolutionTier; 452 | 453 | using (new EditorGUILayout.HorizontalScope()) 454 | { 455 | using (var checkScope = new EditorGUI.ChangeCheckScope()) 456 | { 457 | Rect r = EditorGUILayout.GetControlRect(true); 458 | r.width += 30; 459 | 460 | shadowResolutionTier = EditorGUI.IntPopup(r, Styles.ShadowResolution, shadowResolutionTier, Styles.ShadowResolutionDefaultOptions, Styles.ShadowResolutionDefaultValues); 461 | if (shadowResolutionTier == UniversalAdditionalLightData.AdditionalLightsShadowResolutionTierCustom) 462 | { 463 | // show the custom value field GUI. 464 | var newResolution = EditorGUILayout.IntField(serializedHdLight.settings.shadowsResolution.intValue, GUILayout.ExpandWidth(false)); 465 | serializedHdLight.settings.shadowsResolution.intValue = Mathf.Max(UniversalAdditionalLightData.AdditionalLightsShadowMinimumResolution, Mathf.NextPowerOfTwo(newResolution)); 466 | } 467 | else 468 | { 469 | if (GraphicsSettings.renderPipelineAsset is UniversalRenderPipelineAsset urpAsset) 470 | EditorGUILayout.LabelField($"{urpAsset.GetAdditionalLightsShadowResolution(shadowResolutionTier)} ({urpAsset.name})", GUILayout.ExpandWidth(false)); 471 | } 472 | if (checkScope.changed) 473 | { 474 | serializedHdLight.additionalLightsShadowResolutionTierProp.intValue = shadowResolutionTier; 475 | serializedHdLight.Apply(); 476 | } 477 | } 478 | } 479 | 480 | EditorGUILayout.HelpBox(Styles.ShadowInfo.text, MessageType.Info); 481 | } 482 | 483 | static void DrawLightCookieContent(SerializedHDLight serializedHdLight, Editor owner) 484 | { 485 | var settings = serializedHdLight.settings; 486 | if (settings.lightType.hasMultipleDifferentValues) 487 | { 488 | EditorGUILayout.HelpBox("Cannot multi edit light cookies from different light types.", MessageType.Info); 489 | return; 490 | } 491 | 492 | settings.DrawCookie(); 493 | 494 | // Draw 2D cookie size for directional lights 495 | bool isDirectionalLight = settings.light.type == LightType.Directional; 496 | if (isDirectionalLight) 497 | { 498 | if (settings.cookie != null) 499 | { 500 | EditorGUI.BeginChangeCheck(); 501 | EditorGUILayout.PropertyField(serializedHdLight.lightCookieSizeProp, Styles.LightCookieSize); 502 | EditorGUILayout.PropertyField(serializedHdLight.lightCookieOffsetProp, Styles.LightCookieOffset); 503 | if (EditorGUI.EndChangeCheck()) 504 | Experimental.Lightmapping.SetLightDirty((UnityEngine.Light)owner.target); 505 | } 506 | } 507 | } 508 | } 509 | } 510 | -------------------------------------------------------------------------------- /Runtime/Lighting/LightUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine.Rendering; 3 | 4 | namespace UnityEngine.Rendering.Universal 5 | { 6 | /// 7 | /// Light Utils contains function to convert light intensities between units 8 | /// 9 | class LightUtils 10 | { 11 | static float s_LuminanceToEvFactor => Mathf.Log(100f / ColorUtils.s_LightMeterCalibrationConstant, 2); 12 | static float s_EvToLuminanceFactor => -Mathf.Log(100f / ColorUtils.s_LightMeterCalibrationConstant, 2); 13 | 14 | // Physical light unit helper 15 | // All light unit are in lumen (Luminous power) 16 | // Punctual light (point, spot) are convert to candela (cd = lumens / steradian) 17 | 18 | // For our isotropic area lights which expect radiance(W / (sr* m^2)) in the shader: 19 | // power = Integral{area, Integral{hemisphere, radiance * }}, 20 | // power = area * Pi * radiance, 21 | // radiance = power / (area * Pi). 22 | // We use photometric unit, so radiance is luminance and power is luminous power 23 | 24 | // Ref: Moving Frostbite to PBR 25 | // Also good ref: https://www.radiance-online.org/community/workshops/2004-fribourg/presentations/Wandachowicz_paper.pdf 26 | 27 | /// 28 | /// Convert an intensity in Lumen to Candela for a point light 29 | /// 30 | /// 31 | /// 32 | public static float ConvertPointLightLumenToCandela(float intensity) 33 | => intensity / (4.0f * Mathf.PI); 34 | 35 | /// 36 | /// Convert an intensity in Candela to Lumen for a point light 37 | /// 38 | /// 39 | /// 40 | public static float ConvertPointLightCandelaToLumen(float intensity) 41 | => intensity * (4.0f * Mathf.PI); 42 | 43 | // angle is the full angle, not the half angle in radian 44 | // convert intensity (lumen) to candela 45 | /// 46 | /// Convert an intensity in Lumen to Candela for a cone spot light. 47 | /// 48 | /// 49 | /// Full angle in radian 50 | /// Exact computation or an approximation 51 | /// 52 | public static float ConvertSpotLightLumenToCandela(float intensity, float angle, bool exact) 53 | => exact ? intensity / (2.0f * (1.0f - Mathf.Cos(angle / 2.0f)) * Mathf.PI) : intensity / Mathf.PI; 54 | 55 | /// 56 | /// Convert an intensity in Candela to Lumen for a cone pot light. 57 | /// 58 | /// 59 | /// Full angle in radian 60 | /// Exact computation or an approximation 61 | /// 62 | public static float ConvertSpotLightCandelaToLumen(float intensity, float angle, bool exact) 63 | => exact ? intensity * (2.0f * (1.0f - Mathf.Cos(angle / 2.0f)) * Mathf.PI) : intensity * Mathf.PI; 64 | 65 | // angleA and angleB are the full opening angle, not half angle 66 | // convert intensity (lumen) to candela 67 | /// 68 | /// Convert an intensity in Lumen to Candela for a pyramid spot light. 69 | /// 70 | /// 71 | /// Full opening angle in radian 72 | /// Full opening angle in radian 73 | /// 74 | public static float ConvertFrustrumLightLumenToCandela(float intensity, float angleA, float angleB) 75 | => intensity / (4.0f * Mathf.Asin(Mathf.Sin(angleA / 2.0f) * Mathf.Sin(angleB / 2.0f))); 76 | 77 | /// 78 | /// Convert an intensity in Candela to Lumen for a pyramid spot light. 79 | /// 80 | /// 81 | /// Full opening angle in radian 82 | /// Full opening angle in radian 83 | /// 84 | public static float ConvertFrustrumLightCandelaToLumen(float intensity, float angleA, float angleB) 85 | => intensity * (4.0f * Mathf.Asin(Mathf.Sin(angleA / 2.0f) * Mathf.Sin(angleB / 2.0f))); 86 | 87 | /// 88 | /// Convert an intensity in Lumen to Luminance(nits) for a sphere light. 89 | /// 90 | /// 91 | /// 92 | /// 93 | public static float ConvertSphereLightLumenToLuminance(float intensity, float sphereRadius) 94 | => intensity / ((4.0f * Mathf.PI * sphereRadius * sphereRadius) * Mathf.PI); 95 | 96 | /// 97 | /// Convert an intensity in Luminance(nits) to Lumen for a sphere light. 98 | /// 99 | /// 100 | /// 101 | /// 102 | public static float ConvertSphereLightLuminanceToLumen(float intensity, float sphereRadius) 103 | => intensity * ((4.0f * Mathf.PI * sphereRadius * sphereRadius) * Mathf.PI); 104 | 105 | /// 106 | /// Convert an intensity in Lumen to Luminance(nits) for a disc light. 107 | /// 108 | /// 109 | /// 110 | /// 111 | public static float ConvertDiscLightLumenToLuminance(float intensity, float discRadius) 112 | => intensity / ((discRadius * discRadius * Mathf.PI) * Mathf.PI); 113 | 114 | /// 115 | /// Convert an intensity in Luminance(nits) to Lumen for a disc light. 116 | /// 117 | /// 118 | /// 119 | /// 120 | public static float ConvertDiscLightLuminanceToLumen(float intensity, float discRadius) 121 | => intensity * ((discRadius * discRadius * Mathf.PI) * Mathf.PI); 122 | 123 | /// 124 | /// Convert an intensity in Lumen to Luminance(nits) for a rectangular light. 125 | /// 126 | /// 127 | /// 128 | /// 129 | /// 130 | public static float ConvertRectLightLumenToLuminance(float intensity, float width, float height) 131 | => intensity / ((width * height) * Mathf.PI); 132 | 133 | /// 134 | /// Convert an intensity in Luminance(nits) to Lumen for a rectangular light. 135 | /// 136 | /// 137 | /// 138 | /// 139 | /// 140 | public static float ConvertRectLightLuminanceToLumen(float intensity, float width, float height) 141 | => intensity * ((width * height) * Mathf.PI); 142 | 143 | // Helper for Lux, Candela, Luminance, Ev conversion 144 | /// 145 | /// Convert intensity in Lux at a certain distance in Candela. 146 | /// 147 | /// 148 | /// 149 | /// 150 | public static float ConvertLuxToCandela(float lux, float distance) 151 | => lux * distance * distance; 152 | 153 | /// 154 | /// Convert intensity in Candela at a certain distance in Lux. 155 | /// 156 | /// 157 | /// 158 | /// 159 | public static float ConvertCandelaToLux(float candela, float distance) 160 | => candela / (distance * distance); 161 | 162 | /// 163 | /// Convert EV100 to Luminance(nits) 164 | /// 165 | /// 166 | /// 167 | public static float ConvertEvToLuminance(float ev) 168 | { 169 | return Mathf.Pow(2, ev + s_EvToLuminanceFactor); 170 | } 171 | 172 | /// 173 | /// Convert EV100 to Candela 174 | /// 175 | /// 176 | /// 177 | public static float ConvertEvToCandela(float ev) 178 | // From punctual point of view candela and luminance is the same 179 | => ConvertEvToLuminance(ev); 180 | 181 | /// 182 | /// Convert EV100 to Lux at a certain distance 183 | /// 184 | /// 185 | /// 186 | /// 187 | public static float ConvertEvToLux(float ev, float distance) 188 | // From punctual point of view candela and luminance is the same 189 | => ConvertCandelaToLux(ConvertEvToLuminance(ev), distance); 190 | 191 | /// 192 | /// Convert Luminance(nits) to EV100 193 | /// 194 | /// 195 | /// 196 | public static float ConvertLuminanceToEv(float luminance) 197 | { 198 | return Mathf.Log(luminance, 2) + s_LuminanceToEvFactor; 199 | } 200 | 201 | /// 202 | /// Convert Candela to EV100 203 | /// 204 | /// 205 | /// 206 | public static float ConvertCandelaToEv(float candela) 207 | // From punctual point of view candela and luminance is the same 208 | => ConvertLuminanceToEv(candela); 209 | 210 | /// 211 | /// Convert Lux at a certain distance to EV100 212 | /// 213 | /// 214 | /// 215 | /// 216 | public static float ConvertLuxToEv(float lux, float distance) 217 | // From punctual point of view candela and luminance is the same 218 | => ConvertLuminanceToEv(ConvertLuxToCandela(lux, distance)); 219 | 220 | // Helper for punctual and area light unit conversion 221 | /// 222 | /// Convert a punctual light intensity in Lumen to Candela 223 | /// 224 | /// 225 | /// 226 | /// 227 | /// 228 | /// 229 | public static float ConvertPunctualLightLumenToCandela(LightType lightType, float lumen, float initialIntensity, bool enableSpotReflector) 230 | { 231 | if (lightType == LightType.Spot && enableSpotReflector) 232 | { 233 | // We have already calculate the correct value, just assign it 234 | return initialIntensity; 235 | } 236 | return ConvertPointLightLumenToCandela(lumen); 237 | } 238 | 239 | /// 240 | /// Convert a punctual light intensity in Lumen to Lux 241 | /// 242 | /// 243 | /// 244 | /// 245 | /// 246 | /// 247 | /// 248 | public static float ConvertPunctualLightLumenToLux(LightType lightType, float lumen, float initialIntensity, bool enableSpotReflector, float distance) 249 | { 250 | float candela = ConvertPunctualLightLumenToCandela(lightType, lumen, initialIntensity, enableSpotReflector); 251 | return ConvertCandelaToLux(candela, distance); 252 | } 253 | 254 | /// 255 | /// Convert a punctual light intensity in Candela to Lumen 256 | /// 257 | /// 258 | /// 259 | /// 260 | /// 261 | /// 262 | /// 263 | /// 264 | public static float ConvertPunctualLightCandelaToLumen(LightType lightType, float candela, bool enableSpotReflector, float spotAngle, float aspectRatio) 265 | { 266 | if (lightType == LightType.Spot && enableSpotReflector) 267 | { 268 | // We just need to multiply candela by solid angle in this case 269 | // if (spotLightShape == SpotLightShape.Cone) 270 | return ConvertSpotLightCandelaToLumen(candela, spotAngle * Mathf.Deg2Rad, true); 271 | // else if (spotLightShape == SpotLightShape.Pyramid) 272 | // { 273 | // float angleA, angleB; 274 | // CalculateAnglesForPyramid(aspectRatio, spotAngle * Mathf.Deg2Rad, out angleA, out angleB); 275 | // 276 | // return ConvertFrustrumLightCandelaToLumen(candela, angleA, angleB); 277 | // } 278 | // else // Box 279 | // return ConvertPointLightCandelaToLumen(candela); 280 | } 281 | return ConvertPointLightCandelaToLumen(candela); 282 | } 283 | 284 | /// 285 | /// Convert a punctual light intensity in Lux to Lumen 286 | /// 287 | /// 288 | /// 289 | /// 290 | /// 291 | /// 292 | /// 293 | /// 294 | /// 295 | public static float ConvertPunctualLightLuxToLumen(LightType lightType, float lux, bool enableSpotReflector, float spotAngle, float aspectRatio, float distance) 296 | { 297 | float candela = ConvertLuxToCandela(lux, distance); 298 | return ConvertPunctualLightCandelaToLumen(lightType, candela, enableSpotReflector, spotAngle, aspectRatio); 299 | } 300 | 301 | // This is not correct, we use candela instead of luminance but this is request from artists to support EV100 on punctual light 302 | /// 303 | /// Convert a punctual light intensity in EV100 to Lumen. 304 | /// This is not physically correct but it's handy to have EV100 for punctual lights. 305 | /// 306 | /// 307 | /// 308 | /// 309 | /// 310 | /// 311 | /// 312 | /// 313 | public static float ConvertPunctualLightEvToLumen(LightType lightType, float ev, bool enableSpotReflector, float spotAngle, float aspectRatio) 314 | { 315 | float candela = ConvertEvToCandela(ev); 316 | return ConvertPunctualLightCandelaToLumen(lightType, candela, enableSpotReflector, spotAngle, aspectRatio); 317 | } 318 | 319 | // This is not correct, we use candela instead of luminance but this is request from artists to support EV100 on punctual light 320 | /// 321 | /// Convert a punctual light intensity in Lumen to EV100. 322 | /// This is not physically correct but it's handy to have EV100 for punctual lights. 323 | /// 324 | /// 325 | /// 326 | /// 327 | /// 328 | /// 329 | public static float ConvertPunctualLightLumenToEv(LightType lightType, float lumen, float initialIntensity, bool enableSpotReflector) 330 | { 331 | float candela = ConvertPunctualLightLumenToCandela(lightType, lumen, initialIntensity, enableSpotReflector); 332 | return ConvertCandelaToEv(candela); 333 | } 334 | 335 | /// 336 | /// Convert area light intensity in Lumen to Luminance(nits) 337 | /// 338 | /// 339 | /// 340 | /// 341 | /// 342 | /// 343 | public static float ConvertAreaLightLumenToLuminance(LightType areaLightShape, float lumen, float width, float height = 0) 344 | { 345 | switch (areaLightShape) 346 | { 347 | // case LightType.Tube: 348 | // return LightUtils.CalculateLineLightLumenToLuminance(lumen, width); 349 | case LightType.Rectangle: 350 | return LightUtils.ConvertRectLightLumenToLuminance(lumen, width, height); 351 | case LightType.Disc: 352 | return LightUtils.ConvertDiscLightLumenToLuminance(lumen, width); 353 | } 354 | return lumen; 355 | } 356 | 357 | /// 358 | /// Convert area light intensity in Luminance(nits) to Lumen 359 | /// 360 | /// 361 | /// 362 | /// 363 | /// 364 | /// 365 | public static float ConvertAreaLightLuminanceToLumen(LightType areaLightShape, float luminance, float width, float height = 0) 366 | { 367 | switch (areaLightShape) 368 | { 369 | // case AreaLightShape.Tube: 370 | // return LightUtils.CalculateLineLightLuminanceToLumen(luminance, width); 371 | case LightType.Rectangle: 372 | return LightUtils.ConvertRectLightLuminanceToLumen(luminance, width, height); 373 | case LightType.Disc: 374 | return LightUtils.ConvertDiscLightLuminanceToLumen(luminance, width); 375 | } 376 | return luminance; 377 | } 378 | 379 | /// 380 | /// Convert area light intensity in Lumen to EV100 381 | /// 382 | /// 383 | /// 384 | /// 385 | /// 386 | /// 387 | public static float ConvertAreaLightLumenToEv(LightType AreaLightShape, float lumen, float width, float height) 388 | { 389 | float luminance = ConvertAreaLightLumenToLuminance(AreaLightShape, lumen, width, height); 390 | return ConvertLuminanceToEv(luminance); 391 | } 392 | 393 | /// 394 | /// Convert area light intensity in EV100 to Lumen 395 | /// 396 | /// 397 | /// 398 | /// 399 | /// 400 | /// 401 | public static float ConvertAreaLightEvToLumen(LightType AreaLightShape, float ev, float width, float height) 402 | { 403 | float luminance = ConvertEvToLuminance(ev); 404 | return ConvertAreaLightLuminanceToLumen(AreaLightShape, luminance, width, height); 405 | } 406 | 407 | /// 408 | /// Convert line light intensity in Lumen to Luminance(nits) 409 | /// 410 | /// 411 | /// 412 | /// 413 | public static float CalculateLineLightLumenToLuminance(float intensity, float lineWidth) 414 | { 415 | //Line lights expect radiance (W / (sr * m^2)) in the shader. 416 | //In the UI, we specify luminous flux (power) in lumens. 417 | //First, it needs to be converted to radiometric units (radian flux, W). 418 | 419 | //Then we must recall how to compute power from radiance: 420 | 421 | //radiance = differential_power / (differential_projected_area * differential_solid_angle), 422 | //radiance = differential_power / (differential_area * differential_solid_angle * ), 423 | //power = Integral{area, Integral{hemisphere, radiance * }}. 424 | 425 | //Unlike line lights, our line lights have no surface area, so the integral becomes: 426 | 427 | //power = Integral{length, Integral{sphere, radiance}}. 428 | 429 | //For an isotropic line light, radiance is constant, therefore: 430 | 431 | //power = length * (4 * Pi) * radiance, 432 | //radiance = power / (length * (4 * Pi)). 433 | return intensity / (4.0f * Mathf.PI * lineWidth); 434 | } 435 | 436 | /// 437 | /// Convert a line light intensity in Luminance(nits) to Lumen 438 | /// 439 | /// 440 | /// 441 | /// 442 | public static float CalculateLineLightLuminanceToLumen(float intensity, float lineWidth) 443 | => intensity * (4.0f * Mathf.PI * lineWidth); 444 | 445 | // spotAngle in radian 446 | /// 447 | /// Calculate angles for the pyramid spot light to calculate it's intensity. 448 | /// 449 | /// 450 | /// angle in radian 451 | /// 452 | /// 453 | public static void CalculateAnglesForPyramid(float aspectRatio, float spotAngle, out float angleA, out float angleB) 454 | { 455 | // Since the smallest angles is = to the fov, and we don't care of the angle order, simply make sure the aspect ratio is > 1 456 | if (aspectRatio < 1.0f) 457 | aspectRatio = 1.0f / aspectRatio; 458 | 459 | angleA = spotAngle; 460 | 461 | var halfAngle = angleA * 0.5f; // half of the smallest angle 462 | var length = Mathf.Tan(halfAngle); // half length of the smallest side of the rectangle 463 | length *= aspectRatio; // half length of the bigest side of the rectangle 464 | halfAngle = Mathf.Atan(length); // half of the bigest angle 465 | 466 | angleB = halfAngle * 2.0f; 467 | } 468 | 469 | internal static void ConvertLightIntensity(LightUnit oldLightUnit, LightUnit newLightUnit, AdditionalLightData hdLight, Light light) 470 | { 471 | float intensity = hdLight.intensity; 472 | float luxAtDistance = hdLight.luxAtDistance; 473 | LightType lightType = hdLight.legacyLight.type;//hdLight.ComputeLightType(light); 474 | 475 | // For punctual lights 476 | if (lightType != LightType.Area) 477 | { 478 | // Lumen -> 479 | if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Candela) 480 | intensity = LightUtils.ConvertPunctualLightLumenToCandela(lightType, intensity, light.intensity, hdLight.enableSpotReflector); 481 | else if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Lux) 482 | intensity = LightUtils.ConvertPunctualLightLumenToLux(lightType, intensity, light.intensity, hdLight.enableSpotReflector, hdLight.luxAtDistance); 483 | else if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Ev100) 484 | intensity = LightUtils.ConvertPunctualLightLumenToEv(lightType, intensity, light.intensity, hdLight.enableSpotReflector); 485 | // Candela -> 486 | else if (oldLightUnit == LightUnit.Candela && newLightUnit == LightUnit.Lumen) 487 | intensity = LightUtils.ConvertPunctualLightCandelaToLumen(lightType, intensity, hdLight.enableSpotReflector, light.spotAngle, hdLight.aspectRatio); 488 | else if (oldLightUnit == LightUnit.Candela && newLightUnit == LightUnit.Lux) 489 | intensity = LightUtils.ConvertCandelaToLux(intensity, hdLight.luxAtDistance); 490 | else if (oldLightUnit == LightUnit.Candela && newLightUnit == LightUnit.Ev100) 491 | intensity = LightUtils.ConvertCandelaToEv(intensity); 492 | // Lux -> 493 | else if (oldLightUnit == LightUnit.Lux && newLightUnit == LightUnit.Lumen) 494 | intensity = LightUtils.ConvertPunctualLightLuxToLumen(lightType, intensity, hdLight.enableSpotReflector, light.spotAngle, hdLight.aspectRatio, hdLight.luxAtDistance); 495 | else if (oldLightUnit == LightUnit.Lux && newLightUnit == LightUnit.Candela) 496 | intensity = LightUtils.ConvertLuxToCandela(intensity, hdLight.luxAtDistance); 497 | else if (oldLightUnit == LightUnit.Lux && newLightUnit == LightUnit.Ev100) 498 | intensity = LightUtils.ConvertLuxToEv(intensity, hdLight.luxAtDistance); 499 | // EV100 -> 500 | else if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Lumen) 501 | intensity = LightUtils.ConvertPunctualLightEvToLumen(lightType, intensity, hdLight.enableSpotReflector, light.spotAngle, hdLight.aspectRatio); 502 | else if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Candela) 503 | intensity = LightUtils.ConvertEvToCandela(intensity); 504 | else if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Lux) 505 | intensity = LightUtils.ConvertEvToLux(intensity, hdLight.luxAtDistance); 506 | } 507 | #if UNITY_EDITOR 508 | // Area Light (Editor Only for URP) 509 | else // For area lights 510 | { 511 | if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Nits) 512 | intensity = LightUtils.ConvertAreaLightLumenToLuminance(lightType, intensity, hdLight.legacyLight.areaSize.x, hdLight.legacyLight.areaSize.y); 513 | if (oldLightUnit == LightUnit.Nits && newLightUnit == LightUnit.Lumen) 514 | intensity = LightUtils.ConvertAreaLightLuminanceToLumen(lightType, intensity, hdLight.legacyLight.areaSize.x, hdLight.legacyLight.areaSize.y); 515 | if (oldLightUnit == LightUnit.Nits && newLightUnit == LightUnit.Ev100) 516 | intensity = LightUtils.ConvertLuminanceToEv(intensity); 517 | if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Nits) 518 | intensity = LightUtils.ConvertEvToLuminance(intensity); 519 | if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Lumen) 520 | intensity = LightUtils.ConvertAreaLightEvToLumen(lightType, intensity, hdLight.legacyLight.areaSize.x, hdLight.legacyLight.areaSize.y); 521 | if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Ev100) 522 | intensity = LightUtils.ConvertAreaLightLumenToEv(lightType, intensity, hdLight.legacyLight.areaSize.x, hdLight.legacyLight.areaSize.y); 523 | } 524 | #endif 525 | hdLight.intensity = intensity; 526 | } 527 | 528 | internal static Color EvaluateLightColor(Light light, AdditionalLightData hdLight) 529 | { 530 | Color finalColor = light.color.linear * light.intensity; 531 | if (hdLight.useColorTemperature) 532 | finalColor *= Mathf.CorrelatedColorTemperatureToRGB(light.colorTemperature); 533 | return finalColor; 534 | } 535 | } 536 | } 537 | -------------------------------------------------------------------------------- /Editor/Lighting/URPLightUI.Skin.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Rendering.Universal; 3 | 4 | namespace UnityEditor.Rendering.Universal 5 | { 6 | partial class URPLightUI 7 | { 8 | sealed class HDStyles 9 | { 10 | // Headers 11 | public readonly GUIContent celestialBodyHeader = EditorGUIUtility.TrTextContent("Celestial Body"); 12 | public readonly GUIContent volumetricHeader = EditorGUIUtility.TrTextContent("Volumetrics"); 13 | public readonly GUIContent shadowMapSubHeader = EditorGUIUtility.TrTextContent("Shadow Map"); 14 | public readonly GUIContent contactShadowsSubHeader = EditorGUIUtility.TrTextContent("Contact Shadows"); 15 | public readonly GUIContent bakedShadowsSubHeader = EditorGUIUtility.TrTextContent("Baked Shadows"); 16 | public readonly GUIContent veryHighShadowQualitySubHeader = EditorGUIUtility.TrTextContent("Very High Quality Settings"); 17 | public readonly GUIContent highShadowQualitySubHeader = EditorGUIUtility.TrTextContent("High Quality Settings"); 18 | public readonly GUIContent mediumShadowQualitySubHeader = EditorGUIUtility.TrTextContent("Medium Quality Settings"); 19 | public readonly GUIContent lowShadowQualitySubHeader = EditorGUIUtility.TrTextContent("Low Quality Settings"); 20 | 21 | // Base (copy from LightEditor.cs) 22 | public readonly GUIContent outterAngle = EditorGUIUtility.TrTextContent("Outer Angle", "Controls the angle, in degrees, at the base of a Spot Light's cone."); 23 | public readonly GUIContent cookieSize = EditorGUIUtility.TrTextContent("Size", "Sets the size of the Cookie mask currently assigned to the Light."); 24 | public readonly GUIContent shadowBias = EditorGUIUtility.TrTextContent("Bias", "Controls the distance at which HDRP pushes shadows away from the Light. Useful for avoiding false self-shadowing artifacts."); 25 | public readonly GUIContent shadowNormalBias = EditorGUIUtility.TrTextContent("Normal Bias", "Controls distance at which HDRP shrinks the shadow casting surfaces along the surface normal. Useful for avoiding false self-shadowing artifacts."); 26 | public readonly GUIContent shadowNearPlane = EditorGUIUtility.TrTextContent("Near Plane", "Controls the value of the shadow camera's near clipping plane for rendering shadows. Clamped to [0.01, 10] for Cone, Pyramid and Point Lights, and [0, 10] for Box and Area Lights."); 27 | public readonly GUIContent bakedShadowRadius = EditorGUIUtility.TrTextContent("Radius", "Sets the amount of artificial softening the baking process applies to the edges of shadows cast by this Point or Spot Light."); 28 | public readonly GUIContent bakedShadowAngle = EditorGUIUtility.TrTextContent("Angle", "Controls the amount of artificial softening the baking process applies to the edges of shadows cast by Directional Lights."); 29 | public readonly GUIContent lightBounceIntensity = EditorGUIUtility.TrTextContent("Indirect Multiplier", "Controls the intensity of the indirect light this Light contributes to the Scene. A value of 0 with a Realtime Light causes HDRP to remove it from realtime global illumination. A value of 0 for Baked and Mixed Lights cause them to no longer emit indirect lighting. This has no effect if you disable both Realtime and Baked global illumination."); 30 | public readonly GUIContent areaLightCookie = EditorGUIUtility.TrTextContent("Cookie", "Cookie mask currently assigned to the area light."); 31 | public readonly GUIContent iesTexture = EditorGUIUtility.TrTextContent("IES Profile", "IES Profile (Support: Point, Spot, Rectangular-Area Lights)."); 32 | public readonly GUIContent cookieTextureTypeError = EditorGUIUtility.TrTextContent("HDRP does not support the Cookie Texture type, only Default is supported.", EditorGUIUtility.IconContent("console.warnicon").image); 33 | public readonly string cookieNonPOT = "HDRP does not support non power of two cookie textures."; 34 | public readonly string cookieTooSmall = "Min texture size for cookies is 2x2 pixels."; 35 | public readonly string cookieBaking = "Light Baking for cookies disabled on the Project Settings."; 36 | public readonly GUIContent includeLightForRayTracing = EditorGUIUtility.TrTextContent("Include For RayTracing", "When enabled, the light affects the scene for cameras with the Ray-Tracing frame setting enabled."); 37 | 38 | // Additional light data 39 | public readonly GUIContent directionalIntensity = EditorGUIUtility.TrTextContent("Intensity (Lux)", "Illuminance of the Directional Light, at ground level, in lux."); 40 | public readonly GUIContent punctualIntensity = EditorGUIUtility.TrTextContent("Intensity (Lumen)", "Luminous power of the Light in lumen."); 41 | public readonly GUIContent areaIntensity = EditorGUIUtility.TrTextContent("Intensity (Lumen)", "Luminous power of the Light in Lumen."); 42 | public readonly GUIContent lightIntensity = EditorGUIUtility.TrTextContent("Intensity", "Sets the strength of the Light. Use the drop-down to select the light units to use."); 43 | 44 | public readonly GUIContent lightRadius = EditorGUIUtility.TrTextContent("Radius", "Sets the radius of the light source. This affects the falloff of diffuse lighting, the spread of the specular highlight, and the softness of Ray Traced shadows."); 45 | public readonly GUIContent affectDiffuse = EditorGUIUtility.TrTextContent("Affect Diffuse", "When disabled, HDRP does not calculate diffuse lighting for this Light. Does not increase performance as HDRP still calculates the diffuse lighting."); 46 | public readonly GUIContent affectSpecular = EditorGUIUtility.TrTextContent("Affect Specular", "When disabled, HDRP does not calculate specular lighting for this Light. Does not increase performance as HDRP still calculates the specular lighting."); 47 | public readonly GUIContent nonLightmappedOnly = EditorGUIUtility.TrTextContent("Shadowmask Mode", "Determines Shadowmask functionality when using Mixed lighting. Distance Shadowmask casts real-time shadows within the Shadow Distance, and baked shadows beyond. In Shadowmask mode, static GI contributors always cast baked shadows.\nEnable Shadowmask support in the HDRP asset to make use of this feature. Only available when Lighting Mode is set to Shadowmask in the Lighting window."); 48 | public readonly GUIContent lightDimmer = EditorGUIUtility.TrTextContent("Intensity Multiplier", "Multiplies the intensity of the Light by the given number. This is useful for modifying the intensity of multiple Lights simultaneously without needing know the intensity of each Light."); 49 | public readonly GUIContent fadeDistance = EditorGUIUtility.TrTextContent("Fade Distance", "Sets the distance from the camera at which light smoothly fades out before HDRP culls it completely. This minimizes popping."); 50 | public readonly GUIContent innerOuterSpotAngle = EditorGUIUtility.TrTextContent("Inner / Outer Spot Angle", "Controls the inner and outer angles in degrees, at the base of a Spot light's cone."); 51 | public readonly GUIContent spotIESCutoffPercent = EditorGUIUtility.TrTextContent("IES Cutoff Angle (%)", "Cutoff the IES Light in percent, of the base angle of the Spot Light's cone."); 52 | public readonly GUIContent spotLightShape = EditorGUIUtility.TrTextContent("Shape", "The shape of the Spot Light. Impacts the the cookie transformation and the Light's angular attenuation."); 53 | public readonly GUIContent areaLightShape = EditorGUIUtility.TrTextContent("Shape", "The shape of the Area Light. Note that some are Realtime only and some Baked only."); 54 | public readonly GUIContent[] areaShapeNames = 55 | { 56 | EditorGUIUtility.TrTextContent("Rectangle"), 57 | EditorGUIUtility.TrTextContent("Tube (Realtime only)"), 58 | EditorGUIUtility.TrTextContent("Disc (Baked only)") 59 | }; 60 | public readonly GUIContent shapeWidthTube = EditorGUIUtility.TrTextContent("Length", "Length of the Tube Light."); 61 | public readonly GUIContent shapeWidthRect = EditorGUIUtility.TrTextContent("Size X", "Sets the width of the Rectangle Light."); 62 | public readonly GUIContent shapeHeightRect = EditorGUIUtility.TrTextContent("Size Y", "Sets the height of the Rectangle Light."); 63 | public readonly GUIContent barnDoorAngle = EditorGUIUtility.TrTextContent("Barn Door Angle", "Sets the angle of the Rectangle Light so that is behaves like a barn door."); 64 | public readonly GUIContent barnDoorLength = EditorGUIUtility.TrTextContent("Barn Door Length", "Sets the length for the barn door."); 65 | public readonly GUIContent aspectRatioPyramid = EditorGUIUtility.TrTextContent("Aspect ratio", "Controls the aspect ration of the Pyramid Light's projection. A value of 1 results in a square."); 66 | public readonly GUIContent shapeWidthBox = EditorGUIUtility.TrTextContent("Size X", "Sets the width of the Box Light."); 67 | public readonly GUIContent shapeHeightBox = EditorGUIUtility.TrTextContent("Size Y", "Sets the height of the Box Light."); 68 | public readonly GUIContent applyRangeAttenuation = EditorGUIUtility.TrTextContent("Range Attenuation", "Allows you to enable or disable range attenuation. Range attenuation is useful for indoor environments because you can avoid having to set up a large range for a Light to get correct inverse square attenuation that may leak out of the indoor environment."); 69 | public readonly GUIContent displayAreaLightEmissiveMesh = EditorGUIUtility.TrTextContent("Display Emissive Mesh", "Generate an emissive mesh using the size, Color and Intensity of the Area Light."); 70 | public readonly GUIContent areaLightEmissiveMeshCastShadow = EditorGUIUtility.TrTextContent("Cast Shadows", "Specify wether the generated geometry create shadow or not when a shadow casting Light shines on it"); 71 | public readonly GUIContent areaLightEmissiveMeshMotionVector = EditorGUIUtility.TrTextContent("Motion Vectors", "Specify wether the generated Mesh renders 'Per Object Motion', 'Camera Motion' or 'No Motion' vectors to the Camera Motion Vector Texture."); 72 | public readonly GUIContent areaLightEmissiveMeshSameLayer = EditorGUIUtility.TrTextContent("Same Layer", "If checked, use the same Layer than the Light one."); 73 | public readonly GUIContent areaLightEmissiveMeshCustomLayer = EditorGUIUtility.TrTextContent("Custom Layer", "Specify on which layer the generated Mesh live."); 74 | 75 | public readonly GUIContent interactsWithSky = EditorGUIUtility.TrTextContent("Affect Physically Based Sky", "Check this option to make the light and the Physically Based sky affect one another."); 76 | public readonly GUIContent angularDiameter = EditorGUIUtility.TrTextContent("Angular Diameter", "Angular diameter of the emissive celestial body represented by the light as seen from the camera (in degrees). Used to render the sun/moon disk."); 77 | public readonly GUIContent flareSize = EditorGUIUtility.TrTextContent("Flare Size", "Size of the flare around the celestial body (in degrees)."); 78 | public readonly GUIContent flareTint = EditorGUIUtility.TrTextContent("Flare Tint", "Tints the flare of the celestial body"); 79 | public readonly GUIContent flareFalloff = EditorGUIUtility.TrTextContent("Flare Falloff", "The falloff rate of flare intensity as the angle from the light increases."); 80 | public readonly GUIContent surfaceTexture = EditorGUIUtility.TrTextContent("Surface Texture", "2D (disk) texture of the surface of the celestial body. Acts like a multiplier."); 81 | public readonly GUIContent surfaceTint = EditorGUIUtility.TrTextContent("Surface Tint", "Tints the surface of the celestial body"); 82 | public readonly GUIContent distance = EditorGUIUtility.TrTextContent("Distance", "Distance from the camera (in meters) to the emissive celestial body represented by the light. Primarily used for sorting."); 83 | 84 | public readonly GUIContent shape = EditorGUIUtility.TrTextContent("Type", "Specifies the current type of Light. Possible Light types are Directional, Spot, Point, and Area."); 85 | public readonly GUIContent enableSpotReflector = EditorGUIUtility.TrTextContent("Reflector", "When enabled, HDRP simulates a physically correct Spot Light using a reflector. This means the narrower the Outer Angle, the more intense the Spot Light. When disabled, the intensity of the Light matches the one of a Point Light and thus remains constant regardless of the Outer Angle."); 86 | public readonly GUIContent luxAtDistance = EditorGUIUtility.TrTextContent("At", "Sets the distance, in meters, where a surface receives the amount of light equivalent to the provided number of Lux."); 87 | 88 | // Volumetric Additional light data 89 | public readonly GUIContent volumetricEnable = EditorGUIUtility.TrTextContent("Enable", "When enabled, this Light uses Volumetrics."); 90 | public readonly GUIContent volumetricDimmer = EditorGUIUtility.TrTextContent("Multiplier", "Controls the intensity of the scattered Volumetric lighting."); 91 | public readonly GUIContent volumetricFadeDistance = EditorGUIUtility.TrTextContent("Fade Distance", "Sets the distance from the camera at which light smoothly fades out from contributing to volumetric lighting."); 92 | // Volumetric Additional shadow data 93 | public readonly GUIContent volumetricShadowDimmer = EditorGUIUtility.TrTextContent("Shadow Dimmer", "Dims the volumetric shadows this Light casts."); 94 | 95 | // Additional shadow data 96 | public readonly GUIContent useShadowQualityResolution = EditorGUIUtility.TrTextContent("Use Quality Settings", "Allows to the resolution from the set of predetermined resolutions specified in the quality settings."); 97 | public readonly GUIContent shadowResolution = EditorGUIUtility.TrTextContent("Resolution", "Sets the rendered resolution of the shadow maps. A higher resolution increases the fidelity of shadows at the cost of GPU performance and memory usage."); 98 | public readonly GUIContent shadowFadeDistance = EditorGUIUtility.TrTextContent("Fade Distance", "Sets the distance from the camera at which Shadows fade before HDRP culls them completely. This minimizes popping."); 99 | public readonly GUIContent shadowDimmer = EditorGUIUtility.TrTextContent("Dimmer", "Dims the shadows this Light casts."); 100 | public readonly GUIContent shadowTint = EditorGUIUtility.TrTextContent("Tint", "Specifies the color and transparency that HDRP tints this Light's shadows to. The tint affects dynamic shadows, Contact Shadows, and ShadowMask. It does not affect baked shadows."); 101 | public readonly GUIContent penumbraTint = EditorGUIUtility.TrTextContent("Penumbra Tint", "When enabled, the tint only affects the shadow's penumbra."); 102 | public readonly GUIContent contactShadows = EditorGUIUtility.TrTextContent("Enable", "Enable support for Contact Shadows on this Light. This is better for lights with a lot of visible shadows."); 103 | public readonly GUIContent rayTracedContactShadow = EditorGUIUtility.TrTextContent("Ray Tracing (Preview)", "Uses ray tracing to compute the contact shadow for a light."); 104 | public readonly GUIContent shadowUpdateMode = EditorGUIUtility.TrTextContent("Update Mode", "Specifies when HDRP updates the shadow map."); 105 | public readonly GUIContent shadowAlwaysDrawDynamic = EditorGUIUtility.TrTextContent("Always draw dynamic", "Specifies whether HDRP renders dynamic shadow caster every frame regardless of the update mode."); 106 | public readonly GUIContent shadowUpdateOnLightTransformChange = EditorGUIUtility.TrTextContent("Update on light movement", "Whether a cached shadow map will be automatically updated when the light transform changes."); 107 | public readonly GUIContent useCustomSpotLightShadowCone = EditorGUIUtility.TrTextContent("Custom Spot Angle", "When enabled, this Spot Light uses the custom angle for shadow map rendering."); 108 | public readonly GUIContent customSpotLightShadowCone = EditorGUIUtility.TrTextContent("Shadow Angle", "Controls the custom angle this Spot Light uses for shadow map rendering."); 109 | 110 | // Bias control 111 | public readonly GUIContent slopeBias = EditorGUIUtility.TrTextContent("Slope-Scale Depth Bias", "Controls the bias that HDRP adds to the rendered shadow map, it is proportional to the slope of the polygons relative to the light."); 112 | 113 | public readonly GUIContent normalBias = EditorGUIUtility.TrTextContent("Normal Bias", "Controls the bias this Light applies along the normal of surfaces it illuminates."); 114 | 115 | // Shadow filter settings 116 | public readonly GUIContent blockerSampleCount = EditorGUIUtility.TrTextContent("Blocker Sample Count", "Controls the number of samples that HDRP uses to determine the size of the blocker."); 117 | public readonly GUIContent filterSampleCount = EditorGUIUtility.TrTextContent("Filter Sample Count", "Controls the number of samples that HDRP uses to blur shadows."); 118 | public readonly GUIContent minFilterSize = EditorGUIUtility.TrTextContent("Minimum Blur Intensity", "Controls the minimum blur intensity regardless of the distance between the pixel and the shadow caster. The range [0..1] maps to [0..0.001] in UV space."); 119 | public readonly GUIContent radiusScaleForSoftness = EditorGUIUtility.TrTextContent("Radius Scale for Softness", "Scale the shape radius for the sake of softness calculation. Higher scales will result in higher softness."); 120 | public readonly GUIContent diameterScaleForSoftness = EditorGUIUtility.TrTextContent("Angular Diameter Scale for Softness", "Scale the angular diameter for the sake of softness calculation. Higher scales will result in higher softness."); 121 | public readonly GUIContent areaLightShadowCone = EditorGUIUtility.TrTextContent("Shadow Cone", "Aperture of the cone used for shadowing the area light."); 122 | public readonly GUIContent useScreenSpaceShadows = EditorGUIUtility.TrTextContent("Screen Space Shadows", "Render screen space shadow."); 123 | public readonly GUIContent useRayTracedShadows = EditorGUIUtility.TrTextContent("Ray Traced Shadows (Preview)", "If selected, ray traced shadows are used in place of rasterized ones."); 124 | public readonly GUIContent numRayTracingSamples = EditorGUIUtility.TrTextContent("Sample Count", "This defines the number of samples that will be used to evaluate this shadow."); 125 | public readonly GUIContent denoiseTracedShadow = EditorGUIUtility.TrTextContent("Denoise", "This defines if the ray traced shadow should be filtered."); 126 | public readonly GUIContent denoiserRadius = EditorGUIUtility.TrTextContent("Denoiser Radius", "This defines the denoiser's radius used for filtering ray traced shadows."); 127 | public readonly GUIContent distanceBasedFiltering = EditorGUIUtility.TrTextContent("Distance Based Denoising", "This defines if the denoiser should use the distance to the occluder to improve the filtering."); 128 | public readonly GUIContent semiTransparentShadow = EditorGUIUtility.TrTextContent("Semi Transparent Shadow", "When enabled, the opacity of shadow casters will be taken into account when generating the shadow."); 129 | public readonly GUIContent colorShadow = EditorGUIUtility.TrTextContent("Color Shadow", "When enabled, the opacity and transmittance color of shadow casters will be taken into account when generating the shadow."); 130 | public readonly GUIContent evsmExponent = EditorGUIUtility.TrTextContent("EVSM Exponent", "Exponent used for depth warping. Increasing this could reduce light leak and result in a change in appearance of the shadow."); 131 | public readonly GUIContent evsmLightLeakBias = EditorGUIUtility.TrTextContent("Light Leak Bias", "Increasing this value light leaking, but it eats up a bit of the softness of the shadow."); 132 | public readonly GUIContent evsmVarianceBias = EditorGUIUtility.TrTextContent("Variance Bias", "Variance Bias for EVSM. This is to contrast numerical accuracy issues. "); 133 | public readonly GUIContent evsmAdditionalBlurPasses = EditorGUIUtility.TrTextContent("Blur passes", "Increasing this will increase the softness of the shadow, but it will severely impact performance."); 134 | 135 | // Very high shadow settings 136 | public readonly GUIContent lightAngle = EditorGUIUtility.TrTextContent("Light Angle"); 137 | public readonly GUIContent kernelSize = EditorGUIUtility.TrTextContent("Kernel size"); 138 | public readonly GUIContent maxDepthBias = EditorGUIUtility.TrTextContent("Max Depth Bias"); 139 | 140 | // Layers 141 | public readonly GUIContent unlinkLightAndShadowLayersText = EditorGUIUtility.TrTextContent("Custom Shadow Layers", "When enabled, you can use the Layer property below to specify the layers for shadows seperately to lighting. When disabled, the Light Layer property in the General section specifies the layers for both lighting and for shadows."); 142 | public readonly GUIContent shadowLayerMaskText = EditorGUIUtility.TrTextContent("Layer", "Specifies the light layer to use for shadows."); 143 | 144 | // Settings 145 | public readonly GUIContent enableShadowMap = EditorGUIUtility.TrTextContent("Enable", "When enabled, this Light casts shadows."); 146 | 147 | // Warnings 148 | public readonly string unsupportedLightShapeWarning = L10n.Tr("This light shape is not supported by Realtime Global Illumination."); 149 | } 150 | 151 | static HDStyles s_Styles = new HDStyles(); 152 | 153 | // URP Light Styles 154 | internal static class Styles 155 | { 156 | public static readonly GUIContent Type = EditorGUIUtility.TrTextContent("Type", "Specifies the current type of light. Possible types are Directional, Spot, Point, and Area lights."); 157 | 158 | public static readonly GUIContent AreaLightShapeContent = EditorGUIUtility.TrTextContent("Shape", "Specifies the shape of the Area light. Possible types are Rectangle and Disc."); 159 | public static readonly GUIContent[] LightTypeTitles = { EditorGUIUtility.TrTextContent("Spot"), EditorGUIUtility.TrTextContent("Directional"), EditorGUIUtility.TrTextContent("Point"), EditorGUIUtility.TrTextContent("Area (baked only)") }; 160 | public static readonly int[] LightTypeValues = { (int)LightType.Spot, (int)LightType.Directional, (int)LightType.Point, (int)LightType.Rectangle }; 161 | 162 | public static readonly GUIContent[] AreaLightShapeTitles = { EditorGUIUtility.TrTextContent("Rectangle"), EditorGUIUtility.TrTextContent("Disc") }; 163 | public static readonly int[] AreaLightShapeValues = { (int)LightType.Rectangle, (int)LightType.Disc }; 164 | 165 | public static readonly GUIContent SpotAngle = EditorGUIUtility.TrTextContent("Spot Angle", "Controls the angle in degrees at the base of a Spot light's cone."); 166 | 167 | public static readonly GUIContent BakingWarning = EditorGUIUtility.TrTextContent("Light mode is currently overridden to Realtime mode. Enable Baked Global Illumination to use Mixed or Baked light modes."); 168 | public static readonly GUIContent DisabledLightWarning = EditorGUIUtility.TrTextContent("Lighting has been disabled in at least one Scene view. Any changes applied to lights in the Scene will not be updated in these views until Lighting has been enabled again."); 169 | public static readonly GUIContent SunSourceWarning = EditorGUIUtility.TrTextContent("This light is set as the current Sun Source, which requires a directional light. Go to the Lighting Window's Environment settings to edit the Sun Source."); 170 | public static readonly GUIContent CullingMask = EditorGUIUtility.TrTextContent("Culling Mask", "Specifies which lights are culled per camera. To control exclude certain lights affecting certain objects, use Rendering Layers instead, which is supported across all rendering paths."); 171 | public static readonly GUIContent CullingMaskWarning = EditorGUIUtility.TrTextContent("Culling Mask should be used to control which lights are culled per camera. If you want to exclude certain lights from affecting certain objects, use Rendering Layers on the Light, and Rendering Layer Mask on the Mesh Renderer."); 172 | 173 | public static readonly GUIContent ShadowRealtimeSettings = EditorGUIUtility.TrTextContent("Realtime Shadows", "Settings for realtime direct shadows."); 174 | public static readonly GUIContent ShadowStrength = EditorGUIUtility.TrTextContent("Strength", "Controls how dark the shadows cast by the light will be."); 175 | public static readonly GUIContent ShadowNearPlane = EditorGUIUtility.TrTextContent("Near Plane", "Controls the value for the near clip plane when rendering shadows. Currently clamped to 0.1 units or 1% of the lights range property, whichever is lower."); 176 | public static readonly GUIContent ShadowNormalBias = EditorGUIUtility.TrTextContent("Normal", "Determines the bias this Light applies along the normal of surfaces it illuminates. This is ignored for point lights."); 177 | public static readonly GUIContent ShadowDepthBias = EditorGUIUtility.TrTextContent("Depth", "Determines the bias at which shadows are pushed away from the shadow-casting Game Object along the line from the Light."); 178 | public static readonly GUIContent ShadowInfo = EditorGUIUtility.TrTextContent("Unity might reduce the Light's shadow resolution to ensure that shadow maps fit in the shadow atlas. Consider this when selecting the the size of the shadow atlas, the shadow resolution of Lights, the number of Lights in your scene and whether you use soft shadows."); 179 | 180 | // Resolution (default or custom) 181 | public static readonly GUIContent ShadowResolution = EditorGUIUtility.TrTextContent("Resolution", $"Sets the rendered resolution of the shadow maps. A higher resolution increases the fidelity of shadows at the cost of GPU performance and memory usage. Rounded to the next power of two, and clamped to be at least {UniversalAdditionalLightData.AdditionalLightsShadowMinimumResolution}."); 182 | public static readonly int[] ShadowResolutionDefaultValues = 183 | { 184 | UniversalAdditionalLightData.AdditionalLightsShadowResolutionTierCustom, 185 | UniversalAdditionalLightData.AdditionalLightsShadowResolutionTierLow, 186 | UniversalAdditionalLightData.AdditionalLightsShadowResolutionTierMedium, 187 | UniversalAdditionalLightData.AdditionalLightsShadowResolutionTierHigh 188 | }; 189 | public static readonly GUIContent[] ShadowResolutionDefaultOptions = 190 | { 191 | EditorGUIUtility.TrTextContent("Custom"), 192 | UniversalRenderPipelineAssetUI.Styles.additionalLightsShadowResolutionTierNames[0], 193 | UniversalRenderPipelineAssetUI.Styles.additionalLightsShadowResolutionTierNames[1], 194 | UniversalRenderPipelineAssetUI.Styles.additionalLightsShadowResolutionTierNames[2], 195 | }; 196 | 197 | public static GUIContent SoftShadowQuality = EditorGUIUtility.TrTextContent("Soft Shadows Quality", "Controls the filtering quality of soft shadows. Higher quality has lower performance."); 198 | 199 | // Bias (default or custom) 200 | public static GUIContent shadowBias = EditorGUIUtility.TrTextContent("Bias", "Select if the Bias should use the settings from the Pipeline Asset or Custom settings."); 201 | public static int[] optionDefaultValues = { 0, 1 }; 202 | public static GUIContent[] displayedDefaultOptions = 203 | { 204 | EditorGUIUtility.TrTextContent("Custom"), 205 | EditorGUIUtility.TrTextContent("Use settings from Render Pipeline Asset") 206 | }; 207 | 208 | public static readonly GUIContent customShadowLayers = EditorGUIUtility.TrTextContent("Custom Shadow Layers", "When enabled, you can use the Layer property below to specify the layers for shadows seperately to lighting. When disabled, the Light Layer property in the General section specifies the layers for both lighting and for shadows."); 209 | public static readonly GUIContent ShadowLayer = EditorGUIUtility.TrTextContent("Layer", "Specifies the light layer to use for shadows."); 210 | 211 | public static readonly GUIContent LightCookieSize = EditorGUIUtility.TrTextContent("Cookie Size", "Controls the size of the cookie mask currently assigned to the light."); 212 | public static readonly GUIContent LightCookieOffset = EditorGUIUtility.TrTextContent("Cookie Offset", "Controls the offset of the cookie mask currently assigned to the light."); 213 | /// Title with "Rendering Layer" 214 | public static readonly GUIContent RenderingLayers = EditorGUIUtility.TrTextContent("Rendering Layers", "Select the Rendering Layers that the Light affects. This Light affects objects where at least one Rendering Layer value matches."); 215 | public static readonly GUIContent RenderingLayersDisabled = EditorGUIUtility.TrTextContent("Rendering Layers", "Rendering Layers are disabled because they have a small GPU performance cost. To enable this setting, go to the active Universal Render Pipeline Asset, and enable Lighting -> Use Rendering Layers."); 216 | 217 | // Additional light data 218 | public static readonly GUIContent directionalIntensity = EditorGUIUtility.TrTextContent("Intensity (Lux)", "Illuminance of the Directional Light, at ground level, in lux."); 219 | public static readonly GUIContent punctualIntensity = EditorGUIUtility.TrTextContent("Intensity (Lumen)", "Luminous power of the Light in lumen."); 220 | public static readonly GUIContent areaIntensity = EditorGUIUtility.TrTextContent("Intensity (Lumen)", "Luminous power of the Light in Lumen."); 221 | public static readonly GUIContent lightIntensity = EditorGUIUtility.TrTextContent("Intensity", "Sets the strength of the Light. Use the drop-down to select the light units to use."); 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /Runtime/Lighting/AdditionalLightData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine.Rendering.HighDefinition; 4 | using UnityEngine.Serialization; 5 | 6 | namespace UnityEngine.Rendering.Universal 7 | { 8 | // This structure contains all the old values for every recordable fields from the HD light editor 9 | // so we can force timeline to record changes on other fields from the LateUpdate function (editor only) 10 | struct TimelineWorkaround 11 | { 12 | public float oldSpotAngle; 13 | public Color oldLightColor; 14 | public Vector3 oldLossyScale; 15 | // public bool oldDisplayAreaLightEmissiveMesh; 16 | public float oldLightColorTemperature; 17 | public float oldIntensity; 18 | public bool lightEnabled; 19 | } 20 | 21 | /// 22 | /// Class containing various additional Physical light Unit data used by URP. 23 | /// 24 | [HDRPHelpURLAttribute("Light-Component")] 25 | [AddComponentMenu("")] // Hide in menu 26 | [DisallowMultipleComponent] 27 | [RequireComponent(typeof(Light))] 28 | [ExecuteAlways] 29 | public partial class AdditionalLightData : MonoBehaviour, ISerializationCallbackReceiver, IAdditionalData 30 | { 31 | Exposure exposureVolumeComponent; 32 | Volume volume; 33 | float exposureValue = 1f; 34 | 35 | /// 36 | /// The default intensity value for directional lights in Lux 37 | /// 38 | public const float k_DefaultDirectionalLightIntensity = Mathf.PI; // In lux 39 | /// 40 | /// The default intensity value for punctual lights in Lumen 41 | /// 42 | public const float k_DefaultPunctualLightIntensity = 600.0f; // Light default to 600 lumen, i.e ~48 candela 43 | /// 44 | /// The default intensity value for area lights in Lumen 45 | /// 46 | public const float k_DefaultAreaLightIntensity = 200.0f; // Light default to 200 lumen to better match point light 47 | 48 | /// 49 | /// Minimum aspect ratio for pyramid spot lights 50 | /// 51 | public const float k_MinAspectRatio = 0.05f; 52 | /// 53 | /// Maximum aspect ratio for pyramid spot lights 54 | /// 55 | public const float k_MaxAspectRatio = 20.0f; 56 | 57 | /// List of the lights that overlaps when the OverlapLight scene view mode is enabled 58 | internal static HashSet s_overlappingHDLights = new HashSet(); 59 | 60 | #region HDLight Properties API 61 | [SerializeField, FormerlySerializedAs("displayLightIntensity")] 62 | float m_Intensity; 63 | /// 64 | /// Get/Set the intensity of the light using the current light unit. 65 | /// 66 | public float intensity 67 | { 68 | get => m_Intensity; 69 | set 70 | { 71 | if (m_Intensity == value) 72 | return; 73 | 74 | m_Intensity = Mathf.Clamp(value, 0, float.MaxValue); 75 | UpdateLightIntensity(); 76 | } 77 | } 78 | 79 | // Only for Spotlight, should be hide for other light 80 | [SerializeField, FormerlySerializedAs("enableSpotReflector")] 81 | bool m_EnableSpotReflector = true; 82 | /// 83 | /// Get/Set the Spot Reflection option on spot lights. 84 | /// 85 | public bool enableSpotReflector 86 | { 87 | get => m_EnableSpotReflector; 88 | set 89 | { 90 | if (m_EnableSpotReflector == value) 91 | return; 92 | 93 | m_EnableSpotReflector = value; 94 | UpdateLightIntensity(); 95 | } 96 | } 97 | 98 | // Lux unity for all light except directional require a distance 99 | [SerializeField, FormerlySerializedAs("luxAtDistance")] 100 | float m_LuxAtDistance = 1.0f; 101 | /// 102 | /// Set/Get the distance for spot lights where the emission intensity is matches the value set in the intensity property. 103 | /// 104 | public float luxAtDistance 105 | { 106 | get => m_LuxAtDistance; 107 | set 108 | { 109 | if (m_LuxAtDistance == value) 110 | return; 111 | 112 | m_LuxAtDistance = Mathf.Clamp(value, 0, float.MaxValue); 113 | UpdateLightIntensity(); 114 | } 115 | } 116 | 117 | [Range(0.0f, 16.0f), SerializeField, FormerlySerializedAs("volumetricDimmer")] 118 | float m_VolumetricDimmer = 1.0f; 119 | /// 120 | /// Get/Set the light dimmer / multiplier on volumetric effects, between 0 and 16. 121 | /// 122 | public float volumetricDimmer 123 | { 124 | get => useVolumetric ? m_VolumetricDimmer : 0.0f; 125 | set 126 | { 127 | if (m_VolumetricDimmer == value) 128 | return; 129 | 130 | m_VolumetricDimmer = Mathf.Clamp(value, 0.0f, 16.0f); 131 | 132 | // if (lightEntity.valid) 133 | // HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).volumetricDimmer = m_VolumetricDimmer; 134 | } 135 | } 136 | 137 | // Used internally to convert any light unit input into light intensity 138 | [SerializeField, FormerlySerializedAs("lightUnit")] 139 | LightUnit m_LightUnit = LightUnit.Lumen; 140 | /// 141 | /// Get/Set the light unit. When changing the light unit, the intensity will be converted to match the previous intensity in the new unit. 142 | /// 143 | public LightUnit lightUnit 144 | { 145 | get => m_LightUnit; 146 | set 147 | { 148 | if (m_LightUnit == value) 149 | return; 150 | 151 | if (!IsValidLightUnitForType(legacyLight.type, value)) 152 | { 153 | var supportedTypes = String.Join(", ", GetSupportedLightUnits(legacyLight.type)); 154 | Debug.LogError($"Set Light Unit '{value}' to a {GetLightTypeName()} is not allowed, only {supportedTypes} are supported."); 155 | return; 156 | } 157 | 158 | LightUtils.ConvertLightIntensity(m_LightUnit, value, this, legacyLight); 159 | 160 | m_LightUnit = value; 161 | UpdateLightIntensity(); 162 | } 163 | } 164 | 165 | // Not used for directional lights. 166 | [SerializeField, FormerlySerializedAs("fadeDistance")] 167 | float m_FadeDistance = 10000.0f; 168 | /// 169 | /// Get/Set the light fade distance. 170 | /// 171 | public float fadeDistance 172 | { 173 | get => m_FadeDistance; 174 | set 175 | { 176 | if (m_FadeDistance == value) 177 | return; 178 | 179 | m_FadeDistance = Mathf.Clamp(value, 0, float.MaxValue); 180 | 181 | // if (lightEntity.valid) 182 | // HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).fadeDistance = m_FadeDistance; 183 | } 184 | } 185 | 186 | // Not used for directional lights. 187 | [SerializeField] 188 | float m_VolumetricFadeDistance = 10000.0f; 189 | /// 190 | /// Get/Set the light fade distance for volumetrics. 191 | /// 192 | public float volumetricFadeDistance 193 | { 194 | get => m_VolumetricFadeDistance; 195 | set 196 | { 197 | if (m_VolumetricFadeDistance == value) 198 | return; 199 | 200 | m_VolumetricFadeDistance = Mathf.Clamp(value, 0, float.MaxValue); 201 | // if (lightEntity.valid) 202 | // HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).volumetricFadeDistance = m_VolumetricFadeDistance; 203 | } 204 | } 205 | 206 | // Only for pyramid projector 207 | [SerializeField, FormerlySerializedAs("aspectRatio")] 208 | float m_AspectRatio = 1.0f; 209 | /// 210 | /// Get/Set the aspect ratio of a pyramid light 211 | /// 212 | public float aspectRatio 213 | { 214 | get => m_AspectRatio; 215 | set 216 | { 217 | if (m_AspectRatio == value) 218 | return; 219 | 220 | m_AspectRatio = Mathf.Clamp(value, k_MinAspectRatio, k_MaxAspectRatio); 221 | UpdateAllLightValues(); 222 | } 223 | } 224 | 225 | // TODO: Celestial Body UI in Directional Light only For Physical Sky 226 | 227 | // Directional lights only. 228 | [SerializeField, FormerlySerializedAs("interactsWithSky")] 229 | bool m_InteractsWithSky = true; 230 | /// 231 | /// Controls if the directional light affect the Physically Based sky. 232 | /// This have no effect on other skies. 233 | /// 234 | public bool interactsWithSky 235 | { 236 | // m_InteractWithSky can be true if user changed from directional to point light, so we need to check current type 237 | get => m_InteractsWithSky && legacyLight.type == LightType.Directional; 238 | set 239 | { 240 | if (m_InteractsWithSky == value) 241 | return; 242 | 243 | m_InteractsWithSky = value; 244 | // if (lightEntity.valid) 245 | // HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).interactsWithSky = m_InteractsWithSky; 246 | } 247 | } 248 | [SerializeField, FormerlySerializedAs("angularDiameter")] 249 | float m_AngularDiameter = 0.5f; 250 | /// 251 | /// Angular diameter of the emissive celestial body represented by the light as seen from the camera (in degrees). 252 | /// Used to render the sun/moon disk. 253 | /// 254 | public float angularDiameter 255 | { 256 | get => m_AngularDiameter; 257 | set 258 | { 259 | if (m_AngularDiameter == value) 260 | return; 261 | 262 | m_AngularDiameter = value; // Serialization code clamps 263 | // if (lightEntity.valid) 264 | // HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).angularDiameter = m_AngularDiameter; 265 | } 266 | } 267 | 268 | [SerializeField, FormerlySerializedAs("flareSize")] 269 | float m_FlareSize = 2.0f; 270 | /// 271 | /// Size the flare around the celestial body (in degrees). 272 | /// 273 | public float flareSize 274 | { 275 | get => m_FlareSize; 276 | set 277 | { 278 | if (m_FlareSize == value) 279 | return; 280 | 281 | m_FlareSize = value; // Serialization code clamps 282 | // if (lightEntity.valid) 283 | // HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).flareSize = m_FlareSize; 284 | } 285 | } 286 | 287 | [SerializeField, FormerlySerializedAs("flareTint")] 288 | Color m_FlareTint = Color.white; 289 | /// 290 | /// Tints the flare of the celestial body. 291 | /// 292 | public Color flareTint 293 | { 294 | get => m_FlareTint; 295 | set 296 | { 297 | if (m_FlareTint == value) 298 | return; 299 | 300 | m_FlareTint = value; 301 | // if (lightEntity.valid) 302 | // HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).flareTint = m_FlareTint; 303 | } 304 | } 305 | 306 | [SerializeField, FormerlySerializedAs("flareFalloff")] 307 | float m_FlareFalloff = 4.0f; 308 | /// 309 | /// The falloff rate of flare intensity as the angle from the light increases. 310 | /// 311 | public float flareFalloff 312 | { 313 | get => m_FlareFalloff; 314 | set 315 | { 316 | if (m_FlareFalloff == value) 317 | return; 318 | 319 | m_FlareFalloff = value; // Serialization code clamps 320 | // if (lightEntity.valid) 321 | // HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).flareFalloff = m_FlareFalloff; 322 | } 323 | } 324 | 325 | [SerializeField, FormerlySerializedAs("surfaceTexture")] 326 | Texture2D m_SurfaceTexture = null; 327 | /// 328 | /// 2D (disk) texture of the surface of the celestial body. Acts like a multiplier. 329 | /// 330 | public Texture2D surfaceTexture 331 | { 332 | get => m_SurfaceTexture; 333 | set 334 | { 335 | if (m_SurfaceTexture == value) 336 | return; 337 | 338 | m_SurfaceTexture = value; 339 | } 340 | } 341 | 342 | [SerializeField, FormerlySerializedAs("surfaceTint")] 343 | Color m_SurfaceTint = Color.white; 344 | /// 345 | /// Tints the surface of the celestial body. 346 | /// 347 | public Color surfaceTint 348 | { 349 | get => m_SurfaceTint; 350 | set 351 | { 352 | if (m_SurfaceTint == value) 353 | return; 354 | 355 | m_SurfaceTint = value; 356 | // if (lightEntity.valid) 357 | // HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).surfaceTint = m_SurfaceTint; 358 | } 359 | } 360 | 361 | [SerializeField, FormerlySerializedAs("distance")] 362 | float m_Distance = 150000000000; // Sun to Earth 363 | /// 364 | /// Distance from the camera to the emissive celestial body represented by the light. 365 | /// 366 | public float distance 367 | { 368 | get => m_Distance; 369 | set 370 | { 371 | if (m_Distance == value) 372 | return; 373 | 374 | m_Distance = value; // Serialization code clamps 375 | // if (lightEntity.valid) 376 | // HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).distance = m_Distance; 377 | } 378 | } 379 | 380 | /// 381 | /// Color of the light. 382 | /// 383 | public Color color 384 | { 385 | get => legacyLight.color; 386 | set => legacyLight.color = value; 387 | } 388 | #endregion // HDLight Properties API 389 | 390 | #region HDShadow Properties API (from AdditionalShadowData) 391 | 392 | [Range(0.0f, 1.0f)] 393 | [SerializeField] 394 | float m_VolumetricShadowDimmer = 1.0f; 395 | /// 396 | /// Get/Set the volumetric shadow dimmer value, between 0 and 1. 397 | /// 398 | public float volumetricShadowDimmer 399 | { 400 | get => useVolumetric ? m_VolumetricShadowDimmer : 0.0f; 401 | set 402 | { 403 | if (m_VolumetricShadowDimmer == value) 404 | return; 405 | 406 | m_VolumetricShadowDimmer = Mathf.Clamp01(value); 407 | } 408 | } 409 | 410 | [SerializeField] 411 | float m_ShadowFadeDistance = 10000.0f; 412 | /// 413 | /// Get/Set the shadow fade distance. 414 | /// 415 | public float shadowFadeDistance 416 | { 417 | get => m_ShadowFadeDistance; 418 | set 419 | { 420 | if (m_ShadowFadeDistance == value) 421 | return; 422 | 423 | m_ShadowFadeDistance = Mathf.Clamp(value, 0, float.MaxValue); 424 | } 425 | } 426 | #endregion // HDShadow Properties API (from AdditionalShadowData) 427 | 428 | #pragma warning disable 0414 // The field '...' is assigned but its value is never used, these fields are used by the inspector 429 | [SerializeField, FormerlySerializedAs("useVolumetric")] 430 | bool useVolumetric = true; 431 | #pragma warning restore 0414 432 | 433 | // Runtime datas used to compute light intensity 434 | [NonSerialized] 435 | Light m_Light; 436 | internal Light legacyLight 437 | { 438 | get 439 | { 440 | // Calling TryGetComponent only when needed is faster than letting the null check happen inside TryGetComponent 441 | if (m_Light == null) 442 | TryGetComponent(out m_Light); 443 | 444 | return m_Light; 445 | } 446 | } 447 | 448 | void OnDisable() 449 | { 450 | s_overlappingHDLights.Remove(this); 451 | } 452 | 453 | 454 | // We need these old states to make timeline and the animator record the intensity value and the emissive mesh changes 455 | [System.NonSerialized] 456 | TimelineWorkaround timelineWorkaround = new TimelineWorkaround(); 457 | 458 | 459 | internal bool useColorTemperature 460 | { 461 | get => legacyLight.useColorTemperature; 462 | set 463 | { 464 | if (legacyLight.useColorTemperature == value) 465 | return; 466 | 467 | legacyLight.useColorTemperature = value; 468 | } 469 | } 470 | 471 | // TODO: we might be able to get rid to that 472 | [System.NonSerialized] 473 | bool m_Animated; 474 | 475 | private void Start() 476 | { 477 | // If there is an animator attached ot the light, we assume that some of the light properties 478 | // might be driven by this animator (using timeline or animations) so we force the LateUpdate 479 | // to sync the animated HDAdditionalLightData properties with the light component. 480 | m_Animated = GetComponent() != null; 481 | } 482 | 483 | // TODO: There are a lot of old != current checks and assignation in this function, maybe think about using another system ? 484 | void LateUpdate() 485 | { 486 | // Prevent any unwanted sync when not in HDRP (case 1217575) 487 | if (RenderPipelineManager.currentPipeline == null) 488 | return; 489 | 490 | // We force the animation in the editor and in play mode when there is an animator component attached to the light 491 | #if !UNITY_EDITOR 492 | if (!m_Animated) 493 | return; 494 | #endif 495 | 496 | #if UNITY_EDITOR 497 | 498 | // Update the list of overlapping lights for the LightOverlap scene view mode 499 | if (IsOverlapping()) 500 | s_overlappingHDLights.Add(this); 501 | else 502 | s_overlappingHDLights.Remove(this); 503 | #endif 504 | 505 | if (legacyLight.enabled != timelineWorkaround.lightEnabled) 506 | { 507 | 508 | timelineWorkaround.lightEnabled = legacyLight.enabled; 509 | } 510 | 511 | // Check if the intensity have been changed by the inspector or an animator 512 | if (timelineWorkaround.oldLossyScale != transform.lossyScale 513 | || intensity != timelineWorkaround.oldIntensity 514 | || legacyLight.colorTemperature != timelineWorkaround.oldLightColorTemperature) 515 | { 516 | UpdateLightIntensity(); 517 | 518 | timelineWorkaround.oldLossyScale = transform.lossyScale; 519 | timelineWorkaround.oldIntensity = intensity; 520 | timelineWorkaround.oldLightColorTemperature = legacyLight.colorTemperature; 521 | } 522 | 523 | // Same check for light angle to update intensity using spot angle 524 | if (legacyLight.type == LightType.Spot && (timelineWorkaround.oldSpotAngle != legacyLight.spotAngle)) 525 | { 526 | UpdateLightIntensity(); 527 | timelineWorkaround.oldSpotAngle = legacyLight.spotAngle; 528 | } 529 | 530 | if (legacyLight.color != timelineWorkaround.oldLightColor 531 | || timelineWorkaround.oldLossyScale != transform.lossyScale 532 | || legacyLight.colorTemperature != timelineWorkaround.oldLightColorTemperature) 533 | { 534 | timelineWorkaround.oldLightColor = legacyLight.color; 535 | timelineWorkaround.oldLossyScale = transform.lossyScale; 536 | timelineWorkaround.oldLightColorTemperature = legacyLight.colorTemperature; 537 | } 538 | } 539 | 540 | void OnDidApplyAnimationProperties() 541 | { 542 | UpdateAllLightValues(); 543 | } 544 | 545 | // As we have our own default value, we need to initialize the light intensity correctly 546 | /// 547 | /// Initialize an AdditionalLightData that have just beeing created. 548 | /// 549 | /// 550 | public static void InitDefaultHDAdditionalLightData(AdditionalLightData lightData) 551 | { 552 | // Special treatment for Unity built-in area light. Change it to our rectangle light 553 | var light = lightData.gameObject.GetComponent(); 554 | 555 | // Set light intensity and unit using its type 556 | //note: requiring type convert Rectangle and Disc to Area and correctly set areaLight 557 | switch (lightData.legacyLight.type) 558 | { 559 | case LightType.Directional: 560 | lightData.lightUnit = LightUnit.Lux; 561 | lightData.intensity = k_DefaultDirectionalLightIntensity / Mathf.PI * 100000.0f; // Change back to just k_DefaultDirectionalLightIntensity on 11.0.0 (can't change constant as it's a breaking change) 562 | break; 563 | case LightType.Area: // Rectangle by default when light is created 564 | switch (lightData.legacyLight.type) 565 | { 566 | case LightType.Rectangle: 567 | lightData.lightUnit = LightUnit.Lumen; 568 | lightData.intensity = k_DefaultAreaLightIntensity; 569 | light.shadowNearPlane = 0; 570 | light.shadows = LightShadows.None; 571 | #if UNITY_EDITOR 572 | light.areaSize = new Vector2(0.5f, 0.5f); 573 | #endif 574 | break; 575 | case LightType.Disc: 576 | //[TODO: to be defined] 577 | break; 578 | } 579 | break; 580 | case LightType.Point: 581 | case LightType.Spot: 582 | lightData.lightUnit = LightUnit.Lumen; 583 | lightData.intensity = k_DefaultPunctualLightIntensity; 584 | break; 585 | } 586 | 587 | // We don't use the global settings of shadow mask by default 588 | light.lightShadowCasterMode = LightShadowCasterMode.Everything; 589 | 590 | // lightData.normalBias = 0.75f; light.shadowNormalBias = 0.75f; 591 | // lightData.slopeBias = 0.5f; 592 | 593 | // Enable filter/temperature mode by default for all light types 594 | lightData.useColorTemperature = true; 595 | } 596 | 597 | 598 | #region Update functions to patch values in the Light component when we change properties inside HDAdditionalLightData 599 | 600 | void SetLightIntensityPunctual(float intensity) 601 | { 602 | switch (legacyLight.type) 603 | { 604 | case LightType.Directional: 605 | legacyLight.intensity = intensity; // Always in lux 606 | break; 607 | case LightType.Point: 608 | if (lightUnit == LightUnit.Candela) 609 | legacyLight.intensity = intensity; 610 | else 611 | legacyLight.intensity = LightUtils.ConvertPointLightLumenToCandela(intensity); 612 | break; 613 | case LightType.Spot: 614 | if (lightUnit == LightUnit.Candela) 615 | { 616 | // When using candela, reflector don't have any effect. Our intensity is candela = lumens/steradian and the user 617 | // provide desired value for an angle of 1 steradian. 618 | legacyLight.intensity = intensity; 619 | } 620 | else // lumen 621 | { 622 | if (enableSpotReflector) 623 | { 624 | // If reflector is enabled all the lighting from the sphere is focus inside the solid angle of current shape 625 | // if (spotLightShape == SpotLightShape.Cone) 626 | // { 627 | legacyLight.intensity = LightUtils.ConvertSpotLightLumenToCandela(intensity, legacyLight.spotAngle * Mathf.Deg2Rad, true); 628 | // } 629 | // else if (spotLightShape == SpotLightShape.Pyramid) 630 | // { 631 | // float angleA, angleB; 632 | // LightUtils.CalculateAnglesForPyramid(aspectRatio, legacyLight.spotAngle * Mathf.Deg2Rad, out angleA, out angleB); 633 | // 634 | // legacyLight.intensity = LightUtils.ConvertFrustrumLightLumenToCandela(intensity, angleA, angleB); 635 | // } 636 | // else // Box shape, fallback to punctual light. 637 | // { 638 | // legacyLight.intensity = LightUtils.ConvertPointLightLumenToCandela(intensity); 639 | // } 640 | } 641 | else 642 | { 643 | // No reflector, angle act as occlusion of point light. 644 | legacyLight.intensity = LightUtils.ConvertPointLightLumenToCandela(intensity); 645 | } 646 | } 647 | break; 648 | } 649 | } 650 | 651 | void UpdateLightIntensity() 652 | { 653 | if (lightUnit == LightUnit.Lumen) 654 | { 655 | // if (m_PointlightHDType == PointLightHDType.Punctual) 656 | if (legacyLight.type != LightType.Area ) 657 | SetLightIntensityPunctual(intensity); 658 | #if UNITY_EDITOR 659 | // Area Light (Editor Only for URP) 660 | else 661 | legacyLight.intensity = LightUtils.ConvertAreaLightLumenToLuminance(legacyLight.type, intensity, legacyLight.areaSize.x, legacyLight.areaSize.y); 662 | #endif 663 | } 664 | else if (lightUnit == LightUnit.Ev100) 665 | { 666 | legacyLight.intensity = LightUtils.ConvertEvToLuminance(m_Intensity); 667 | } 668 | else 669 | { 670 | LightType lightType = legacyLight.type; 671 | if ((lightType == LightType.Spot || lightType == LightType.Point) && lightUnit == LightUnit.Lux) 672 | { 673 | // Box are local directional light with lux unity without at distance 674 | // if ((lightType == LightType.Spot) && (spotLightShape == SpotLightShape.Box)) 675 | // legacyLight.intensity = m_Intensity; 676 | // else 677 | legacyLight.intensity = LightUtils.ConvertLuxToCandela(m_Intensity, luxAtDistance); 678 | } 679 | else 680 | legacyLight.intensity = m_Intensity; 681 | } 682 | 683 | // add exposure 684 | legacyLight.intensity *= exposureValue; 685 | 686 | #if UNITY_EDITOR 687 | legacyLight.SetLightDirty(); // Should be apply only to parameter that's affect GI, but make the code cleaner 688 | #endif 689 | } 690 | 691 | /// 692 | /// Synchronize all the HD Additional Light values with the Light component. 693 | /// 694 | public void UpdateAllLightValues() 695 | { 696 | // Update light intensity 697 | UpdateLightIntensity(); 698 | } 699 | 700 | #endregion 701 | 702 | #region User API functions 703 | 704 | /// 705 | /// Set the color of the light. 706 | /// 707 | /// Color 708 | /// Optional color temperature 709 | public void SetColor(Color color, float colorTemperature = -1) 710 | { 711 | if (colorTemperature != -1) 712 | { 713 | legacyLight.colorTemperature = colorTemperature; 714 | useColorTemperature = true; 715 | } 716 | 717 | this.color = color; 718 | } 719 | 720 | /// 721 | /// Toggle the usage of color temperature. 722 | /// 723 | /// 724 | public void EnableColorTemperature(bool enable) 725 | { 726 | useColorTemperature = enable; 727 | } 728 | 729 | /// 730 | /// Set the intensity of the light using the current unit. 731 | /// 732 | /// 733 | public void SetIntensity(float intensity) => this.intensity = intensity; 734 | 735 | /// 736 | /// Set the intensity of the light using unit in parameter. 737 | /// 738 | /// 739 | /// Unit must be a valid Light Unit for the current light type 740 | public void SetIntensity(float intensity, LightUnit unit) 741 | { 742 | this.lightUnit = unit; 743 | this.intensity = intensity; 744 | } 745 | 746 | /// 747 | /// For Spot Lights only, set the intensity that the spot should emit at a certain distance in meter 748 | /// 749 | /// 750 | /// 751 | public void SetSpotLightLuxAt(float luxIntensity, float distance) 752 | { 753 | lightUnit = LightUnit.Lux; 754 | luxAtDistance = distance; 755 | intensity = luxIntensity; 756 | } 757 | 758 | /// 759 | /// Set the light unit. 760 | /// 761 | /// Unit of the light 762 | public void SetLightUnit(LightUnit unit) => lightUnit = unit; 763 | 764 | /// 765 | /// Get the list of supported light units depending on the current light type. 766 | /// 767 | /// 768 | public LightUnit[] GetSupportedLightUnits() => GetSupportedLightUnits(legacyLight.type); 769 | 770 | #endregion // User API Functions 771 | 772 | /// 773 | /// Deserialization callback 774 | /// 775 | void ISerializationCallbackReceiver.OnAfterDeserialize(){} 776 | 777 | /// 778 | /// Serialization callback 779 | /// 780 | void ISerializationCallbackReceiver.OnBeforeSerialize(){} 781 | 782 | internal void Reset() 783 | => InitDefaultHDAdditionalLightData(this); 784 | 785 | void Update() 786 | { 787 | #region Exposure 788 | if (exposureVolumeComponent == null) 789 | { 790 | volume = FindObjectOfType() as Volume; 791 | if (volume != null && volume.sharedProfile != null) 792 | { 793 | volume.sharedProfile.TryGet(out exposureVolumeComponent); 794 | } 795 | } 796 | 797 | if(exposureVolumeComponent != null && exposureVolumeComponent.IsActive()) 798 | { 799 | float newExposure = exposureVolumeComponent.exposure; 800 | if ( Mathf.Abs(exposureValue - newExposure) > Mathf.Epsilon ) 801 | { 802 | exposureValue = newExposure; 803 | UpdateLightIntensity(); 804 | } 805 | } 806 | else 807 | { 808 | float newExposure = 1f; 809 | if (Mathf.Abs(exposureValue - newExposure) > Mathf.Epsilon) 810 | { 811 | exposureValue = newExposure; 812 | UpdateLightIntensity(); 813 | } 814 | } 815 | #endregion 816 | } 817 | 818 | /// Tell if the light is overlapping for the light overlap debug mode 819 | internal bool IsOverlapping() 820 | { 821 | var baking = GetComponent().bakingOutput; 822 | bool isOcclusionSeparatelyBaked = baking.occlusionMaskChannel != -1; 823 | bool isDirectUsingBakedOcclusion = baking.mixedLightingMode == MixedLightingMode.Shadowmask || baking.mixedLightingMode == MixedLightingMode.Subtractive; 824 | return isDirectUsingBakedOcclusion && !isOcclusionSeparatelyBaked; 825 | } 826 | } 827 | } 828 | --------------------------------------------------------------------------------