├── .github
└── FUNDING.yml
├── .gitignore
├── CHANGELOG.md
├── CHANGELOG.md.meta
├── Editor.meta
├── Editor
├── MirrorEditor.cs
├── MirrorEditor.cs.meta
├── kTools.Mirrors.Editor.asmdef
└── kTools.Mirrors.Editor.asmdef.meta
├── Gizmos.meta
├── Gizmos
├── Mirror.png
└── Mirror.png.meta
├── LICENSE
├── LICENSE.meta
├── README.md
├── README.md.meta
├── Runtime.meta
├── Runtime
├── Mirror.cs
├── Mirror.cs.meta
├── kTools.Mirrors.Runtime.asmdef
└── kTools.Mirrors.Runtime.asmdef.meta
├── Tests.meta
├── Tests
├── Editor.meta
└── Editor
│ ├── kTools.Mirrors.Editor.Tests.asmdef
│ └── kTools.Mirrors.Editor.Tests.asmdef.meta
├── package.json
└── package.json.meta
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: Kink3d
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.vscode/
2 |
3 | # Visual Studio cache directory
4 | .vs/
5 |
6 | # Autogenerated VS/MD/Consulo solution and project files
7 | ExportedObj/
8 | .consulo/
9 | *.csproj
10 | *.unityproj
11 | *.sln
12 | *.suo
13 | *.tmp
14 | *.user
15 | *.userprefs
16 | *.pidb
17 | *.booproj
18 | *.svd
19 | *.pdb
20 | *.mdb
21 | *.opendb
22 | *.VC.db
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this package are documented in this file.
3 |
4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6 |
7 | ## 0.1.0 - 2020-01-30
8 | ### Added
9 | - Camera management
10 | - Reflection rendering
11 | - Camera controls
12 | - Local mirrors
13 | - Support for kShading Lit and Toon Lit
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e6f16d9661a4cd04bb5b4b6af1f5e9a3
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 91b6875a43543e9459f88d979b41fec1
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/MirrorEditor.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEditor;
3 | using UnityEditorInternal;
4 |
5 | namespace kTools.Mirrors.Editor
6 | {
7 | using Editor = UnityEditor.Editor;
8 |
9 | [CustomEditor(typeof(Mirror)), CanEditMultipleObjects]
10 | sealed class MirrorEditor : Editor
11 | {
12 | #region Structs
13 | struct Styles
14 | {
15 | // Foldouts
16 | public static readonly GUIContent ProjectionOptions = new GUIContent("Projection Options");
17 | public static readonly GUIContent OutputOptions = new GUIContent("Output Options");
18 |
19 | // Properties
20 | public static readonly GUIContent Offset = new GUIContent("Offset",
21 | "Offset value for oplique near clip plane.");
22 |
23 | public static readonly GUIContent LayerMask = new GUIContent("Layer Mask",
24 | "Which layers should the Mirror render.");
25 |
26 | public static readonly GUIContent Scope = new GUIContent("Scope",
27 | "Global output renders to the global texture. Only one Mirror can be global. Local output renders to one texture per Mirror, this is set on all elements of the Renderers list.");
28 |
29 | public static readonly GUIContent Renderers = new GUIContent("Renderers",
30 | "Renderers to set the reflection texture on.");
31 |
32 | public static readonly GUIContent TextureScale = new GUIContent("Texture Scale",
33 | "Scale value applied to the size of the source camera texture.");
34 |
35 | public static readonly GUIContent HDR = new GUIContent("HDR",
36 | "Should reflections be rendered in HDR.");
37 |
38 | public static readonly GUIContent MSAA = new GUIContent("MSAA",
39 | "Should reflections be resolved with MSAA.");
40 | }
41 |
42 | struct PropertyNames
43 | {
44 | public static readonly string Offset = "m_Offset";
45 | public static readonly string LayerMask = "m_LayerMask";
46 | public static readonly string Scope = "m_Scope";
47 | public static readonly string Renderers = "m_Renderers";
48 | public static readonly string TextureScale = "m_TextureScale";
49 | public static readonly string AllowHDR = "m_AllowHDR";
50 | public static readonly string AllowMSAA = "m_AllowMSAA";
51 | }
52 | #endregion
53 |
54 | #region Fields
55 | const string kEditorPrefKey = "kMirrors:MirrorData:";
56 | Mirror m_Target;
57 |
58 | // Foldouts
59 | bool m_ProjectionOptionsFoldout;
60 | bool m_OutputOptionsFoldout;
61 |
62 | // Properties
63 | SerializedProperty m_OffsetProp;
64 | SerializedProperty m_LayerMaskProp;
65 | SerializedProperty m_ScopeProp;
66 | SerializedProperty m_RenderersProp;
67 | SerializedProperty m_TextureScaleProp;
68 | SerializedProperty m_AllowHDR;
69 | SerializedProperty m_AllowMSAA;
70 | #endregion
71 |
72 | #region State
73 | void OnEnable()
74 | {
75 | // Set data
76 | m_Target = target as Mirror;
77 |
78 | // Get Properties
79 | m_OffsetProp = serializedObject.FindProperty(PropertyNames.Offset);
80 | m_LayerMaskProp = serializedObject.FindProperty(PropertyNames.LayerMask);
81 | m_ScopeProp = serializedObject.FindProperty(PropertyNames.Scope);
82 | m_RenderersProp = serializedObject.FindProperty(PropertyNames.Renderers);
83 | m_TextureScaleProp = serializedObject.FindProperty(PropertyNames.TextureScale);
84 | m_AllowHDR = serializedObject.FindProperty(PropertyNames.AllowHDR);
85 | m_AllowMSAA = serializedObject.FindProperty(PropertyNames.AllowMSAA);
86 | }
87 | #endregion
88 |
89 | #region GUI
90 | public override void OnInspectorGUI()
91 | {
92 | // Get foldouts from EditorPrefs
93 | m_ProjectionOptionsFoldout = GetFoldoutState("ProjectionOptions");
94 | m_OutputOptionsFoldout = GetFoldoutState("OutputOptions");
95 |
96 | // Setup
97 | serializedObject.Update();
98 |
99 | // Projection Options
100 | var projectionOptions = EditorGUILayout.BeginFoldoutHeaderGroup(m_ProjectionOptionsFoldout, Styles.ProjectionOptions);
101 | if(projectionOptions)
102 | {
103 | DrawProjectionOptions();
104 | EditorGUILayout.Space();
105 | }
106 | SetFoldoutState("ProjectionOptions", m_ProjectionOptionsFoldout, projectionOptions);
107 | EditorGUILayout.EndFoldoutHeaderGroup();
108 |
109 | // Output Options
110 | var outputOptions = EditorGUILayout.BeginFoldoutHeaderGroup(m_OutputOptionsFoldout, Styles.OutputOptions);
111 | if(outputOptions)
112 | {
113 | DrawOutputOptions();
114 | EditorGUILayout.Space();
115 | }
116 | SetFoldoutState("OutputOptions", m_OutputOptionsFoldout, outputOptions);
117 | EditorGUILayout.EndFoldoutHeaderGroup();
118 |
119 | // Finalize
120 | serializedObject.ApplyModifiedProperties();
121 | }
122 |
123 | void DrawProjectionOptions()
124 | {
125 | // Clip Plane Offset
126 | EditorGUILayout.PropertyField(m_OffsetProp, Styles.Offset);
127 |
128 | // Layer Mask
129 | EditorGUI.BeginChangeCheck();
130 | LayerMask tempMask = EditorGUILayout.MaskField(Styles.LayerMask, (LayerMask)m_LayerMaskProp.intValue, InternalEditorUtility.layers);
131 | if(EditorGUI.EndChangeCheck())
132 | {
133 | m_LayerMaskProp.intValue = (int)tempMask;
134 | }
135 | }
136 |
137 | void DrawOutputOptions()
138 | {
139 | // Scope
140 | EditorGUILayout.PropertyField(m_ScopeProp, Styles.Scope);
141 |
142 | // Renderers
143 | if(m_ScopeProp.enumValueIndex == (int)Mirror.OutputScope.Local)
144 | {
145 | EditorGUI.indentLevel++;
146 | EditorGUILayout.PropertyField(m_RenderersProp, Styles.Renderers);
147 | EditorGUI.indentLevel--;
148 | }
149 |
150 | // Texture Scale
151 | EditorGUI.BeginChangeCheck();
152 | var textureScale = EditorGUILayout.Slider(Styles.TextureScale, m_TextureScaleProp.floatValue, 0, 1);
153 | if(EditorGUI.EndChangeCheck())
154 | {
155 | m_TextureScaleProp.floatValue = textureScale;
156 | }
157 |
158 | // HDR
159 | EditorGUILayout.PropertyField(m_AllowHDR, Styles.HDR);
160 |
161 | // MSAA
162 | EditorGUILayout.PropertyField(m_AllowMSAA, Styles.MSAA);
163 | }
164 | #endregion
165 |
166 | #region EditorPrefs
167 | bool GetFoldoutState(string name)
168 | {
169 | // Get value from EditorPrefs
170 | return EditorPrefs.GetBool($"{kEditorPrefKey}.{name}");
171 | }
172 |
173 | void SetFoldoutState(string name, bool field, bool value)
174 | {
175 | if(field == value)
176 | return;
177 |
178 | // Set value to EditorPrefs and field
179 | EditorPrefs.SetBool($"{kEditorPrefKey}.{name}", value);
180 | field = value;
181 | }
182 | #endregion
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/Editor/MirrorEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f989b0e08ab6cc34f9643e90fcb99cd8
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/kTools.Mirrors.Editor.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kTools.Mirrors.Editor",
3 | "references": [
4 | "GUID:9e705d6a72d9a354b810376671ddb3ac"
5 | ],
6 | "includePlatforms": [
7 | "Editor"
8 | ],
9 | "excludePlatforms": [],
10 | "allowUnsafeCode": false,
11 | "overrideReferences": true,
12 | "precompiledReferences": [],
13 | "autoReferenced": false,
14 | "defineConstraints": [],
15 | "versionDefines": [],
16 | "noEngineReferences": false
17 | }
--------------------------------------------------------------------------------
/Editor/kTools.Mirrors.Editor.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bfe6fbd0060e7e94883a1710b8ae7995
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Gizmos.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2aa965d0b276c594685f9dac6f21c0d0
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Gizmos/Mirror.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kink3d/kMirrors/79b0526e5da06689f12a7f4846b2b568e997be35/Gizmos/Mirror.png
--------------------------------------------------------------------------------
/Gizmos/Mirror.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 679c06de8e095fc4d8489115cfc43352
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 10
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 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: 1
31 | maxTextureSize: 2048
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -100
37 | wrapU: -1
38 | wrapV: -1
39 | wrapW: -1
40 | nPOTScale: 1
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 0
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 0
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | platformSettings:
61 | - serializedVersion: 3
62 | buildTarget: DefaultTexturePlatform
63 | maxTextureSize: 2048
64 | resizeAlgorithm: 0
65 | textureFormat: -1
66 | textureCompression: 1
67 | compressionQuality: 50
68 | crunchedCompression: 0
69 | allowsAlphaSplitting: 0
70 | overridden: 0
71 | androidETC2FallbackOverride: 0
72 | forceMaximumCompressionQuality_BC6H_BC7: 0
73 | - serializedVersion: 3
74 | buildTarget: Standalone
75 | maxTextureSize: 2048
76 | resizeAlgorithm: 0
77 | textureFormat: -1
78 | textureCompression: 1
79 | compressionQuality: 50
80 | crunchedCompression: 0
81 | allowsAlphaSplitting: 0
82 | overridden: 0
83 | androidETC2FallbackOverride: 0
84 | forceMaximumCompressionQuality_BC6H_BC7: 0
85 | spriteSheet:
86 | serializedVersion: 2
87 | sprites: []
88 | outline: []
89 | physicsShape: []
90 | bones: []
91 | spriteID:
92 | internalID: 0
93 | vertices: []
94 | indices:
95 | edges: []
96 | weights: []
97 | secondaryTextures: []
98 | spritePackingTag:
99 | pSDRemoveMatte: 0
100 | pSDShowRemoveMatteOption: 0
101 | userData:
102 | assetBundleName:
103 | assetBundleVariant:
104 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Matt Dean
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0b1ef955611321d4c964399689ef1ef7
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # kMirrors
2 | ### Planar reflections for Unity’s Universal Render Pipeline.
3 |
4 | 
5 | *An example of global and local reflections.*
6 |
7 | kMirrors is a system for defining and rendering planar reflection cameras in Unity's Universal Render Pipeline. It supports **Global** mode, where a single reflection camera can be used across an entire scene (useful for water and other large reflective surfaces) and **Local** mode, where a list of **Renderers** can be defined to receive reflections (useful for smaller surfaces like wall mirrors).
8 |
9 | Refer to the [Wiki](https://github.com/Kink3d/kMirrors/wiki/Home) for more information.
10 |
11 | ## Instructions
12 | - Open your project manifest file (`MyProject/Packages/manifest.json`).
13 | - Add `"com.kink3d.mirrors": "https://github.com/Kink3d/kMirrors.git"` to the `dependencies` list.
14 | - Open or focus on Unity Editor to resolve packages.
15 |
16 | ## Requirements
17 | - Unity 2019.3.0f3 or higher.
--------------------------------------------------------------------------------
/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 348769316597fe54b9db2cd435d11a02
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Runtime.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 683872b7a9e58404d86a72f64355a908
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/Mirror.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 | using UnityEngine.Rendering;
4 | using UnityEngine.Rendering.Universal;
5 |
6 | namespace kTools.Mirrors
7 | {
8 | ///
9 | /// Mirror Object component.
10 | ///
11 | [AddComponentMenu("kTools/Mirror"), ExecuteInEditMode]
12 | [RequireComponent(typeof(Camera), typeof(UniversalAdditionalCameraData))]
13 | public class Mirror : MonoBehaviour
14 | {
15 | #region Enumerations
16 | ///
17 | /// Camera override enumeration for Mirror properties
18 | ///
19 | public enum MirrorCameraOverride
20 | {
21 | UseSourceCameraSettings,
22 | Off,
23 | }
24 |
25 | ///
26 | /// Scope enumeration for Mirror output destination
27 | ///
28 | public enum OutputScope
29 | {
30 | Global,
31 | Local,
32 | }
33 | #endregion
34 |
35 | #region Serialized Fields
36 | [SerializeField]
37 | float m_Offset;
38 |
39 | [SerializeField]
40 | int m_LayerMask;
41 |
42 | [SerializeField]
43 | OutputScope m_Scope;
44 |
45 | [SerializeField]
46 | List m_Renderers;
47 |
48 | [SerializeField]
49 | float m_TextureScale;
50 |
51 | [SerializeField]
52 | MirrorCameraOverride m_AllowHDR;
53 |
54 | [SerializeField]
55 | MirrorCameraOverride m_AllowMSAA;
56 | #endregion
57 |
58 | #region Fields
59 | const string kGizmoPath = "Packages/com.kink3d.mirrors/Gizmos/Mirror.png";
60 | Camera m_ReflectionCamera;
61 | UniversalAdditionalCameraData m_CameraData;
62 | RenderTexture m_RenderTexture;
63 | RenderTextureDescriptor m_PreviousDescriptor;
64 | #endregion
65 |
66 | #region Constructors
67 | public Mirror()
68 | {
69 | // Set data
70 | m_Offset = 0.01f;
71 | m_LayerMask = -1;
72 | m_Scope = OutputScope.Global;
73 | m_Renderers = new List();
74 | m_TextureScale = 1.0f;
75 | m_AllowHDR = MirrorCameraOverride.UseSourceCameraSettings;
76 | m_AllowMSAA = MirrorCameraOverride.UseSourceCameraSettings;
77 | }
78 | #endregion
79 |
80 | #region Properties
81 | /// Offset value for oplique near clip plane.
82 | public float offest
83 | {
84 | get => m_Offset;
85 | set => m_Offset = value;
86 | }
87 |
88 | /// Which layers should the Mirror render.
89 | public LayerMask layerMask
90 | {
91 | get => m_LayerMask;
92 | set => m_LayerMask = value;
93 | }
94 |
95 | ///
96 | /// Global output renders to the global texture. Only one Mirror can be global.
97 | /// Local output renders to one texture per Mirror, this is set on all elements of the Renderers list.
98 | ///
99 | public OutputScope scope
100 | {
101 | get => m_Scope;
102 | set => m_Scope = value;
103 | }
104 |
105 | /// Renderers to set the reflection texture on.
106 | public List renderers
107 | {
108 | get => m_Renderers;
109 | set => m_Renderers = value;
110 | }
111 |
112 | /// Scale value applied to the size of the source camera texture.
113 | public float textureScale
114 | {
115 | get => m_TextureScale;
116 | set => m_TextureScale = value;
117 | }
118 |
119 | /// Should reflections be rendered in HDR.
120 | public MirrorCameraOverride allowHDR
121 | {
122 | get => m_AllowHDR;
123 | set => m_AllowHDR = value;
124 | }
125 |
126 | /// Should reflections be resolved with MSAA.
127 | public MirrorCameraOverride allowMSAA
128 | {
129 | get => m_AllowMSAA;
130 | set => m_AllowMSAA = value;
131 | }
132 |
133 | Camera reflectionCamera
134 | {
135 | get
136 | {
137 | if(m_ReflectionCamera == null)
138 | m_ReflectionCamera = GetComponent();
139 | return m_ReflectionCamera;
140 | }
141 | }
142 |
143 | UniversalAdditionalCameraData cameraData
144 | {
145 | get
146 | {
147 | if(m_CameraData == null)
148 | m_CameraData = GetComponent();
149 | return m_CameraData;
150 | }
151 | }
152 | #endregion
153 |
154 | #region State
155 | void OnEnable()
156 | {
157 | // Callbacks
158 | RenderPipelineManager.beginCameraRendering += BeginCameraRendering;
159 |
160 | // Initialize Components
161 | InitializeCamera();
162 | }
163 |
164 | void OnDisable()
165 | {
166 | // Callbacks
167 | RenderPipelineManager.beginCameraRendering -= BeginCameraRendering;
168 |
169 | // Dispose RenderTexture
170 | SafeDestroyObject(m_RenderTexture);
171 | }
172 | #endregion
173 |
174 | #region Initialization
175 | void InitializeCamera()
176 | {
177 | // Setup Camera
178 | reflectionCamera.cameraType = CameraType.Reflection;
179 | reflectionCamera.targetTexture = m_RenderTexture;
180 |
181 | // Setup AdditionalCameraData
182 | cameraData.renderShadows = false;
183 | cameraData.requiresColorOption = CameraOverrideOption.Off;
184 | cameraData.requiresDepthOption = CameraOverrideOption.Off;
185 | }
186 | #endregion
187 |
188 | #region RenderTexture
189 | RenderTextureDescriptor GetDescriptor(Camera camera)
190 | {
191 | // Get scaled Texture size
192 | var width = (int)Mathf.Max(camera.pixelWidth * textureScale, 4);
193 | var height = (int)Mathf.Max(camera.pixelHeight * textureScale, 4);
194 |
195 | // Get Texture format
196 | var hdr = allowHDR == MirrorCameraOverride.UseSourceCameraSettings ? camera.allowHDR : false;
197 | var renderTextureFormat = hdr ? RenderTextureFormat.DefaultHDR : RenderTextureFormat.Default;
198 | return new RenderTextureDescriptor(width, height, renderTextureFormat, 16) { autoGenerateMips = true, useMipMap = true };
199 | }
200 | #endregion
201 |
202 | #region Rendering
203 | void BeginCameraRendering(ScriptableRenderContext context, Camera camera)
204 | {
205 | // Never render Mirrors for Preview or Reflection cameras
206 | if(camera.cameraType == CameraType.Preview || camera.cameraType == CameraType.Reflection)
207 | return;
208 |
209 | // Profiling command
210 | CommandBuffer cmd = CommandBufferPool.Get($"Mirror {gameObject.GetInstanceID()}");
211 | using (new ProfilingSample(cmd, $"Mirror {gameObject.GetInstanceID()}"))
212 | {
213 | ExecuteCommand(context, cmd);
214 |
215 | // Test for Descriptor changes
216 | var descriptor = GetDescriptor(camera);
217 | if(!descriptor.Equals(m_PreviousDescriptor))
218 | {
219 | // Dispose RenderTexture
220 | if(m_RenderTexture != null)
221 | {
222 | SafeDestroyObject(m_RenderTexture);
223 | }
224 |
225 | // Create new RenderTexture
226 | m_RenderTexture = new RenderTexture(descriptor);
227 | m_PreviousDescriptor = descriptor;
228 | reflectionCamera.targetTexture = m_RenderTexture;
229 | }
230 |
231 | // Execute
232 | RenderMirror(context, camera);
233 | SetShaderUniforms(context, m_RenderTexture, cmd);
234 | }
235 | ExecuteCommand(context, cmd);
236 | }
237 |
238 | void RenderMirror(ScriptableRenderContext context, Camera camera)
239 | {
240 | // Mirror the view matrix
241 | var mirrorMatrix = GetMirrorMatrix();
242 | reflectionCamera.worldToCameraMatrix = camera.worldToCameraMatrix * mirrorMatrix;
243 |
244 | // Make oplique projection matrix where near plane is mirror plane
245 | var mirrorPlane = GetMirrorPlane(reflectionCamera);
246 | var projectionMatrix = camera.CalculateObliqueMatrix(mirrorPlane);
247 | reflectionCamera.projectionMatrix = projectionMatrix;
248 |
249 | // Miscellanious camera settings
250 | reflectionCamera.cullingMask = layerMask;
251 | reflectionCamera.allowHDR = allowHDR == MirrorCameraOverride.UseSourceCameraSettings ? camera.allowHDR : false;
252 | reflectionCamera.allowMSAA = allowMSAA == MirrorCameraOverride.UseSourceCameraSettings ? camera.allowMSAA : false;
253 | reflectionCamera.enabled = false;
254 |
255 | // Render reflection camera with inverse culling
256 | GL.invertCulling = true;
257 | UniversalRenderPipeline.RenderSingleCamera(context, reflectionCamera);
258 | GL.invertCulling = false;
259 | }
260 | #endregion
261 |
262 | #region Projection
263 | Matrix4x4 GetMirrorMatrix()
264 | {
265 | // Setup
266 | var position = transform.position;
267 | var normal = transform.forward;
268 | var depth = -Vector3.Dot(normal, position) - offest;
269 |
270 | // Create matrix
271 | var mirrorMatrix = new Matrix4x4()
272 | {
273 | m00 = (1f - 2f * normal.x * normal.x),
274 | m01 = (-2f * normal.x * normal.y),
275 | m02 = (-2f * normal.x * normal.z),
276 | m03 = (-2f * depth * normal.x),
277 | m10 = (-2f * normal.y * normal.x),
278 | m11 = (1f - 2f * normal.y * normal.y),
279 | m12 = (-2f * normal.y * normal.z),
280 | m13 = (-2f * depth * normal.y),
281 | m20 = (-2f * normal.z * normal.x),
282 | m21 = (-2f * normal.z * normal.y),
283 | m22 = (1f - 2f * normal.z * normal.z),
284 | m23 = (-2f * depth * normal.z),
285 | m30 = 0f,
286 | m31 = 0f,
287 | m32 = 0f,
288 | m33 = 1f,
289 | };
290 | return mirrorMatrix;
291 | }
292 |
293 | Vector4 GetMirrorPlane(Camera camera)
294 | {
295 | // Calculate mirror plane in camera space.
296 | var pos = transform.position - Vector3.forward * 0.1f;
297 | var normal = transform.forward;
298 | var offsetPos = pos + normal * offest;
299 | var cpos = camera.worldToCameraMatrix.MultiplyPoint(offsetPos);
300 | var cnormal = camera.worldToCameraMatrix.MultiplyVector(normal).normalized;
301 | return new Vector4(cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos, cnormal));
302 | }
303 | #endregion
304 |
305 | #region Output
306 | void SetShaderUniforms(ScriptableRenderContext context, RenderTexture renderTexture, CommandBuffer cmd)
307 | {
308 | var block = new MaterialPropertyBlock();
309 | switch(scope)
310 | {
311 | case OutputScope.Global:
312 | // Globals
313 | cmd.SetGlobalTexture("_ReflectionMap", renderTexture);
314 | ExecuteCommand(context, cmd);
315 |
316 | // Property Blocm
317 | block.SetFloat("_LocalMirror", 0.0f);
318 | foreach(var renderer in renderers)
319 | {
320 | renderer.SetPropertyBlock(block);
321 | }
322 | break;
323 | case OutputScope.Local:
324 | // Keywords
325 | Shader.EnableKeyword("_BLEND_MIRRORS");
326 |
327 | // Property Block
328 | block.SetTexture("_LocalReflectionMap", renderTexture);
329 | block.SetFloat("_LocalMirror", 1.0f);
330 | foreach(var renderer in renderers)
331 | {
332 | renderer.SetPropertyBlock(block);
333 | }
334 | break;
335 | }
336 | }
337 | #endregion
338 |
339 | #region CommandBufer
340 | void ExecuteCommand(ScriptableRenderContext context, CommandBuffer cmd)
341 | {
342 | context.ExecuteCommandBuffer(cmd);
343 | cmd.Clear();
344 | }
345 | #endregion
346 |
347 | #region Object
348 | void SafeDestroyObject(Object obj)
349 | {
350 | if(obj == null)
351 | return;
352 |
353 | #if UNITY_EDITOR
354 | DestroyImmediate(obj);
355 | #else
356 | Destroy(obj);
357 | #endif
358 | }
359 | #endregion
360 |
361 | #region AssetMenu
362 | #if UNITY_EDITOR
363 | // Add a menu item to Mirrors
364 | [UnityEditor.MenuItem("GameObject/kTools/Mirror", false, 10)]
365 | static void CreateMirrorObject(UnityEditor.MenuCommand menuCommand)
366 | {
367 | // Create Mirror
368 | GameObject go = new GameObject("New Mirror", typeof(Mirror));
369 |
370 | // Transform
371 | UnityEditor.GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
372 |
373 | // Undo and Selection
374 | UnityEditor.Undo.RegisterCreatedObjectUndo(go, "Create " + go.name);
375 | UnityEditor.Selection.activeObject = go;
376 | }
377 | #endif
378 | #endregion
379 |
380 | #region Gizmos
381 | #if UNITY_EDITOR
382 | void OnDrawGizmos()
383 | {
384 | // Setup
385 | var bounds = new Vector3(1.0f, 1.0f, 0.0f);
386 | var color = new Color32(0, 120, 255, 255);
387 | var selectedColor = new Color32(255, 255, 255, 255);
388 | var isSelected = UnityEditor.Selection.activeObject == gameObject;
389 |
390 | // Draw Gizmos
391 | Gizmos.matrix = transform.localToWorldMatrix;
392 | Gizmos.color = isSelected ? selectedColor : color;
393 | Gizmos.DrawIcon(transform.position, kGizmoPath, true);
394 | Gizmos.DrawWireCube(Vector3.zero, bounds);
395 | }
396 | #endif
397 | #endregion
398 | }
399 | }
400 |
--------------------------------------------------------------------------------
/Runtime/Mirror.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 20b9488e8346e6445b175d59f4d2cd5c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {fileID: 2800000, guid: 679c06de8e095fc4d8489115cfc43352, type: 3}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/kTools.Mirrors.Runtime.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kTools.Mirrors.Runtime",
3 | "references": [
4 | "GUID:15fc0a57446b3144c949da3e2b9737a9",
5 | "GUID:df380645f10b7bc4b97d4f5eb6303d95"
6 | ],
7 | "includePlatforms": [
8 | "Android",
9 | "Editor",
10 | "iOS",
11 | "LinuxStandalone64",
12 | "Lumin",
13 | "macOSStandalone",
14 | "PS4",
15 | "Stadia",
16 | "Switch",
17 | "tvOS",
18 | "WSA",
19 | "WebGL",
20 | "WindowsStandalone32",
21 | "WindowsStandalone64",
22 | "XboxOne"
23 | ],
24 | "excludePlatforms": [],
25 | "allowUnsafeCode": false,
26 | "overrideReferences": false,
27 | "precompiledReferences": [],
28 | "autoReferenced": true,
29 | "defineConstraints": [],
30 | "versionDefines": [],
31 | "noEngineReferences": false
32 | }
--------------------------------------------------------------------------------
/Runtime/kTools.Mirrors.Runtime.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9e705d6a72d9a354b810376671ddb3ac
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Tests.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e45556970c8b54c46b3ee1eadf721cbd
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Tests/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 22f2c05c0f4e4d14b9c3ebcd98868851
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Tests/Editor/kTools.Mirrors.Editor.Tests.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kTools.Mirrors.Editor.Tests",
3 | "references": [
4 | "GUID:27619889b8ba8c24980f49ee34dbb44a",
5 | "GUID:0acc523941302664db1f4e527237feb3",
6 | "GUID:9e705d6a72d9a354b810376671ddb3ac"
7 | ],
8 | "includePlatforms": [
9 | "Editor"
10 | ],
11 | "excludePlatforms": [],
12 | "allowUnsafeCode": false,
13 | "overrideReferences": true,
14 | "precompiledReferences": [
15 | "nunit.framework.dll"
16 | ],
17 | "autoReferenced": false,
18 | "defineConstraints": [
19 | "UNITY_INCLUDE_TESTS"
20 | ],
21 | "versionDefines": [],
22 | "noEngineReferences": false
23 | }
--------------------------------------------------------------------------------
/Tests/Editor/kTools.Mirrors.Editor.Tests.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 64aacc806d6d42c4bb2ca701deaaf9ea
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.kink3d.mirrors",
3 | "description": "Planar reflections for Unity's Universal Render Pipeline.",
4 | "version": "0.1.0",
5 | "unity": "2019.3",
6 | "displayName": "kMirrors",
7 | "dependencies": {
8 | "com.unity.render-pipelines.universal": "7.x.x"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/package.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5996b974fb4948d4fb830ac288fefbd1
3 | PackageManifestImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------