Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
Binary file added Documentation~/URPPlanarReflectionLit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Code.meta → Editor/Icons.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added Editor/Icons/URP-PlanarReflections.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
130 changes: 130 additions & 0 deletions Editor/Icons/URP-PlanarReflections.png.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"name": "PlanarReflections.Editor",
"rootNamespace": "",
"rootNamespace": "SiestaGames.PlanarReflections.Editor",
"references": [
"GUID:3eae0364be2026648bf74846acb8a731",
"GUID:c579267770062bf448e75eb160330b7f",
"GUID:37bcc21526b0fe044a73963c28c9faff"
],
"includePlatforms": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
using UnityEngine;
using UnityEditor;


namespace SiestaGames.PlanarReflections
namespace SiestaGames.PlanarReflections.Editor
{

[CustomEditor(typeof(PlanarReflections))]
public class PlanarReflectionsEditor : Editor
public class PlanarReflectionsEditor : UnityEditor.Editor
{
//UnityEditor.Rendering.Universal.ShaderGUI.LitShader
#region Attributes

private bool showPlanarReflTex = true;
Expand Down Expand Up @@ -100,5 +99,4 @@ public override void OnInspectorGUI()
#region Methods
#endregion
}

}
2 changes: 1 addition & 1 deletion Code/Blur.meta → Editor/ShaderGraph/ShaderGUI.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

169 changes: 169 additions & 0 deletions Editor/ShaderGraph/ShaderGUI/URPPlanarReflectionLit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

