├── 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 | 
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 | 
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 |
--------------------------------------------------------------------------------