namespace UnityEditor.Rendering.Universal.ShaderGUI {
public class URPPlanarReflectionLit : BaseShaderGUI {

internal static class Styles
{
public static readonly GUIContent normalReflectionDistortionText = EditorGUIUtility.TrTextContent("Normal Reflection Distortion",
"These settings define the surface refelction distortion.");

public static readonly GUIContent reflectionMultiplierText = EditorGUIUtility.TrTextContent("Reflection Multiplier",
"How much Reflection.");

public static readonly GUIContent reflectionPowerText = EditorGUIUtility.TrTextContent("Reflection Power",
"Reflection Power.");
}

public struct PlanarReflectionLitProperties {
public MaterialProperty normalReflectionDistortion;
public MaterialProperty reflectionMultiplier;
public MaterialProperty reflectionPower;

public PlanarReflectionLitProperties(MaterialProperty[] properties)
{
normalReflectionDistortion = BaseShaderGUI.FindProperty("_NormalReflectionDistortion", properties, false);
reflectionMultiplier = BaseShaderGUI.FindProperty("_ReflectionMultiplier", properties, false);
reflectionPower = BaseShaderGUI.FindProperty("_ReflectionPower", properties, false);
}
}

static readonly string[] workflowModeNames = Enum.GetNames(typeof(LitGUI.WorkflowMode));

private LitGUI.LitProperties litProperties;
private LitDetailGUI.LitProperties litDetailProperties;
private PlanarReflectionLitProperties planarReflectionLitProperties;

public override void FillAdditionalFoldouts(MaterialHeaderScopeList materialScopesList)
{
materialScopesList.RegisterHeaderScope(LitDetailGUI.Styles.detailInputs, Expandable.Details, _ => LitDetailGUI.DoDetailArea(litDetailProperties, materialEditor));
}

// collect properties from the material properties
public override void FindProperties(MaterialProperty[] properties)
{
base.FindProperties(properties);
litProperties = new LitGUI.LitProperties(properties);
litDetailProperties = new LitDetailGUI.LitProperties(properties);
planarReflectionLitProperties = new PlanarReflectionLitProperties(properties);
}

// material changed check
public override void ValidateMaterial(Material material)
{
SetMaterialKeywords(material, LitGUI.SetMaterialKeywords, LitDetailGUI.SetMaterialKeywords);
}

// material main surface options
public override void DrawSurfaceOptions(Material material)
{
// Use default labelWidth
EditorGUIUtility.labelWidth = 0f;

if (litProperties.workflowMode != null)
DoPopup(LitGUI.Styles.workflowModeText, litProperties.workflowMode, workflowModeNames);

base.DrawSurfaceOptions(material);
}

// material main surface inputs
public override void DrawSurfaceInputs(Material material)
{
base.DrawSurfaceInputs(material);
LitGUI.Inputs(litProperties, materialEditor, material);
DrawEmissionProperties(material, true);
DrawTileOffset(materialEditor, baseMapProp);
}

// material main advanced options
public override void DrawAdvancedOptions(Material material)
{
if (litProperties.reflections != null && litProperties.highlights != null)
{
materialEditor.ShaderProperty(litProperties.highlights, LitGUI.Styles.highlightsText);
materialEditor.ShaderProperty(litProperties.reflections, LitGUI.Styles.reflectionsText);
}

if (planarReflectionLitProperties.normalReflectionDistortion != null) {
materialEditor.ShaderProperty(planarReflectionLitProperties.normalReflectionDistortion, Styles.normalReflectionDistortionText);
}

if (planarReflectionLitProperties.reflectionMultiplier != null) {
materialEditor.ShaderProperty(planarReflectionLitProperties.reflectionMultiplier, Styles.reflectionMultiplierText);
}

if(planarReflectionLitProperties.reflectionPower != null) {
materialEditor.ShaderProperty(planarReflectionLitProperties.reflectionPower, Styles.reflectionPowerText);
}


base.DrawAdvancedOptions(material);
}

public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader)
{
if (material == null)
throw new ArgumentNullException("material");

// _Emission property is lost after assigning Standard shader to the material
// thus transfer it before assigning the new shader
if (material.HasProperty("_Emission"))
{
material.SetColor("_EmissionColor", material.GetColor("_Emission"));
}

base.AssignNewShaderToMaterial(material, oldShader, newShader);

if (oldShader == null || !oldShader.name.Contains("Legacy Shaders/"))
{
SetupMaterialBlendMode(material);
return;
}

SurfaceType surfaceType = SurfaceType.Opaque;
BlendMode blendMode = BlendMode.Alpha;
if (oldShader.name.Contains("/Transparent/Cutout/"))
{
surfaceType = SurfaceType.Opaque;
material.SetFloat("_AlphaClip", 1);
}
else if (oldShader.name.Contains("/Transparent/"))
{
// NOTE: legacy shaders did not provide physically based transparency
// therefore Fade mode
surfaceType = SurfaceType.Transparent;
blendMode = BlendMode.Alpha;
}
material.SetFloat("_Blend", (float)blendMode);

material.SetFloat("_Surface", (float)surfaceType);
if (surfaceType == SurfaceType.Opaque)
{
material.DisableKeyword("_SURFACE_TYPE_TRANSPARENT");
}
else
{
material.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
}

if (oldShader.name.Equals("Standard (Specular setup)"))
{
material.SetFloat("_WorkflowMode", (float)LitGUI.WorkflowMode.Specular);
Texture texture = material.GetTexture("_SpecGlossMap");
if (texture != null)
material.SetTexture("_MetallicSpecGlossMap", texture);
}
else
{
material.SetFloat("_WorkflowMode", (float)LitGUI.WorkflowMode.Metallic);
Texture texture = material.GetTexture("_MetallicGlossMap");
if (texture != null)
material.SetTexture("_MetallicSpecGlossMap", texture);
}
}

}
}
3 changes: 3 additions & 0 deletions Editor/ShaderGraph/ShaderGUI/URPPlanarReflectionLit.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ The performance of this technique depends a lot on the complexity of the scene.
* Add an option to select the number of blurred sub targets (this should come with extra info to allow blurring with more or less steps these subtargets).
* Haven't tested the technique in different platforms (mobile, WebGL, consoles,...) so it may need tweaking or changes to work there.

# Shader

## URPPlanarReflectionLit

![URPPlanarReflectionLit.png](Documentation%7E/URPPlanarReflectionLit.png)

# Samples

The sample images here have been taken from *Empire in Decay*. It's a game developed by Siesta Games and should release sometime in Q1 or Q2 2026. If you find this project useful we ask you to take a look at the game:
Expand Down
2 changes: 1 addition & 1 deletion Code/Editor.meta → Runtime.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Code/Blur/BlurHelper.cs → Runtime/BlurHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public static void DualKawaseBlur(RenderTexture orig, ref RenderTexture[] blurre
/// </summary>
public static void LimitColorValue(RenderTexture rt, Material blurMat, float maxValue)
{
RenderTexture tempRT = RenderTexture.GetTemporary(rt.descriptor);
var tempRT = RenderTexture.GetTemporary(rt.descriptor);

blurMat.SetTexture(blitTextureId, rt);
blurMat.SetFloat(maxValueId, maxValue);
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading