John
7 年前
当前提交
e5aff3eb
共有 35 个文件被更改,包括 2626 次插入 和 0 次删除
-
7ScriptableRenderPipeline/LightweightPipeline/LWRP/Data/LightweightPipelineAsset.cs
-
7ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/LightweightAssetEditor.cs
-
35ScriptableRenderPipeline/LightweightPipeline/LWRP/LightweightPipeline.cs
-
8ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile.meta
-
8ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile.meta
-
388ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/ShaderGUI/LightweightSubsurfaceGUI.cs
-
11ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/ShaderGUI/LightweightSubsurfaceGUI.cs.meta
-
8ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface.meta
-
350ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs
-
16ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs.hlsl
-
9ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs.hlsl.meta
-
11ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs.meta
-
31ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.MenuItem.cs
-
11ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.MenuItem.cs.meta
-
64ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.Styles.cs
-
11ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.Styles.cs.meta
-
241ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.cs
-
11ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.cs.meta
-
8ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile.meta
-
79ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/DrawTransmittanceGraph.shader
-
9ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/DrawTransmittanceGraph.shader.meta
-
99ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/PreintegratedScatter.shader
-
9ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/PreintegratedScatter.shader.meta
-
667ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/Lighting.hlsl
-
9ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/Lighting.hlsl.meta
-
170ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightPassLit.hlsl
-
9ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightPassLit.hlsl.meta
-
207ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightSubsurface.shader
-
9ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightSubsurface.shader.meta
-
115ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/SubsurfaceScattering.hlsl
-
9ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/SubsurfaceScattering.hlsl.meta
|
|||
fileFormatVersion: 2 |
|||
guid: 2cff1682e26d69446a8e653056d517bb |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 27f2302b1772a3e40bbb5eab46e29919 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using UnityEngine; |
|||
using UnityEngine.Experimental.Rendering; |
|||
using UnityEngine.Experimental.Rendering.LightweightPipeline; |
|||
|
|||
namespace UnityEditor |
|||
{ |
|||
internal class LightweightSubsurfaceGUI : LightweightShaderGUI |
|||
{ |
|||
public enum WorkflowMode |
|||
{ |
|||
Specular = 0, |
|||
Metallic |
|||
} |
|||
|
|||
public enum SmoothnessMapChannel |
|||
{ |
|||
SpecularMetallicAlpha, |
|||
AlbedoAlpha, |
|||
} |
|||
|
|||
private static class Styles |
|||
{ |
|||
public static GUIContent albedoText = new GUIContent("Albedo", "Albedo (RGB) and Transparency (A)"); |
|||
public static GUIContent alphaCutoffText = new GUIContent("Alpha Cutoff", "Threshold for alpha cutoff"); |
|||
public static GUIContent specularMapText = new GUIContent("Specular", "Specular (RGB) and Smoothness (A)"); |
|||
public static GUIContent metallicMapText = new GUIContent("Metallic", "Metallic (R) and Smoothness (A)"); |
|||
public static GUIContent smoothnessText = new GUIContent("Smoothness", "Smoothness value"); |
|||
public static GUIContent smoothnessScaleText = new GUIContent("Smoothness", "Smoothness scale factor"); |
|||
public static GUIContent smoothnessMapChannelText = new GUIContent("Source", "Smoothness texture and channel"); |
|||
public static GUIContent highlightsText = new GUIContent("Specular Highlights", "Specular Highlights"); |
|||
public static GUIContent reflectionsText = new GUIContent("Reflections", "Glossy Reflections"); |
|||
public static GUIContent normalMapText = new GUIContent("Normal Map", "Normal Map"); |
|||
public static GUIContent occlusionText = new GUIContent("Occlusion", "Occlusion (G)"); |
|||
public static GUIContent emissionText = new GUIContent("Color", "Emission (RGB)"); |
|||
public static GUIContent bumpScaleNotSupported = new GUIContent("Bump scale is not supported on mobile platforms"); |
|||
public static GUIContent fixNow = new GUIContent("Fix now"); |
|||
|
|||
//Subsurface
|
|||
public static GUIContent subsurfaceAreaText = new GUIContent("Subsurface Scattering", ""); |
|||
public static GUIContent diffusionProfileText = new GUIContent("Diffusion profile", "A profile determines the shape of the SSS/transmission filter."); |
|||
|
|||
public static string primaryMapsText = "Main Maps"; |
|||
public static string secondaryMapsText = "Secondary Maps"; |
|||
public static string forwardText = "Forward Rendering Options"; |
|||
public static string workflowModeText = "Workflow Mode"; |
|||
public static string renderingMode = "Rendering Mode"; |
|||
public static string advancedText = "Advanced Options"; |
|||
public static readonly string[] workflowNames = Enum.GetNames(typeof(WorkflowMode)); |
|||
public static readonly string[] blendNames = Enum.GetNames(typeof(BlendMode)); |
|||
public static readonly string[] metallicSmoothnessChannelNames = {"Metallic Alpha", "Albedo Alpha"}; |
|||
public static readonly string[] specularSmoothnessChannelNames = {"Specular Alpha", "Albedo Alpha"}; |
|||
} |
|||
|
|||
private MaterialProperty workflowMode; |
|||
private MaterialProperty blendMode; |
|||
|
|||
private MaterialProperty albedoColor; |
|||
private MaterialProperty albedoMap; |
|||
private MaterialProperty alphaCutoff; |
|||
|
|||
private MaterialProperty smoothness; |
|||
private MaterialProperty smoothnessScale; |
|||
private MaterialProperty smoothnessMapChannel; |
|||
|
|||
private MaterialProperty metallic; |
|||
private MaterialProperty specColor; |
|||
private MaterialProperty metallicGlossMap; |
|||
private MaterialProperty specGlossMap; |
|||
private MaterialProperty highlights; |
|||
private MaterialProperty reflections; |
|||
|
|||
private MaterialProperty bumpScale; |
|||
private MaterialProperty bumpMap; |
|||
private MaterialProperty occlusionStrength; |
|||
private MaterialProperty occlusionMap; |
|||
private MaterialProperty emissionColorForRendering; |
|||
private MaterialProperty emissionMap; |
|||
|
|||
//Subsurface
|
|||
private MaterialProperty diffusionProfileID; |
|||
|
|||
public override void FindProperties(MaterialProperty[] properties) |
|||
{ |
|||
workflowMode = FindProperty("_WorkflowMode", properties); |
|||
blendMode = FindProperty("_Mode", properties); |
|||
albedoColor = FindProperty("_Color", properties); |
|||
albedoMap = FindProperty("_MainTex", properties); |
|||
alphaCutoff = FindProperty("_Cutoff", properties); |
|||
|
|||
smoothness = FindProperty("_Glossiness", properties); |
|||
smoothnessScale = FindProperty("_GlossMapScale", properties, false); |
|||
smoothnessMapChannel = FindProperty("_SmoothnessTextureChannel", properties, false); |
|||
|
|||
metallic = FindProperty("_Metallic", properties); |
|||
specColor = FindProperty("_SpecColor", properties); |
|||
metallicGlossMap = FindProperty("_MetallicGlossMap", properties); |
|||
specGlossMap = FindProperty("_SpecGlossMap", properties); |
|||
highlights = FindProperty("_SpecularHighlights", properties); |
|||
reflections = FindProperty("_GlossyReflections", properties); |
|||
|
|||
bumpScale = FindProperty("_BumpScale", properties); |
|||
bumpMap = FindProperty("_BumpMap", properties); |
|||
occlusionStrength = FindProperty("_OcclusionStrength", properties); |
|||
occlusionMap = FindProperty("_OcclusionMap", properties); |
|||
emissionColorForRendering = FindProperty("_EmissionColor", properties); |
|||
emissionMap = FindProperty("_EmissionMap", properties); |
|||
|
|||
//Subsurface
|
|||
diffusionProfileID = FindProperty("_DiffusionProfile", properties); |
|||
} |
|||
|
|||
public override void MaterialChanged(Material material) |
|||
{ |
|||
material.shaderKeywords = null; |
|||
SetupMaterialBlendMode(material); |
|||
SetMaterialKeywords(material); |
|||
} |
|||
|
|||
public override void ShaderPropertiesGUI(Material material) |
|||
{ |
|||
// Use default labelWidth
|
|||
EditorGUIUtility.labelWidth = 0f; |
|||
|
|||
// Detect any changes to the material
|
|||
EditorGUI.BeginChangeCheck(); |
|||
{ |
|||
DoPopup(Styles.workflowModeText, workflowMode, Styles.workflowNames); |
|||
DoPopup(Styles.renderingMode, blendMode, Styles.blendNames); |
|||
|
|||
// Primary properties
|
|||
GUILayout.Label(Styles.primaryMapsText, EditorStyles.boldLabel); |
|||
DoAlbedoArea(material); |
|||
DoMetallicSpecularArea(); |
|||
DoNormalArea(); |
|||
|
|||
m_MaterialEditor.TexturePropertySingleLine(Styles.occlusionText, occlusionMap, occlusionMap.textureValue != null ? occlusionStrength : null); |
|||
|
|||
DoEmissionArea(material); |
|||
EditorGUI.BeginChangeCheck(); |
|||
m_MaterialEditor.TextureScaleOffsetProperty(albedoMap); |
|||
if (EditorGUI.EndChangeCheck()) |
|||
emissionMap.textureScaleAndOffset = albedoMap.textureScaleAndOffset; // Apply the main texture scale and offset to the emission texture as well, for Enlighten's sake
|
|||
|
|||
EditorGUILayout.Space(); |
|||
|
|||
m_MaterialEditor.ShaderProperty(highlights, Styles.highlightsText); |
|||
m_MaterialEditor.ShaderProperty(reflections, Styles.reflectionsText); |
|||
|
|||
EditorGUILayout.Space(); |
|||
DoSubsurfaceArea(); |
|||
} |
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
foreach (var obj in blendMode.targets) |
|||
MaterialChanged((Material)obj); |
|||
} |
|||
|
|||
EditorGUILayout.Space(); |
|||
|
|||
// NB renderqueue editor is not shown on purpose: we want to override it based on blend mode
|
|||
GUILayout.Label(Styles.advancedText, EditorStyles.boldLabel); |
|||
m_MaterialEditor.EnableInstancingField(); |
|||
m_MaterialEditor.DoubleSidedGIField(); |
|||
} |
|||
|
|||
public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader) |
|||
{ |
|||
// _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; |
|||
} |
|||
|
|||
BlendMode blendMode = BlendMode.Opaque; |
|||
if (oldShader.name.Contains("/Transparent/Cutout/")) |
|||
{ |
|||
blendMode = BlendMode.Cutout; |
|||
} |
|||
else if (oldShader.name.Contains("/Transparent/")) |
|||
{ |
|||
// NOTE: legacy shaders did not provide physically based transparency
|
|||
// therefore Fade mode
|
|||
blendMode = BlendMode.Fade; |
|||
} |
|||
material.SetFloat("_Mode", (float)blendMode); |
|||
|
|||
if (oldShader.name.Equals("Standard (Specular setup)")) |
|||
{ |
|||
material.SetFloat("_WorkflowMode", (float)WorkflowMode.Specular); |
|||
Texture texture = material.GetTexture("_SpecGlossMap"); |
|||
if (texture != null) |
|||
material.SetTexture("_MetallicSpecGlossMap", texture); |
|||
} |
|||
else |
|||
{ |
|||
material.SetFloat("_WorkflowMode", (float)WorkflowMode.Metallic); |
|||
Texture texture = material.GetTexture("_MetallicGlossMap"); |
|||
if (texture != null) |
|||
material.SetTexture("_MetallicSpecGlossMap", texture); |
|||
} |
|||
|
|||
MaterialChanged(material); |
|||
} |
|||
|
|||
void DoAlbedoArea(Material material) |
|||
{ |
|||
m_MaterialEditor.TexturePropertySingleLine(Styles.albedoText, albedoMap, albedoColor); |
|||
if (((BlendMode)material.GetFloat("_Mode") == BlendMode.Cutout)) |
|||
{ |
|||
m_MaterialEditor.ShaderProperty(alphaCutoff, Styles.alphaCutoffText.text, MaterialEditor.kMiniTextureFieldLabelIndentLevel + 1); |
|||
} |
|||
} |
|||
|
|||
void DoNormalArea() |
|||
{ |
|||
m_MaterialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap, bumpMap.textureValue != null ? bumpScale : null); |
|||
if (bumpScale.floatValue != 1 && UnityEditorInternal.InternalEditorUtility.IsMobilePlatform(EditorUserBuildSettings.activeBuildTarget)) |
|||
if (m_MaterialEditor.HelpBoxWithButton(Styles.bumpScaleNotSupported, Styles.fixNow)) |
|||
bumpScale.floatValue = 1; |
|||
} |
|||
|
|||
void DoEmissionArea(Material material) |
|||
{ |
|||
// Emission for GI?
|
|||
if (m_MaterialEditor.EmissionEnabledProperty()) |
|||
{ |
|||
bool hadEmissionTexture = emissionMap.textureValue != null; |
|||
|
|||
// Texture and HDR color controls
|
|||
m_MaterialEditor.TexturePropertyWithHDRColor(Styles.emissionText, emissionMap, emissionColorForRendering, false); |
|||
|
|||
// If texture was assigned and color was black set color to white
|
|||
float brightness = emissionColorForRendering.colorValue.maxColorComponent; |
|||
if (emissionMap.textureValue != null && !hadEmissionTexture && brightness <= 0f) |
|||
emissionColorForRendering.colorValue = Color.white; |
|||
|
|||
// LW does not support RealtimeEmissive. We set it to bake emissive and handle the emissive is black right.
|
|||
material.globalIlluminationFlags = MaterialGlobalIlluminationFlags.BakedEmissive; |
|||
if (brightness <= 0f) |
|||
material.globalIlluminationFlags |= MaterialGlobalIlluminationFlags.EmissiveIsBlack; |
|||
} |
|||
} |
|||
|
|||
void DoMetallicSpecularArea() |
|||
{ |
|||
string[] metallicSpecSmoothnessChannelName; |
|||
bool hasGlossMap = false; |
|||
if ((WorkflowMode)workflowMode.floatValue == WorkflowMode.Metallic) |
|||
{ |
|||
hasGlossMap = metallicGlossMap.textureValue != null; |
|||
metallicSpecSmoothnessChannelName = Styles.metallicSmoothnessChannelNames; |
|||
m_MaterialEditor.TexturePropertySingleLine(Styles.metallicMapText, metallicGlossMap, |
|||
hasGlossMap ? null : metallic); |
|||
} |
|||
else |
|||
{ |
|||
hasGlossMap = specGlossMap.textureValue != null; |
|||
metallicSpecSmoothnessChannelName = Styles.specularSmoothnessChannelNames; |
|||
m_MaterialEditor.TexturePropertySingleLine(Styles.specularMapText, specGlossMap, |
|||
hasGlossMap ? null : specColor); |
|||
} |
|||
|
|||
bool showSmoothnessScale = hasGlossMap; |
|||
if (smoothnessMapChannel != null) |
|||
{ |
|||
int smoothnessChannel = (int)smoothnessMapChannel.floatValue; |
|||
if (smoothnessChannel == (int)SmoothnessMapChannel.AlbedoAlpha) |
|||
showSmoothnessScale = true; |
|||
} |
|||
|
|||
int indentation = 2; // align with labels of texture properties
|
|||
m_MaterialEditor.ShaderProperty(showSmoothnessScale ? smoothnessScale : smoothness, showSmoothnessScale ? Styles.smoothnessScaleText : Styles.smoothnessText, indentation); |
|||
|
|||
int prevIndentLevel = EditorGUI.indentLevel; |
|||
EditorGUI.indentLevel = 3; |
|||
if (smoothnessMapChannel != null) |
|||
DoPopup(Styles.smoothnessMapChannelText.text, smoothnessMapChannel, metallicSpecSmoothnessChannelName); |
|||
EditorGUI.indentLevel = prevIndentLevel; |
|||
} |
|||
|
|||
void DoSubsurfaceArea() |
|||
{ |
|||
GUILayout.Label(Styles.subsurfaceAreaText, EditorStyles.boldLabel); |
|||
|
|||
var lwPipeine = RenderPipelineManager.currentPipeline as LightweightPipeline; |
|||
var diffusionProfileSettings = lwPipeine.DiffusionProfileSettings; |
|||
|
|||
if(lwPipeine.IsInternalDiffusionProfile(diffusionProfileSettings)) |
|||
{ |
|||
EditorGUILayout.HelpBox("No diffusion profile Settings have been assigned to the render pipeline asset.", MessageType.Warning); |
|||
return; |
|||
} |
|||
|
|||
// TODO: Optimize me
|
|||
var profiles = diffusionProfileSettings.profiles; |
|||
var names = new GUIContent[profiles.Length + 1]; |
|||
names[0] = new GUIContent("None"); |
|||
|
|||
var values = new int[names.Length]; |
|||
values[0] = DiffusionProfileConstants.DIFFUSION_PROFILE_NEUTRAL_ID; |
|||
|
|||
for (int i = 0; i < profiles.Length; i++) |
|||
{ |
|||
names[i + 1] = new GUIContent(profiles[i].name); |
|||
values[i + 1] = i + 1; |
|||
} |
|||
|
|||
using (var scope = new EditorGUI.ChangeCheckScope()) |
|||
{ |
|||
int profileID = (int)diffusionProfileID.floatValue; |
|||
|
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
EditorGUILayout.PrefixLabel(Styles.diffusionProfileText); |
|||
|
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
profileID = EditorGUILayout.IntPopup(profileID, names, values); |
|||
|
|||
if (GUILayout.Button("Goto", EditorStyles.miniButton, GUILayout.Width(50f))) |
|||
Selection.activeObject = diffusionProfileSettings; |
|||
} |
|||
} |
|||
|
|||
if (scope.changed) |
|||
diffusionProfileID.floatValue = profileID; |
|||
} |
|||
} |
|||
|
|||
static SmoothnessMapChannel GetSmoothnessMapChannel(Material material) |
|||
{ |
|||
int ch = (int)material.GetFloat("_SmoothnessTextureChannel"); |
|||
if (ch == (int)SmoothnessMapChannel.AlbedoAlpha) |
|||
return SmoothnessMapChannel.AlbedoAlpha; |
|||
|
|||
return SmoothnessMapChannel.SpecularMetallicAlpha; |
|||
} |
|||
|
|||
static void SetMaterialKeywords(Material material) |
|||
{ |
|||
// Note: keywords must be based on Material value not on MaterialProperty due to multi-edit & material animation
|
|||
// (MaterialProperty value might come from renderer material property block)
|
|||
bool isSpecularWorkFlow = (WorkflowMode)material.GetFloat("_WorkflowMode") == WorkflowMode.Specular; |
|||
bool hasGlossMap = false; |
|||
if (isSpecularWorkFlow) |
|||
hasGlossMap = material.GetTexture("_SpecGlossMap"); |
|||
else |
|||
hasGlossMap = material.GetTexture("_MetallicGlossMap"); |
|||
|
|||
CoreUtils.SetKeyword(material, "_SPECULAR_SETUP", isSpecularWorkFlow); |
|||
|
|||
CoreUtils.SetKeyword(material, "_METALLICSPECGLOSSMAP", hasGlossMap); |
|||
CoreUtils.SetKeyword(material, "_SPECGLOSSMAP", hasGlossMap && isSpecularWorkFlow); |
|||
CoreUtils.SetKeyword(material, "_METALLICGLOSSMAP", hasGlossMap && !isSpecularWorkFlow); |
|||
|
|||
CoreUtils.SetKeyword(material, "_NORMALMAP", material.GetTexture("_BumpMap")); |
|||
|
|||
CoreUtils.SetKeyword(material, "_SPECULARHIGHLIGHTS_OFF", material.GetFloat("_SpecularHighlights") == 0.0f); |
|||
CoreUtils.SetKeyword(material, "_GLOSSYREFLECTIONS_OFF", material.GetFloat("_GlossyReflections") == 0.0f); |
|||
|
|||
CoreUtils.SetKeyword(material, "_OCCLUSIONMAP", material.GetTexture("_OcclusionMap")); |
|||
CoreUtils.SetKeyword(material, "_PARALLAXMAP", material.GetTexture("_ParallaxMap")); |
|||
CoreUtils.SetKeyword(material, "_DETAIL_MULX2", material.GetTexture("_DetailAlbedoMap") || material.GetTexture("_DetailNormalMap")); |
|||
|
|||
// A material's GI flag internally keeps track of whether emission is enabled at all, it's enabled but has no effect
|
|||
// or is enabled and may be modified at runtime. This state depends on the values of the current flag and emissive color.
|
|||
// The fixup routine makes sure that the material is in the correct state if/when changes are made to the mode or color.
|
|||
MaterialEditor.FixupEmissiveFlag(material); |
|||
bool shouldEmissionBeEnabled = (material.globalIlluminationFlags & MaterialGlobalIlluminationFlags.EmissiveIsBlack) == 0; |
|||
CoreUtils.SetKeyword(material, "_EMISSION", shouldEmissionBeEnabled); |
|||
|
|||
if (material.HasProperty("_SmoothnessTextureChannel")) |
|||
{ |
|||
CoreUtils.SetKeyword(material, "_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A", GetSmoothnessMapChannel(material) == SmoothnessMapChannel.AlbedoAlpha); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 5dffac7b065436640b6936d57dfedd75 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 87e0edbcda41b5745a77ba54a215b20b |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
|
|||
using UnityEngine.Rendering; |
|||
|
|||
namespace UnityEngine.Experimental.Rendering.LightweightPipeline |
|||
{ |
|||
|
|||
//A minimal implementation of HDRP Diffusion Profile, to support out preintegrated subsurface scattering model.
|
|||
|
|||
[GenerateHLSL] |
|||
public class DiffusionProfileConstants |
|||
{ |
|||
public const int DIFFUSION_PROFILE_COUNT = 5; |
|||
public const int DIFFUSION_PROFILE_NEUTRAL_ID = 0; |
|||
public const int SSS_N_SAMPLES = 11; |
|||
public const int SSS_DISTANCE_SCALE = 3; |
|||
} |
|||
|
|||
public static class DiffusionProfileShaderIDs |
|||
{ |
|||
public static readonly int _StdDev1 = Shader.PropertyToID("_StdDev1"); |
|||
public static readonly int _StdDev2 = Shader.PropertyToID("_StdDev2"); |
|||
public static readonly int _LerpWeight = Shader.PropertyToID("_LerpWeight"); |
|||
public static readonly int _HalfRcpVarianceAndWeight1 = Shader.PropertyToID("_HalfRcpVarianceAndWeight1"); |
|||
public static readonly int _HalfRcpVarianceAndWeight2 = Shader.PropertyToID("_HalfRcpVarianceAndWeight2"); |
|||
public static readonly int _TransmissionTint = Shader.PropertyToID("_TransmissionTint"); |
|||
public static readonly int _ThicknessRemap = Shader.PropertyToID("_ThicknessRemap"); |
|||
} |
|||
|
|||
[Serializable] |
|||
public sealed class DiffusionProfile |
|||
{ |
|||
public string name; |
|||
|
|||
[ColorUsage(false, true)] public Color scatterDistance1; |
|||
[ColorUsage(false, true)] public Color scatterDistance2; |
|||
[Range(0f, 1f)] public float lerpWeight; |
|||
[ColorUsage(false)] public Color transmissionTint; |
|||
public Vector2 thicknessRemap; |
|||
public float worldScale; |
|||
|
|||
public Vector4[] filterKernel { get; private set; } |
|||
public Vector4 halfRcpWeightedVariances { get; private set; } |
|||
|
|||
public DiffusionProfile(string name) |
|||
{ |
|||
this.name = name; |
|||
|
|||
scatterDistance1 = new Color(0.3f, 0.3f, 0.3f, 0f); |
|||
scatterDistance2 = new Color(0.5f, 0.5f, 0.5f, 0f); |
|||
lerpWeight = 1f; |
|||
|
|||
transmissionTint = Color.white; |
|||
thicknessRemap = new Vector2(0f, 5f); |
|||
worldScale = 1f; |
|||
|
|||
} |
|||
|
|||
public void Validate() |
|||
{ |
|||
thicknessRemap.y = Mathf.Max(thicknessRemap.y, 0f); |
|||
thicknessRemap.x = Mathf.Clamp(thicknessRemap.x, 0f, thicknessRemap.y); |
|||
worldScale = Mathf.Max(worldScale, 0.001f); |
|||
|
|||
scatterDistance1 = new Color |
|||
{ |
|||
r = Mathf.Max(0.05f, scatterDistance1.r), |
|||
g = Mathf.Max(0.05f, scatterDistance1.g), |
|||
b = Mathf.Max(0.05f, scatterDistance1.b), |
|||
a = 0.0f |
|||
}; |
|||
|
|||
scatterDistance2 = new Color |
|||
{ |
|||
r = Mathf.Max(0.05f, scatterDistance2.r), |
|||
g = Mathf.Max(0.05f, scatterDistance2.g), |
|||
b = Mathf.Max(0.05f, scatterDistance2.b), |
|||
a = 0.0f |
|||
}; |
|||
|
|||
UpdateKernelAndVarianceData(); |
|||
} |
|||
|
|||
public void UpdateKernelAndVarianceData() |
|||
{ |
|||
const int kNumSamples = DiffusionProfileConstants.SSS_N_SAMPLES; |
|||
const int kDistanceScale = DiffusionProfileConstants.SSS_DISTANCE_SCALE; |
|||
|
|||
if (filterKernel == null || filterKernel.Length != kNumSamples) |
|||
filterKernel = new Vector4[kNumSamples]; |
|||
|
|||
// Apply the three-sigma rule, and rescale.
|
|||
var stdDev1 = ((1f / 3f) * kDistanceScale) * scatterDistance1; |
|||
var stdDev2 = ((1f / 3f) * kDistanceScale) * scatterDistance2; |
|||
|
|||
// Our goal is to blur the image using a filter which is represented
|
|||
// as a product of a linear combination of two normalized 1D Gaussians
|
|||
// as suggested by Jimenez et al. in "Separable Subsurface Scattering".
|
|||
// A normalized (i.e. energy-preserving) 1D Gaussian with the mean of 0
|
|||
// is defined as follows: G1(x, v) = exp(-x * x / (2 * v)) / sqrt(2 * Pi * v),
|
|||
// where 'v' is variance and 'x' is the radial distance from the origin.
|
|||
// Using the weight 'w', our 1D and the resulting 2D filters are given as:
|
|||
// A1(v1, v2, w, x) = G1(x, v1) * (1 - w) + G1(r, v2) * w,
|
|||
// A2(v1, v2, w, x, y) = A1(v1, v2, w, x) * A1(v1, v2, w, y).
|
|||
// The resulting filter function is a non-Gaussian PDF.
|
|||
// It is separable by design, but generally not radially symmetric.
|
|||
|
|||
// N.b.: our scattering distance is rather limited. Therefore, in order to allow
|
|||
// for a greater range of standard deviation values for flatter profiles,
|
|||
// we rescale the world using 'distanceScale', effectively reducing the SSS
|
|||
// distance units from centimeters to (1 / distanceScale).
|
|||
|
|||
// Find the widest Gaussian across 3 color channels.
|
|||
float maxStdDev1 = Mathf.Max(stdDev1.r, stdDev1.g, stdDev1.b); |
|||
float maxStdDev2 = Mathf.Max(stdDev2.r, stdDev2.g, stdDev2.b); |
|||
|
|||
var weightSum = Vector3.zero; |
|||
|
|||
float step = 1f / (kNumSamples - 1); |
|||
|
|||
// Importance sample the linear combination of two Gaussians.
|
|||
for (int i = 0; i < kNumSamples; i++) |
|||
{ |
|||
// Generate 'u' on (0, 0.5] and (0.5, 1).
|
|||
float u = (i <= kNumSamples / 2) ? 0.5f - i * step // The center and to the left
|
|||
: i * step; // From the center to the right
|
|||
|
|||
u = Mathf.Clamp(u, 0.001f, 0.999f); |
|||
|
|||
float pos = GaussianCombinationCdfInverse(u, maxStdDev1, maxStdDev2, lerpWeight); |
|||
float pdf = GaussianCombination(pos, maxStdDev1, maxStdDev2, lerpWeight); |
|||
|
|||
Vector3 val; |
|||
val.x = GaussianCombination(pos, stdDev1.r, stdDev2.r, lerpWeight); |
|||
val.y = GaussianCombination(pos, stdDev1.g, stdDev2.g, lerpWeight); |
|||
val.z = GaussianCombination(pos, stdDev1.b, stdDev2.b, lerpWeight); |
|||
|
|||
// We do not divide by 'numSamples' since we will renormalize, anyway.
|
|||
filterKernel[i].x = val.x * (1 / pdf); |
|||
filterKernel[i].y = val.y * (1 / pdf); |
|||
filterKernel[i].z = val.z * (1 / pdf); |
|||
filterKernel[i].w = pos; |
|||
|
|||
weightSum.x += filterKernel[i].x; |
|||
weightSum.y += filterKernel[i].y; |
|||
weightSum.z += filterKernel[i].z; |
|||
} |
|||
|
|||
// Renormalize the weights to conserve energy.
|
|||
for (int i = 0; i < kNumSamples; i++) |
|||
{ |
|||
filterKernel[i].x *= 1 / weightSum.x; |
|||
filterKernel[i].y *= 1 / weightSum.y; |
|||
filterKernel[i].z *= 1 / weightSum.z; |
|||
} |
|||
|
|||
Vector4 weightedStdDev; |
|||
weightedStdDev.x = Mathf.Lerp(stdDev1.r, stdDev2.r, lerpWeight); |
|||
weightedStdDev.y = Mathf.Lerp(stdDev1.g, stdDev2.g, lerpWeight); |
|||
weightedStdDev.z = Mathf.Lerp(stdDev1.b, stdDev2.b, lerpWeight); |
|||
weightedStdDev.w = Mathf.Lerp(maxStdDev1, maxStdDev2, lerpWeight); |
|||
|
|||
// Store (1 / (2 * WeightedVariance)) per color channel.
|
|||
// Warning: do not use halfRcpWeightedVariances.Set(). It will not work.
|
|||
halfRcpWeightedVariances = new Vector4(0.5f / (weightedStdDev.x * weightedStdDev.x), |
|||
0.5f / (weightedStdDev.y * weightedStdDev.y), |
|||
0.5f / (weightedStdDev.z * weightedStdDev.z), |
|||
0.5f / (weightedStdDev.w * weightedStdDev.w)); |
|||
} |
|||
|
|||
static float Gaussian(float x, float stdDev) |
|||
{ |
|||
float variance = stdDev * stdDev; |
|||
return Mathf.Exp(-x * x / (2 * variance)) / Mathf.Sqrt(2 * Mathf.PI * variance); |
|||
} |
|||
|
|||
static float GaussianCombination(float x, float stdDev1, float stdDev2, float lerpWeight) |
|||
{ |
|||
return Mathf.Lerp(Gaussian(x, stdDev1), Gaussian(x, stdDev2), lerpWeight); |
|||
} |
|||
|
|||
static float RationalApproximation(float t) |
|||
{ |
|||
// Abramowitz and Stegun formula 26.2.23.
|
|||
// The absolute value of the error should be less than 4.5 e-4.
|
|||
float[] c = { 2.515517f, 0.802853f, 0.010328f }; |
|||
float[] d = { 1.432788f, 0.189269f, 0.001308f }; |
|||
return t - ((c[2] * t + c[1]) * t + c[0]) / (((d[2] * t + d[1]) * t + d[0]) * t + 1.0f); |
|||
} |
|||
|
|||
// Ref: https://www.johndcook.com/blog/csharp_phi_inverse/
|
|||
static float NormalCdfInverse(float p, float stdDev) |
|||
{ |
|||
float x; |
|||
|
|||
if (p < 0.5) |
|||
{ |
|||
// F^-1(p) = - G^-1(p)
|
|||
x = -RationalApproximation(Mathf.Sqrt(-2f * Mathf.Log(p))); |
|||
} |
|||
else |
|||
{ |
|||
// F^-1(p) = G^-1(1-p)
|
|||
x = RationalApproximation(Mathf.Sqrt(-2f * Mathf.Log(1f - p))); |
|||
} |
|||
|
|||
return x * stdDev; |
|||
} |
|||
|
|||
static float GaussianCombinationCdfInverse(float p, float stdDev1, float stdDev2, float lerpWeight) |
|||
{ |
|||
return Mathf.Lerp(NormalCdfInverse(p, stdDev1), NormalCdfInverse(p, stdDev2), lerpWeight); |
|||
} |
|||
} |
|||
|
|||
public sealed class DiffusionProfileSettings : ScriptableObject |
|||
{ |
|||
public DiffusionProfile[] profiles; |
|||
|
|||
[NonSerialized] public Vector4[] worldScales; |
|||
[NonSerialized] public Vector4[] thicknessRemaps; |
|||
[NonSerialized] public Vector4[] transmissionTints; |
|||
|
|||
//TODO: Preintegration turns this into a texture
|
|||
[NonSerialized] public Vector4[] halfRcpWeightedVariances; |
|||
[NonSerialized] public Vector4[] halfRcpVariancesAndWeights; |
|||
[NonSerialized] public Vector4[] filterKernels; |
|||
|
|||
private Material preintegration; |
|||
[NonSerialized] public Texture2DArray preintegratedScatterLUTs; |
|||
|
|||
public DiffusionProfile this[int index] |
|||
{ |
|||
get |
|||
{ |
|||
if (index >= DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT - 1) |
|||
throw new IndexOutOfRangeException("index"); |
|||
|
|||
return profiles[index]; |
|||
} |
|||
} |
|||
|
|||
static void ValidateArray<T>(ref T[] array, int len) |
|||
{ |
|||
if (array == null || array.Length != len) |
|||
array = new T[len]; |
|||
} |
|||
|
|||
void OnEnable() |
|||
{ |
|||
// The neutral profile is not a part of the array.
|
|||
int profileArraySize = DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT - 1; |
|||
|
|||
if (profiles != null && profiles.Length != profileArraySize) |
|||
Array.Resize(ref profiles, profileArraySize); |
|||
|
|||
if (profiles == null) |
|||
profiles = new DiffusionProfile[profileArraySize]; |
|||
|
|||
for (int i = 0; i < profileArraySize; i++) |
|||
{ |
|||
if (profiles[i] == null) |
|||
profiles[i] = new DiffusionProfile("Profile " + (i + 1)); |
|||
|
|||
profiles[i].Validate(); |
|||
} |
|||
|
|||
ValidateArray(ref thicknessRemaps, DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT); |
|||
ValidateArray(ref worldScales, DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT); |
|||
ValidateArray(ref transmissionTints, DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT); |
|||
ValidateArray(ref halfRcpWeightedVariances, DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT); |
|||
ValidateArray(ref halfRcpVariancesAndWeights, DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT * 2); |
|||
ValidateArray(ref filterKernels, DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT * DiffusionProfileConstants.SSS_N_SAMPLES); |
|||
|
|||
Debug.Assert(DiffusionProfileConstants.DIFFUSION_PROFILE_NEUTRAL_ID <= 32, "Transmission and Texture flags (32-bit integer) cannot support more than 32 profiles."); |
|||
|
|||
//TLS: Will this work in build? May need to do extra work to be found in resources. If broken, SSS won't work in build.
|
|||
preintegration = CoreUtils.CreateEngineMaterial("Hidden/LightweightPipeline/PreintegratedScatter"); |
|||
preintegratedScatterLUTs = new Texture2DArray(128, 128, DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT, TextureFormat.ARGB32, false, true) |
|||
{ |
|||
hideFlags = HideFlags.HideAndDontSave, |
|||
wrapMode = TextureWrapMode.Clamp, |
|||
filterMode = FilterMode.Point |
|||
}; |
|||
|
|||
UpdateCache(); |
|||
} |
|||
|
|||
public void UpdateCache() |
|||
{ |
|||
for (int i = 0; i < DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT - 1; i++) |
|||
{ |
|||
UpdateCache(i); |
|||
} |
|||
|
|||
// Fill the neutral profile.
|
|||
int neutralId = DiffusionProfileConstants.DIFFUSION_PROFILE_NEUTRAL_ID; |
|||
worldScales[neutralId] = Vector4.one; |
|||
halfRcpWeightedVariances[neutralId] = Vector4.one; |
|||
for (int j = 0, n = DiffusionProfileConstants.SSS_N_SAMPLES; j < n; j++) |
|||
{ |
|||
filterKernels[n * neutralId + j] = Vector4.one; |
|||
filterKernels[n * neutralId + j].w = 0f; |
|||
} |
|||
|
|||
//TODO: Remember to apply
|
|||
//Graphics.CopyTexture(Texture2D.blackTexture, 0, preintegratedScatterLUTs, neutralId);
|
|||
preintegratedScatterLUTs.Apply(); |
|||
} |
|||
|
|||
public void UpdateCache(int p) |
|||
{ |
|||
// 'p' is the profile array index. 'i' is the index in the shader (accounting for the neutral profile).
|
|||
int i = p + 1; |
|||
|
|||
// Erase previous value (This need to be done here individually as in the SSS editor we edit individual component)
|
|||
thicknessRemaps[i] = new Vector4(profiles[p].thicknessRemap.x, profiles[p].thicknessRemap.y - profiles[p].thicknessRemap.x, 0f, 0f); |
|||
worldScales[i] = new Vector4(profiles[p].worldScale, 1.0f / profiles[p].worldScale, 0f, 0f); |
|||
// Convert ior to fresnel0
|
|||
transmissionTints[i] = new Vector4(profiles[p].transmissionTint.r * 0.25f, profiles[p].transmissionTint.g * 0.25f, profiles[p].transmissionTint.b * 0.25f, 0f); // Premultiplied
|
|||
//disabledTransmissionTintsAndFresnel0[i] = new Vector4(0.0f, 0.0f, 0.0f, fresnel0);
|
|||
|
|||
halfRcpWeightedVariances[i] = profiles[p].halfRcpWeightedVariances; |
|||
|
|||
var stdDev1 = ((1f / 3f) * DiffusionProfileConstants.SSS_DISTANCE_SCALE) * (Vector4)profiles[p].scatterDistance1; |
|||
var stdDev2 = ((1f / 3f) * DiffusionProfileConstants.SSS_DISTANCE_SCALE) * (Vector4)profiles[p].scatterDistance2; |
|||
|
|||
// Multiply by 0.1 to convert from millimeters to centimeters. Apply the distance scale.
|
|||
// Rescale by 4 to counter rescaling of transmission tints.
|
|||
float a = 0.1f * DiffusionProfileConstants.SSS_DISTANCE_SCALE; |
|||
halfRcpVariancesAndWeights[2 * i + 0] = new Vector4(a * a * 0.5f / (stdDev1.x * stdDev1.x), a * a * 0.5f / (stdDev1.y * stdDev1.y), a * a * 0.5f / (stdDev1.z * stdDev1.z), 4f * (1f - profiles[p].lerpWeight)); |
|||
halfRcpVariancesAndWeights[2 * i + 1] = new Vector4(a * a * 0.5f / (stdDev2.x * stdDev2.x), a * a * 0.5f / (stdDev2.y * stdDev2.y), a * a * 0.5f / (stdDev2.z * stdDev2.z), 4f * profiles[p].lerpWeight); |
|||
|
|||
for (int j = 0, n = DiffusionProfileConstants.SSS_N_SAMPLES; j < n; j++) |
|||
{ |
|||
filterKernels[n * i + j] = profiles[p].filterKernel[j]; |
|||
} |
|||
|
|||
//Pribobly keep this too?
|
|||
RTHandle preintegratedScatterRT = RTHandle.Alloc(128, 128, 1, DepthBits.None, RenderTextureFormat.ARGB32, FilterMode.Point, TextureWrapMode.Clamp, TextureDimension.Tex2D, false); |
|||
|
|||
preintegration.SetVector(DiffusionProfileShaderIDs._StdDev1, stdDev1); |
|||
preintegration.SetVector(DiffusionProfileShaderIDs._StdDev2, stdDev2); |
|||
preintegration.SetFloat (DiffusionProfileShaderIDs._LerpWeight, profiles[p].lerpWeight); |
|||
|
|||
Graphics.Blit(null, preintegratedScatterRT, preintegration); |
|||
Graphics.CopyTexture(preintegratedScatterRT, 0, preintegratedScatterLUTs, i); |
|||
} |
|||
} |
|||
} |
|
|||
// |
|||
// This file was automatically generated. Please don't edit by hand. |
|||
// |
|||
|
|||
#ifndef DIFFUSIONPROFILESETTINGS_CS_HLSL |
|||
#define DIFFUSIONPROFILESETTINGS_CS_HLSL |
|||
// |
|||
// UnityEngine.Experimental.Rendering.LightweightPipeline.DiffusionProfileConstants: static fields |
|||
// |
|||
#define DIFFUSION_PROFILE_COUNT (4) |
|||
#define DIFFUSION_PROFILE_NEUTRAL_ID (0) |
|||
#define SSS_N_SAMPLES (11) |
|||
#define SSS_DISTANCE_SCALE (3) |
|||
|
|||
|
|||
#endif |
|
|||
fileFormatVersion: 2 |
|||
guid: 353718a28c425b44587b4499bd1c21f9 |
|||
ShaderImporter: |
|||
externalObjects: {} |
|||
defaultTextures: [] |
|||
nonModifiableTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 28b82677dd641e24d8383f91eaff8df5 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.IO; |
|||
using UnityEngine; |
|||
using UnityEngine.Experimental.Rendering; |
|||
using UnityEngine.Experimental.Rendering.LightweightPipeline; |
|||
|
|||
namespace UnityEditor.Experimental.Rendering.LightweightPipeline |
|||
{ |
|||
public sealed partial class DiffusionProfileSettingsEditor |
|||
{ |
|||
class DoCreateNewAsset<TAssetType> : ProjectWindowCallback.EndNameEditAction where TAssetType : ScriptableObject |
|||
{ |
|||
public override void Action(int instanceId, string pathName, string resourceFile) |
|||
{ |
|||
var newAsset = CreateInstance<TAssetType>(); |
|||
newAsset.name = Path.GetFileName(pathName); |
|||
AssetDatabase.CreateAsset(newAsset, pathName); |
|||
ProjectWindowUtil.ShowCreatedAsset(newAsset); |
|||
} |
|||
} |
|||
|
|||
class DoCreateNewAssetDiffusionProfileSettings : DoCreateNewAsset<DiffusionProfileSettings> {} |
|||
|
|||
[MenuItem("Assets/Create/Custom/Diffusion Profile Settings (LW)", priority = CoreUtils.assetCreateMenuPriority2)] |
|||
static void MenuCreateDiffusionProfile() |
|||
{ |
|||
var icon = EditorGUIUtility.FindTexture("ScriptableObject Icon"); |
|||
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, ScriptableObject.CreateInstance<DoCreateNewAssetDiffusionProfileSettings>(), "New Diffusion Profile Settings.asset", icon, null); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 7368191fdab83514aaada1bce4a1b2ba |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.Rendering.LightweightPipeline |
|||
{ |
|||
public sealed partial class DiffusionProfileSettingsEditor |
|||
{ |
|||
sealed class Styles |
|||
{ |
|||
public readonly GUIContent profilePreview0 = new GUIContent("Profile Preview"); |
|||
public readonly GUIContent profilePreview1 = new GUIContent("Shows the fraction of light scattered from the source (center)."); |
|||
public readonly GUIContent profilePreview2 = new GUIContent("The distance to the boundary of the image corresponds to the Max Radius."); |
|||
public readonly GUIContent profilePreview3 = new GUIContent("Note that the intensity of pixels around the center may be clipped."); |
|||
public readonly GUIContent transmittancePreview0 = new GUIContent("Transmittance Preview"); |
|||
public readonly GUIContent transmittancePreview1 = new GUIContent("Shows the fraction of light passing through the object for thickness values from the remap."); |
|||
public readonly GUIContent transmittancePreview2 = new GUIContent("Can be viewed as a cross section of a slab of material illuminated by white light from the left."); |
|||
public readonly GUIContent profileScatteringDistance = new GUIContent("Scattering Distance", "Determines the shape of the profile, and the blur radius of the filter per color channel. Alpha is ignored."); |
|||
public readonly GUIContent profileTransmissionTint = new GUIContent("Transmission tint", "Color which tints transmitted light. Alpha is ignored."); |
|||
public readonly GUIContent profileMaxRadius = new GUIContent("Max Radius", "Effective radius of the filter (in millimeters). The blur is energy-preserving, so a wide filter results in a large area with small contributions of individual samples. Reducing the distance increases the sharpness of the result."); |
|||
public readonly GUIContent texturingMode = new GUIContent("Texturing Mode", "Specifies when the diffuse texture should be applied."); |
|||
public readonly GUIContent[] texturingModeOptions = new GUIContent[2] |
|||
{ |
|||
new GUIContent("Pre- and post-scatter", "Texturing is performed during both the lighting and the SSS passes. Slightly blurs the diffuse texture. Choose this mode if your diffuse texture contains little to no SSS lighting."), |
|||
new GUIContent("Post-scatter", "Texturing is performed only during the SSS pass. Effectively preserves the sharpness of the diffuse texture. Choose this mode if your diffuse texture already contains SSS lighting (e.g. a photo of skin).") |
|||
}; |
|||
public readonly GUIContent profileTransmissionMode = new GUIContent("Transmission Mode", "Configures the simulation of light passing through thin objects. Depends on the thickness value (which is applied in the normal direction)."); |
|||
public readonly GUIContent[] transmissionModeOptions = new GUIContent[2] |
|||
{ |
|||
new GUIContent("Regular", "Choose this mode for moderately thick objects. For performance reasons, transmitted light ignores occlusion (shadows)."), |
|||
new GUIContent("Thin Object", "Choose this mode for thin objects, such as paper or leaves. Transmitted light reuses the shadowing state of the surface.") |
|||
}; |
|||
public readonly GUIContent profileMinMaxThickness = new GUIContent("Min-Max Thickness (mm)", "Shows the values of the thickness remap below (in millimeters)."); |
|||
public readonly GUIContent profileThicknessRemap = new GUIContent("Thickness Remap (mm)", "Remaps the thickness parameter from [0, 1] to the desired range (in millimeters)."); |
|||
public readonly GUIContent profileWorldScale = new GUIContent("World Scale", "Size of the world unit in meters."); |
|||
public readonly GUIContent profileIor = new GUIContent("Index of Refraction", "Index of refraction. 1.4 for skin. Between 1.3-1.5 for most other material."); |
|||
// Jimenez SSS Model
|
|||
public readonly GUIContent profileScatterDistance1 = new GUIContent("Scattering Distance #1", "The radius (in centimeters) of the 1st Gaussian filter, one per color channel. Alpha is ignored. The blur is energy-preserving, so a wide filter results in a large area with small contributions of individual samples. Smaller values increase the sharpness."); |
|||
public readonly GUIContent profileScatterDistance2 = new GUIContent("Scattering Distance #2", "The radius (in centimeters) of the 2nd Gaussian filter, one per color channel. Alpha is ignored. The blur is energy-preserving, so a wide filter results in a large area with small contributions of individual samples. Smaller values increase the sharpness."); |
|||
public readonly GUIContent profileLerpWeight = new GUIContent("Filter Interpolation", "Controls linear interpolation between the two Gaussian filters."); |
|||
// End Jimenez SSS Model
|
|||
public readonly GUIStyle centeredMiniBoldLabel = new GUIStyle(GUI.skin.label); |
|||
|
|||
public readonly GUIContent SubsurfaceScatteringLabel = new GUIContent("Subsurface Scattering only"); |
|||
public readonly GUIContent TransmissionLabel = new GUIContent("Transmission only"); |
|||
|
|||
|
|||
public Styles() |
|||
{ |
|||
centeredMiniBoldLabel.alignment = TextAnchor.MiddleCenter; |
|||
centeredMiniBoldLabel.fontSize = 10; |
|||
centeredMiniBoldLabel.fontStyle = FontStyle.Bold; |
|||
} |
|||
} |
|||
|
|||
static Styles s_Styles; |
|||
|
|||
// Can't use a static initializer in case we need to create GUIStyle in the Styles class as
|
|||
// these can only be created with an active GUI rendering context
|
|||
void CheckStyles() |
|||
{ |
|||
if (s_Styles == null) |
|||
s_Styles = new Styles(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 055ed0a936f136d4c899453811ccaf04 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
|
|||
|
|||
using UnityEngine; |
|||
using UnityEngine.Experimental.Rendering; |
|||
using UnityEngine.Experimental.Rendering.LightweightPipeline; |
|||
|
|||
namespace UnityEditor.Experimental.Rendering.LightweightPipeline |
|||
{ |
|||
|
|||
//LW class name and namespace are the same. Gotta do this for now.
|
|||
using LWPipeline = UnityEngine.Experimental.Rendering.LightweightPipeline.LightweightPipeline; |
|||
public class LWBaseEditor<T> : Editor where T : UnityEngine.Object |
|||
{ |
|||
internal PropertyFetcher<T> properties { get; private set; } |
|||
|
|||
protected T m_Target |
|||
{ |
|||
get { return target as T; } |
|||
} |
|||
|
|||
protected T[] m_Targets |
|||
{ |
|||
get { return targets as T[]; } |
|||
} |
|||
|
|||
protected LWPipeline m_LWPipeline |
|||
{ |
|||
get { return RenderPipelineManager.currentPipeline as LWPipeline; } |
|||
} |
|||
|
|||
protected virtual void OnEnable() |
|||
{ |
|||
properties = new PropertyFetcher<T>(serializedObject); |
|||
} |
|||
} |
|||
|
|||
[CustomEditor(typeof(DiffusionProfileSettings))] |
|||
public sealed partial class DiffusionProfileSettingsEditor : LWBaseEditor<DiffusionProfileSettings> |
|||
{ |
|||
sealed class Profile |
|||
{ |
|||
internal SerializedProperty self; |
|||
internal DiffusionProfile objReference; |
|||
|
|||
internal SerializedProperty name; |
|||
internal SerializedProperty transmissionTint; |
|||
internal SerializedProperty thicknessRemap; |
|||
internal SerializedProperty worldScale; |
|||
internal SerializedProperty scatterDistance1; |
|||
internal SerializedProperty scatterDistance2; |
|||
internal SerializedProperty lerpWeight; |
|||
|
|||
// Render preview
|
|||
internal RenderTexture profileRT; |
|||
internal RenderTexture transmittanceRT; |
|||
|
|||
internal Profile() |
|||
{ |
|||
profileRT = new RenderTexture(256, 256, 0, RenderTextureFormat.DefaultHDR); |
|||
transmittanceRT = new RenderTexture( 16, 256, 0, RenderTextureFormat.DefaultHDR); |
|||
} |
|||
|
|||
internal void Release() |
|||
{ |
|||
CoreUtils.Destroy(profileRT); |
|||
CoreUtils.Destroy(transmittanceRT); |
|||
} |
|||
} |
|||
|
|||
List<Profile> m_Profiles; |
|||
|
|||
Material m_ProfileMaterial; |
|||
Material m_TransmittanceMaterial; |
|||
|
|||
protected override void OnEnable() |
|||
{ |
|||
base.OnEnable(); |
|||
|
|||
// These shaders don't need to be reference by RenderPipelineResource as they are not use at runtime
|
|||
m_ProfileMaterial = CoreUtils.CreateEngineMaterial("Hidden/LightweightPipeline/PreintegratedScatter"); |
|||
m_TransmittanceMaterial = CoreUtils.CreateEngineMaterial("Hidden/LightweightPipeline/DrawTransmittanceGraph"); |
|||
|
|||
int count = DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT - 1; |
|||
m_Profiles = new List<Profile>(); |
|||
|
|||
var serializedProfiles = properties.Find(x => x.profiles); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
var serializedProfile = serializedProfiles.GetArrayElementAtIndex(i); |
|||
var rp = new RelativePropertyFetcher<DiffusionProfile>(serializedProfile); |
|||
|
|||
var profile = new Profile |
|||
{ |
|||
self = serializedProfile, |
|||
objReference = m_Target.profiles[i], |
|||
|
|||
name = rp.Find(x => x.name), |
|||
|
|||
transmissionTint = rp.Find(x => x.transmissionTint), |
|||
thicknessRemap = rp.Find(x => x.thicknessRemap), |
|||
worldScale = rp.Find(x => x.worldScale), |
|||
scatterDistance1 = rp.Find(x => x.scatterDistance1), |
|||
scatterDistance2 = rp.Find(x => x.scatterDistance2), |
|||
lerpWeight = rp.Find(x => x.lerpWeight) |
|||
}; |
|||
|
|||
m_Profiles.Add(profile); |
|||
} |
|||
} |
|||
|
|||
void OnDisable() |
|||
{ |
|||
CoreUtils.Destroy(m_ProfileMaterial); |
|||
CoreUtils.Destroy(m_TransmittanceMaterial); |
|||
|
|||
foreach (var profile in m_Profiles) |
|||
profile.Release(); |
|||
|
|||
m_Profiles = null; |
|||
} |
|||
|
|||
public override void OnInspectorGUI() |
|||
{ |
|||
CheckStyles(); |
|||
|
|||
// Display a warning if this settings asset is not currently in use
|
|||
if (m_LWPipeline == null || m_LWPipeline.DiffusionProfileSettings != m_Target) |
|||
EditorGUILayout.HelpBox("These profiles aren't currently in use, assign this asset to the LW render pipeline asset to use them.", MessageType.Warning); |
|||
|
|||
serializedObject.Update(); |
|||
|
|||
EditorGUILayout.Space(); |
|||
|
|||
if (m_Profiles == null || m_Profiles.Count == 0) |
|||
return; |
|||
|
|||
for (int i = 0; i < m_Profiles.Count; i++) |
|||
{ |
|||
var profile = m_Profiles[i]; |
|||
|
|||
CoreEditorUtils.DrawSplitter(); |
|||
|
|||
bool state = profile.self.isExpanded; |
|||
state = CoreEditorUtils.DrawHeaderFoldout(profile.name.stringValue, state); |
|||
|
|||
if (state) |
|||
{ |
|||
EditorGUI.indentLevel++; |
|||
EditorGUILayout.PropertyField(profile.name); |
|||
|
|||
using (var scope = new EditorGUI.ChangeCheckScope()) |
|||
{ |
|||
EditorGUILayout.PropertyField(profile.scatterDistance1, s_Styles.profileScatterDistance1); |
|||
EditorGUILayout.PropertyField(profile.scatterDistance2, s_Styles.profileScatterDistance2); |
|||
EditorGUILayout.PropertyField(profile.lerpWeight, s_Styles.profileLerpWeight); |
|||
|
|||
EditorGUILayout.PropertyField(profile.worldScale, s_Styles.profileWorldScale); |
|||
|
|||
EditorGUILayout.Space(); |
|||
EditorGUILayout.LabelField(s_Styles.TransmissionLabel, EditorStyles.boldLabel); |
|||
|
|||
EditorGUILayout.PropertyField(profile.transmissionTint, s_Styles.profileTransmissionTint); |
|||
EditorGUILayout.PropertyField(profile.thicknessRemap, s_Styles.profileMinMaxThickness); |
|||
var thicknessRemap = profile.thicknessRemap.vector2Value; |
|||
EditorGUILayout.MinMaxSlider(s_Styles.profileThicknessRemap, ref thicknessRemap.x, ref thicknessRemap.y, 0f, 50f); |
|||
profile.thicknessRemap.vector2Value = thicknessRemap; |
|||
|
|||
EditorGUILayout.Space(); |
|||
EditorGUILayout.LabelField(s_Styles.profilePreview0, s_Styles.centeredMiniBoldLabel); |
|||
EditorGUILayout.LabelField(s_Styles.profilePreview1, EditorStyles.centeredGreyMiniLabel); |
|||
EditorGUILayout.LabelField(s_Styles.profilePreview2, EditorStyles.centeredGreyMiniLabel); |
|||
EditorGUILayout.LabelField(s_Styles.profilePreview3, EditorStyles.centeredGreyMiniLabel); |
|||
EditorGUILayout.Space(); |
|||
|
|||
serializedObject.ApplyModifiedProperties(); |
|||
|
|||
if (scope.changed) |
|||
{ |
|||
// Validate and update the cache for this profile only
|
|||
profile.objReference.Validate(); |
|||
m_Target.UpdateCache(i); |
|||
} |
|||
} |
|||
|
|||
RenderPreview(profile); |
|||
|
|||
EditorGUILayout.Space(); |
|||
EditorGUI.indentLevel--; |
|||
} |
|||
|
|||
profile.self.isExpanded = state; |
|||
} |
|||
|
|||
CoreEditorUtils.DrawSplitter(); |
|||
|
|||
serializedObject.ApplyModifiedProperties(); |
|||
} |
|||
|
|||
void RenderPreview(Profile profile) |
|||
{ |
|||
var T = (Vector4)profile.transmissionTint.colorValue; |
|||
var R = profile.thicknessRemap.vector2Value; |
|||
|
|||
// Apply the three-sigma rule, and rescale.
|
|||
float s = (1f / 3f) * DiffusionProfileConstants.SSS_DISTANCE_SCALE; |
|||
var scatterDist1 = profile.scatterDistance1.colorValue; |
|||
var scatterDist2 = profile.scatterDistance2.colorValue; |
|||
//float rMax = Mathf.Max(scatterDist1.r, scatterDist1.g, scatterDist1.b,
|
|||
// scatterDist2.r, scatterDist2.g, scatterDist2.b);
|
|||
var stdDev1 = s * (Vector4)scatterDist1; |
|||
var stdDev2 = s * (Vector4)scatterDist2; |
|||
m_ProfileMaterial.SetVector(DiffusionProfileShaderIDs._StdDev1, stdDev1); |
|||
m_ProfileMaterial.SetVector(DiffusionProfileShaderIDs._StdDev2, stdDev2); |
|||
m_ProfileMaterial.SetFloat (DiffusionProfileShaderIDs._LerpWeight, profile.lerpWeight.floatValue); |
|||
|
|||
// Draw the profile.
|
|||
EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(128f, 128f), profile.profileRT, m_ProfileMaterial, ScaleMode.ScaleToFit, 1f); |
|||
|
|||
EditorGUILayout.Space(); |
|||
EditorGUILayout.LabelField(s_Styles.transmittancePreview0, s_Styles.centeredMiniBoldLabel); |
|||
EditorGUILayout.LabelField(s_Styles.transmittancePreview1, EditorStyles.centeredGreyMiniLabel); |
|||
EditorGUILayout.LabelField(s_Styles.transmittancePreview2, EditorStyles.centeredGreyMiniLabel); |
|||
EditorGUILayout.Space(); |
|||
|
|||
// Multiply by 0.1 to convert from millimeters to centimeters. Apply the distance scale.
|
|||
float a = 0.1f * DiffusionProfileConstants.SSS_DISTANCE_SCALE; |
|||
var halfRcpVarianceAndWeight1 = new Vector4(a * a * 0.5f / (stdDev1.x * stdDev1.x), a * a * 0.5f / (stdDev1.y * stdDev1.y), a * a * 0.5f / (stdDev1.z * stdDev1.z), 4f * (1f - profile.lerpWeight.floatValue)); |
|||
var halfRcpVarianceAndWeight2 = new Vector4(a * a * 0.5f / (stdDev2.x * stdDev2.x), a * a * 0.5f / (stdDev2.y * stdDev2.y), a * a * 0.5f / (stdDev2.z * stdDev2.z), 4f * profile.lerpWeight.floatValue); |
|||
m_TransmittanceMaterial.SetVector(DiffusionProfileShaderIDs._HalfRcpVarianceAndWeight1, halfRcpVarianceAndWeight1); |
|||
m_TransmittanceMaterial.SetVector(DiffusionProfileShaderIDs._HalfRcpVarianceAndWeight2, halfRcpVarianceAndWeight2); |
|||
|
|||
m_TransmittanceMaterial.SetVector(DiffusionProfileShaderIDs._TransmissionTint, T); |
|||
m_TransmittanceMaterial.SetVector(DiffusionProfileShaderIDs._ThicknessRemap, R); |
|||
|
|||
// Draw the transmittance graph.
|
|||
EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(16f, 16f), profile.transmittanceRT, m_TransmittanceMaterial, ScaleMode.ScaleToFit, 16f); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 504eaede1bddcaf49abd69af8188ce17 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 1790e4a0d6a8b8443b88dfcff73cc9fe |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "Hidden/LightweightPipeline/DrawTransmittanceGraph" |
|||
{ |
|||
SubShader |
|||
{ |
|||
Pass |
|||
{ |
|||
Cull Off |
|||
ZTest Always |
|||
ZWrite Off |
|||
Blend Off |
|||
|
|||
HLSLPROGRAM |
|||
#pragma target 4.5 |
|||
#pragma only_renderers d3d11 ps4 xboxone vulkan metal |
|||
|
|||
#pragma vertex Vert |
|||
#pragma fragment Frag |
|||
|
|||
//------------------------------------------------------------------------------------- |
|||
// Include |
|||
//------------------------------------------------------------------------------------- |
|||
|
|||
#include "CoreRP/ShaderLibrary/Common.hlsl" |
|||
#include "CoreRP/ShaderLibrary/CommonMaterial.hlsl" |
|||
|
|||
#define USE_LEGACY_UNITY_MATRIX_VARIABLES |
|||
#include "HDRP/ShaderVariables.hlsl" |
|||
#include "HDRP/Material/DiffusionProfile/DiffusionProfile.hlsl" |
|||
|
|||
//------------------------------------------------------------------------------------- |
|||
// Inputs & outputs |
|||
//------------------------------------------------------------------------------------- |
|||
|
|||
float4 _HalfRcpVarianceAndWeight1, _HalfRcpVarianceAndWeight2; |
|||
float4 _TransmissionTint, _ThicknessRemap; |
|||
|
|||
//------------------------------------------------------------------------------------- |
|||
// Implementation |
|||
//------------------------------------------------------------------------------------- |
|||
|
|||
struct Attributes |
|||
{ |
|||
float3 vertex : POSITION; |
|||
float2 texcoord : TEXCOORD0; |
|||
}; |
|||
|
|||
struct Varyings |
|||
{ |
|||
float4 vertex : SV_POSITION; |
|||
float2 texcoord : TEXCOORD0; |
|||
}; |
|||
|
|||
Varyings Vert(Attributes input) |
|||
{ |
|||
Varyings output; |
|||
output.vertex = TransformWorldToHClip(input.vertex); |
|||
output.texcoord = input.texcoord.xy; |
|||
return output; |
|||
} |
|||
|
|||
float4 Frag(Varyings input) : SV_Target |
|||
{ |
|||
float d = (_ThicknessRemap.x + input.texcoord.x * (_ThicknessRemap.y - _ThicknessRemap.x)); |
|||
float3 T; |
|||
|
|||
T = ComputeTransmittanceJimenez(_HalfRcpVarianceAndWeight1.rgb, |
|||
_HalfRcpVarianceAndWeight1.a, |
|||
_HalfRcpVarianceAndWeight2.rgb, |
|||
_HalfRcpVarianceAndWeight2.a, |
|||
float3(0.25, 0.25, 0.25), d); |
|||
|
|||
// Apply gamma for visualization only. Do not apply gamma to the color. |
|||
return float4(sqrt(T) * _TransmissionTint.rgb, 1); |
|||
} |
|||
ENDHLSL |
|||
} |
|||
} |
|||
Fallback Off |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: db930d68409d0d94ebbb2a14fa07f3ee |
|||
ShaderImporter: |
|||
externalObjects: {} |
|||
defaultTextures: [] |
|||
nonModifiableTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "Hidden/LightweightPipeline/PreintegratedScatter" |
|||
{ |
|||
SubShader |
|||
{ |
|||
Pass |
|||
{ |
|||
Cull Off |
|||
ZTest Always |
|||
ZWrite Off |
|||
Blend Off |
|||
|
|||
HLSLPROGRAM |
|||
#pragma target 4.5 |
|||
#pragma only_renderers d3d11 ps4 xboxone vulkan metal |
|||
|
|||
#pragma vertex Vert |
|||
#pragma fragment Frag |
|||
|
|||
//------------------------------------------------------------------------------------- |
|||
// Include |
|||
//------------------------------------------------------------------------------------- |
|||
|
|||
#include "CoreRP/ShaderLibrary/Common.hlsl" |
|||
#define USE_LEGACY_UNITY_MATRIX_VARIABLES |
|||
#include "HDRP/ShaderVariables.hlsl" |
|||
|
|||
#include "LWRP/DiffusionProfile/DiffusionProfileSettings.cs.hlsl" |
|||
|
|||
//------------------------------------------------------------------------------------- |
|||
// Inputs & outputs |
|||
//------------------------------------------------------------------------------------- |
|||
|
|||
float4 _StdDev1, _StdDev2; |
|||
float _LerpWeight, _MaxRadius; // See 'SubsurfaceScatteringParameters' |
|||
|
|||
//------------------------------------------------------------------------------------- |
|||
// Implementation |
|||
//------------------------------------------------------------------------------------- |
|||
|
|||
struct Attributes |
|||
{ |
|||
float3 vertex : POSITION; |
|||
float2 texcoord : TEXCOORD0; |
|||
}; |
|||
|
|||
struct Varyings |
|||
{ |
|||
float4 vertex : SV_POSITION; |
|||
float2 texcoord : TEXCOORD0; |
|||
}; |
|||
|
|||
Varyings Vert(Attributes input) |
|||
{ |
|||
Varyings output; |
|||
output.vertex = TransformWorldToHClip(input.vertex); |
|||
output.texcoord = input.texcoord.xy; |
|||
return output; |
|||
} |
|||
|
|||
float3 DiffusionProfile(float r, float3 var1, float3 var2) |
|||
{ |
|||
return lerp(exp(-r * r / (2 * var1)) / (TWO_PI * var1), |
|||
exp(-r * r / (2 * var2)) / (TWO_PI * var2), _LerpWeight); |
|||
} |
|||
|
|||
//Pre-integrate the diffuse scattering, indexed by Curvate and NdotL. |
|||
float4 Frag(Varyings input) : SV_Target |
|||
{ |
|||
float NdotL = 2.0 * input.texcoord.x - 1.0; |
|||
float Curvature = 1.0 / input.texcoord.y; |
|||
|
|||
float Theta = acos(NdotL); |
|||
float3 W = float3(0, 0, 0); |
|||
float3 L = float3(0, 0, 0); |
|||
|
|||
float x = -(PI / 2.0); |
|||
while(x <= (PI / 2.0)) |
|||
{ |
|||
float Diffuse = saturate(cos(Theta + x)); |
|||
|
|||
float R = abs(2.0 * Curvature * sin(x * 0.5)); |
|||
float3 Variance1 = _StdDev1.rgb * _StdDev1.rgb; |
|||
float3 Variance2 = _StdDev2.rgb * _StdDev2.rgb; |
|||
|
|||
float3 Weights = DiffusionProfile(R, Variance1, Variance2); |
|||
|
|||
W += Weights; |
|||
L += Diffuse * Weights; |
|||
|
|||
x += PI / 180.0; |
|||
} |
|||
|
|||
return float4(sqrt(L / W), 1); |
|||
} |
|||
ENDHLSL |
|||
} |
|||
} |
|||
Fallback Off |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: c509c4fbe7201384f9bfea1d062070a6 |
|||
ShaderImporter: |
|||
externalObjects: {} |
|||
defaultTextures: [] |
|||
nonModifiableTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#ifndef LIGHTWEIGHT_LIGHTING_INCLUDED |
|||
#define LIGHTWEIGHT_LIGHTING_INCLUDED |
|||
|
|||
#include "CoreRP/ShaderLibrary/Common.hlsl" |
|||
#include "CoreRP/ShaderLibrary/EntityLighting.hlsl" |
|||
#include "CoreRP/ShaderLibrary/ImageBasedLighting.hlsl" |
|||
#include "LWRP/ShaderLibrary/Core.hlsl" |
|||
#include "LWRP/ShaderLibrary/Shadows.hlsl" |
|||
|
|||
#ifdef NO_ADDITIONAL_LIGHTS |
|||
#undef _ADDITIONAL_LIGHTS |
|||
#endif |
|||
|
|||
// If lightmap is not defined than we evaluate GI (ambient + probes) from SH |
|||
// We might do it fully or partially in vertex to save shader ALU |
|||
#if !defined(LIGHTMAP_ON) |
|||
#ifdef SHADER_API_GLES |
|||
// Evaluates SH fully in vertex |
|||
#define EVALUATE_SH_VERTEX |
|||
#else |
|||
// Evaluates L2 SH in vertex and L0L1 in pixel |
|||
#define EVALUATE_SH_MIXED |
|||
#endif |
|||
#endif |
|||
|
|||
#ifdef LIGHTMAP_ON |
|||
#define OUTPUT_LIGHTMAP_UV(lightmapUV, lightmapScaleOffset, OUT) OUT.xy = lightmapUV.xy * lightmapScaleOffset.xy + lightmapScaleOffset.zw; |
|||
#define OUTPUT_SH(normalWS, OUT) |
|||
#else |
|||
#define OUTPUT_LIGHTMAP_UV(lightmapUV, lightmapScaleOffset, OUT) |
|||
#define OUTPUT_SH(normalWS, OUT) OUT.xyz = SampleSHVertex(normalWS) |
|||
#endif |
|||
|
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
// Light Helpers // |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
|
|||
// Abstraction over Light input constants |
|||
struct LightInput |
|||
{ |
|||
float4 position; |
|||
half3 color; |
|||
half4 distanceAttenuation; |
|||
half4 spotDirection; |
|||
half4 spotAttenuation; |
|||
}; |
|||
|
|||
// Abstraction over Light shading data. |
|||
struct Light |
|||
{ |
|||
half3 direction; |
|||
half3 color; |
|||
half3 attenuation; //NOTE: RGB attenuation for shadow scatter. |
|||
half subtractiveModeAttenuation; |
|||
}; |
|||
|
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
// Attenuation Functions / |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
half CookieAttenuation(float3 worldPos) |
|||
{ |
|||
#ifdef _MAIN_LIGHT_COOKIE |
|||
#ifdef _MAIN_LIGHT_DIRECTIONAL |
|||
float2 cookieUV = mul(_WorldToLight, float4(worldPos, 1.0)).xy; |
|||
return SAMPLE_TEXTURE2D(_MainLightCookie, sampler_MainLightCookie, cookieUV).a; |
|||
#elif defined(_MAIN_LIGHT_SPOT) |
|||
float4 projPos = mul(_WorldToLight, float4(worldPos, 1.0)); |
|||
float2 cookieUV = projPos.xy / projPos.w + 0.5; |
|||
return SAMPLE_TEXTURE2D(_MainLightCookie, sampler_MainLightCookie, cookieUV).a; |
|||
#endif // POINT LIGHT cookie not supported |
|||
#endif |
|||
|
|||
return 1; |
|||
} |
|||
|
|||
// Matches Unity Vanila attenuation |
|||
// Attenuation smoothly decreases to light range. |
|||
half DistanceAttenuation(half distanceSqr, half3 distanceAttenuation) |
|||
{ |
|||
// We use a shared distance attenuation for additional directional and puctual lights |
|||
// for directional lights attenuation will be 1 |
|||
half quadFalloff = distanceAttenuation.x; |
|||
half denom = distanceSqr * quadFalloff + 1.0; |
|||
half lightAtten = 1.0 / denom; |
|||
|
|||
// We need to smoothly fade attenuation to light range. We start fading linearly at 80% of light range |
|||
// Therefore: |
|||
// fadeDistance = (0.8 * 0.8 * lightRangeSq) |
|||
// smoothFactor = (lightRangeSqr - distanceSqr) / (lightRangeSqr - fadeDistance) |
|||
// We can rewrite that to fit a MAD by doing |
|||
// distanceSqr * (1.0 / (fadeDistanceSqr - lightRangeSqr)) + (-lightRangeSqr / (fadeDistanceSqr - lightRangeSqr) |
|||
// distanceSqr * distanceAttenuation.y + distanceAttenuation.z |
|||
half smoothFactor = saturate(distanceSqr * distanceAttenuation.y + distanceAttenuation.z); |
|||
return lightAtten * smoothFactor; |
|||
} |
|||
|
|||
half SpotAttenuation(half3 spotDirection, half3 lightDirection, half4 spotAttenuation) |
|||
{ |
|||
// Spot Attenuation with a linear falloff can be defined as |
|||
// (SdotL - cosOuterAngle) / (cosInnerAngle - cosOuterAngle) |
|||
// This can be rewritten as |
|||
// invAngleRange = 1.0 / (cosInnerAngle - cosOuterAngle) |
|||
// SdotL * invAngleRange + (-cosOuterAngle * invAngleRange) |
|||
// SdotL * spotAttenuation.x + spotAttenuation.y |
|||
|
|||
// If we precompute the terms in a MAD instruction |
|||
half SdotL = dot(spotDirection, lightDirection); |
|||
half atten = saturate(SdotL * spotAttenuation.x + spotAttenuation.y); |
|||
return atten * atten; |
|||
} |
|||
|
|||
half4 GetLightDirectionAndAttenuation(LightInput lightInput, float3 positionWS) |
|||
{ |
|||
half4 directionAndAttenuation; |
|||
float3 posToLightVec = lightInput.position.xyz - positionWS * lightInput.position.w; |
|||
float distanceSqr = max(dot(posToLightVec, posToLightVec), FLT_MIN); |
|||
|
|||
directionAndAttenuation.xyz = half3(posToLightVec * rsqrt(distanceSqr)); |
|||
directionAndAttenuation.w = DistanceAttenuation(distanceSqr, lightInput.distanceAttenuation.xyz); |
|||
directionAndAttenuation.w *= SpotAttenuation(lightInput.spotDirection.xyz, directionAndAttenuation.xyz, lightInput.spotAttenuation); |
|||
return directionAndAttenuation; |
|||
} |
|||
|
|||
half4 GetMainLightDirectionAndAttenuation(LightInput lightInput, float3 positionWS) |
|||
{ |
|||
half4 directionAndAttenuation; |
|||
|
|||
#if defined(_MAIN_LIGHT_DIRECTIONAL) |
|||
directionAndAttenuation = half4(lightInput.position.xyz, 1.0); |
|||
#else |
|||
directionAndAttenuation = GetLightDirectionAndAttenuation(lightInput, positionWS); |
|||
#endif |
|||
|
|||
// Cookies are only computed for main light |
|||
directionAndAttenuation.w *= CookieAttenuation(positionWS); |
|||
|
|||
return directionAndAttenuation; |
|||
} |
|||
|
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
// Light Abstraction // |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
|
|||
Light GetMainLight(float3 positionWS) |
|||
{ |
|||
LightInput lightInput; |
|||
lightInput.position = _MainLightPosition; |
|||
lightInput.color = _MainLightColor.rgb; |
|||
lightInput.distanceAttenuation = _MainLightDistanceAttenuation; |
|||
lightInput.spotDirection = _MainLightSpotDir; |
|||
lightInput.spotAttenuation = _MainLightSpotAttenuation; |
|||
|
|||
half4 directionAndRealtimeAttenuation = GetMainLightDirectionAndAttenuation(lightInput, positionWS); |
|||
|
|||
Light light; |
|||
light.direction = directionAndRealtimeAttenuation.xyz; |
|||
light.attenuation = directionAndRealtimeAttenuation.w; |
|||
light.subtractiveModeAttenuation = lightInput.distanceAttenuation.w; |
|||
light.color = lightInput.color; |
|||
|
|||
return light; |
|||
} |
|||
|
|||
Light GetLight(int i, float3 positionWS) |
|||
{ |
|||
LightInput lightInput; |
|||
half4 indices = (i < 4) ? unity_4LightIndices0 : unity_4LightIndices1; |
|||
int index = (i < 4) ? i : i - 4; |
|||
int lightIndex = indices[index]; |
|||
lightInput.position = _AdditionalLightPosition[lightIndex]; |
|||
lightInput.color = _AdditionalLightColor[lightIndex].rgb; |
|||
lightInput.distanceAttenuation = _AdditionalLightDistanceAttenuation[lightIndex]; |
|||
lightInput.spotDirection = _AdditionalLightSpotDir[lightIndex]; |
|||
lightInput.spotAttenuation = _AdditionalLightSpotAttenuation[lightIndex]; |
|||
|
|||
half4 directionAndRealtimeAttenuation = GetLightDirectionAndAttenuation(lightInput, positionWS); |
|||
|
|||
Light light; |
|||
light.direction = directionAndRealtimeAttenuation.xyz; |
|||
light.attenuation = directionAndRealtimeAttenuation.w; |
|||
light.subtractiveModeAttenuation = lightInput.distanceAttenuation.w; |
|||
light.color = lightInput.color; |
|||
|
|||
return light; |
|||
} |
|||
|
|||
half GetPixelLightCount() |
|||
{ |
|||
return min(_AdditionalLightCount.x, unity_LightIndicesOffsetAndCount.y); |
|||
} |
|||
|
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
// BRDF Functions // |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
|
|||
#define kDieletricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04) // standard dielectric reflectivity coef at incident angle (= 4%) |
|||
|
|||
struct BRDFData |
|||
{ |
|||
half3 diffuse; |
|||
half3 specular; |
|||
half perceptualRoughness; |
|||
half roughness; |
|||
half roughness2; |
|||
half grazingTerm; |
|||
half curvature; //Our subsurface scattering is parameterized by curvature |
|||
}; |
|||
|
|||
half ReflectivitySpecular(half3 specular) |
|||
{ |
|||
#if (SHADER_TARGET < 30) |
|||
// SM2.0: instruction count limitation |
|||
return specular.r; // Red channel - because most metals are either monocrhome or with redish/yellowish tint |
|||
#else |
|||
return max(max(specular.r, specular.g), specular.b); |
|||
#endif |
|||
} |
|||
|
|||
half OneMinusReflectivityMetallic(half metallic) |
|||
{ |
|||
// We'll need oneMinusReflectivity, so |
|||
// 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic) |
|||
// store (1-dielectricSpec) in kDieletricSpec.a, then |
|||
// 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) = |
|||
// = alpha - metallic * alpha |
|||
half oneMinusDielectricSpec = kDieletricSpec.a; |
|||
return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec; |
|||
} |
|||
|
|||
inline void InitializeBRDFData(half3 albedo, half metallic, half3 specular, half smoothness, half alpha, half curvature, out BRDFData outBRDFData) |
|||
{ |
|||
#ifdef _SPECULAR_SETUP |
|||
half reflectivity = ReflectivitySpecular(specular); |
|||
half oneMinusReflectivity = 1.0 - reflectivity; |
|||
|
|||
outBRDFData.diffuse = albedo * (half3(1.0h, 1.0h, 1.0h) - specular); |
|||
outBRDFData.specular = specular; |
|||
#else |
|||
|
|||
half oneMinusReflectivity = OneMinusReflectivityMetallic(metallic); |
|||
half reflectivity = 1.0 - oneMinusReflectivity; |
|||
|
|||
outBRDFData.diffuse = albedo * oneMinusReflectivity; |
|||
outBRDFData.specular = lerp(kDieletricSpec.rgb, albedo, metallic); |
|||
#endif |
|||
|
|||
outBRDFData.grazingTerm = saturate(smoothness + reflectivity); |
|||
outBRDFData.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(smoothness); |
|||
outBRDFData.roughness = PerceptualRoughnessToRoughness(outBRDFData.perceptualRoughness); |
|||
outBRDFData.roughness2 = outBRDFData.roughness * outBRDFData.roughness; |
|||
outBRDFData.curvature = curvature; |
|||
|
|||
#ifdef _ALPHAPREMULTIPLY_ON |
|||
outBRDFData.diffuse *= alpha; |
|||
alpha = alpha * oneMinusReflectivity + reflectivity; |
|||
#endif |
|||
} |
|||
|
|||
half3 EnvironmentBRDF(BRDFData brdfData, half3 indirectDiffuse, half3 indirectSpecular, half fresnelTerm) |
|||
{ |
|||
half3 c = indirectDiffuse * brdfData.diffuse; |
|||
float surfaceReduction = 1.0 / (brdfData.roughness2 + 1.0); |
|||
c += surfaceReduction * indirectSpecular * lerp(brdfData.specular, brdfData.grazingTerm, fresnelTerm); |
|||
return c; |
|||
} |
|||
|
|||
// Based on Minimalist CookTorrance BRDF |
|||
// Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255 |
|||
// |
|||
// * NDF [Modified] GGX |
|||
// * Modified Kelemen and Szirmay-Kalos for Visibility term |
|||
// * Fresnel approximated with 1/LdotH |
|||
half3 DirectBDRF(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS) |
|||
{ |
|||
#ifndef _SPECULARHIGHLIGHTS_OFF |
|||
half3 halfDir = SafeNormalize(lightDirectionWS + viewDirectionWS); |
|||
|
|||
half NoH = saturate(dot(normalWS, halfDir)); |
|||
half LoH = saturate(dot(lightDirectionWS, halfDir)); |
|||
|
|||
// GGX Distribution multiplied by combined approximation of Visibility and Fresnel |
|||
// See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course |
|||
// https://community.arm.com/events/1155 |
|||
half d = NoH * NoH * (brdfData.roughness2 - 1.h) + 1.00001h; |
|||
|
|||
half LoH2 = LoH * LoH; |
|||
half specularTerm = brdfData.roughness2 / ((d * d) * max(0.1h, LoH2) * (brdfData.roughness + 0.5h) * 4); |
|||
|
|||
// on mobiles (where half actually means something) denominator have risk of overflow |
|||
// clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles) |
|||
// sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...)) |
|||
#if defined (SHADER_API_MOBILE) |
|||
specularTerm = specularTerm - HALF_MIN; |
|||
specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles |
|||
#endif |
|||
|
|||
half3 color = specularTerm * brdfData.specular + brdfData.diffuse; |
|||
return color; |
|||
#else |
|||
return brdfData.diffuse; |
|||
#endif |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////// |
|||
// Subsurface Scattering // |
|||
//////////////////////////////////////////////////////////////////////////////// |
|||
|
|||
TEXTURE2D(_PreintegratedDiffuseScatteringTex); |
|||
SAMPLER(sampler_PreintegratedDiffuseScatteringTex); |
|||
|
|||
TEXTURE2D_ARRAY(_PreintegratedDiffuseScatteringTextures); |
|||
SAMPLER(sampler_PreintegratedDiffuseScatteringTextures); |
|||
|
|||
int _DiffusionProfile; |
|||
|
|||
half3 DiffuseScattering(BRDFData brdfData, half3 normalHighWS, half3 normalLowWS, half3 lightDirectionWS) |
|||
{ |
|||
float3 c = 1.0 - float3(0.5, 0.3, 0.22); //TODO: BRDFData? |
|||
float3 rN = lerp(normalHighWS, normalLowWS, c.r); |
|||
float3 gN = lerp(normalHighWS, normalLowWS, c.g); |
|||
float3 bN = lerp(normalHighWS, normalLowWS, c.b); |
|||
|
|||
float3 NdotL = float3(dot(rN, lightDirectionWS), |
|||
dot(gN, lightDirectionWS), |
|||
dot(bN, lightDirectionWS)); |
|||
NdotL = 0.5 * NdotL + 0.5; //Scale to 0..1 for lookup. |
|||
|
|||
float3 scatteredDiffuse; |
|||
// scatteredDiffuse.r = SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(NdotL.r, brdfData.curvature)).r; |
|||
// scatteredDiffuse.g = SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(NdotL.g, brdfData.curvature)).g; |
|||
// scatteredDiffuse.b = SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(NdotL.b, brdfData.curvature)).b; |
|||
|
|||
scatteredDiffuse.r = SAMPLE_TEXTURE2D_ARRAY(_PreintegratedDiffuseScatteringTextures, sampler_PreintegratedDiffuseScatteringTextures, float2(NdotL.r, brdfData.curvature), _DiffusionProfile).r; |
|||
scatteredDiffuse.g = SAMPLE_TEXTURE2D_ARRAY(_PreintegratedDiffuseScatteringTextures, sampler_PreintegratedDiffuseScatteringTextures, float2(NdotL.g, brdfData.curvature), _DiffusionProfile).g; |
|||
scatteredDiffuse.b = SAMPLE_TEXTURE2D_ARRAY(_PreintegratedDiffuseScatteringTextures, sampler_PreintegratedDiffuseScatteringTextures, float2(NdotL.b, brdfData.curvature), _DiffusionProfile).b; |
|||
return scatteredDiffuse; |
|||
} |
|||
|
|||
half3 ShadowScattering(half shadow, half NdotL) |
|||
{ |
|||
//NOTE: We should be looking up into a second generated LUT with respect to our shadow penumbra, |
|||
// but reusing the diffuse scattering preintegration and indexing with our shadow term does |
|||
// more than enough to emulate shadow scattering. |
|||
half penumbraWidth = 0.5 * NdotL + 0.5; |
|||
//return SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(shadow, penumbraWidth)); |
|||
|
|||
return SAMPLE_TEXTURE2D_ARRAY(_PreintegratedDiffuseScatteringTextures, sampler_PreintegratedDiffuseScatteringTextures, float2(shadow, penumbraWidth), _DiffusionProfile); |
|||
} |
|||
|
|||
// This function can be precomputed for efficiency |
|||
float3 T(float s) { |
|||
return float3(0.233, 0.455, 0.649) * exp(s / 0.0064) + |
|||
float3(0.1, 0.336, 0.344) * exp(s / 0.0484) + |
|||
float3(0.118, 0.198, 0.0) * exp(s / 0.187) + |
|||
float3(0.113, 0.007, 0.007) * exp(s / 0.567) + |
|||
float3(0.358, 0.004, 0.0) * exp(s / 1.99) + |
|||
float3(0.078, 0.0, 0.0) * exp(s / 7.41); |
|||
} |
|||
|
|||
SamplerState Sampler_Linear_Clamp; |
|||
float4 _ShadowBiasForTransmission[4]; |
|||
half3 Transmittance(float3 positionWS, float3 normalWS, float3 albedo, Light light) |
|||
{ |
|||
//TODO: Normal bias here? |
|||
positionWS = positionWS - 0.025 * normalWS; |
|||
|
|||
//Calculate the distance traveled through the media. |
|||
half cascadeIndex = ComputeCascadeIndex(positionWS); |
|||
float4 shadowCoord = mul(_WorldToShadow[cascadeIndex], float4(positionWS, 1.0)); |
|||
|
|||
//NOTE: Need to counteract the bias done during shadowmap construction, to properly calculate thickness |
|||
shadowCoord.z += _ShadowBiasForTransmission[cascadeIndex].x; |
|||
|
|||
half d1 = SAMPLE_TEXTURE2D(_ShadowMap, Sampler_Linear_Clamp, shadowCoord.xyz); |
|||
half d2 = shadowCoord.z; |
|||
|
|||
float translucency = 0.7; |
|||
float scale = 8.25 * (1.0 - translucency) / 0.005; |
|||
float s = abs(d1 - d2) * scale; |
|||
|
|||
float irradiance = max(0.3 + dot(-normalWS, light.direction), 0.0); |
|||
return T(-s * s) * irradiance * light.color * albedo; |
|||
} |
|||
|
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
// Global Illumination // |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
|
|||
// Samples SH L0, L1 and L2 terms |
|||
half3 SampleSH(half3 normalWS) |
|||
{ |
|||
// LPPV is not supported in Ligthweight Pipeline |
|||
real4 SHCoefficients[7]; |
|||
SHCoefficients[0] = unity_SHAr; |
|||
SHCoefficients[1] = unity_SHAg; |
|||
SHCoefficients[2] = unity_SHAb; |
|||
SHCoefficients[3] = unity_SHBr; |
|||
SHCoefficients[4] = unity_SHBg; |
|||
SHCoefficients[5] = unity_SHBb; |
|||
SHCoefficients[6] = unity_SHC; |
|||
|
|||
return max(half3(0, 0, 0), SampleSH9(SHCoefficients, normalWS)); |
|||
} |
|||
|
|||
// SH Vertex Evaluation. Depending on target SH sampling might be |
|||
// done completely per vertex or mixed with L2 term per vertex and L0, L1 |
|||
// per pixel. See SampleSHPixel |
|||
half3 SampleSHVertex(half3 normalWS) |
|||
{ |
|||
#if defined(EVALUATE_SH_VERTEX) |
|||
return max(half3(0, 0, 0), SampleSH(normalWS)); |
|||
#elif defined(EVALUATE_SH_MIXED) |
|||
// no max since this is only L2 contribution |
|||
return SHEvalLinearL2(normalWS, unity_SHBr, unity_SHBg, unity_SHBb, unity_SHC); |
|||
#endif |
|||
|
|||
// Fully per-pixel. Nothing to compute. |
|||
return half3(0.0, 0.0, 0.0); |
|||
} |
|||
|
|||
// SH Pixel Evaluation. Depending on target SH sampling might be done |
|||
// mixed or fully in pixel. See SampleSHVertex |
|||
half3 SampleSHPixel(half3 L2Term, half3 normalWS) |
|||
{ |
|||
#ifdef EVALUATE_SH_MIXED |
|||
half3 L0L1Term = SHEvalLinearL0L1(normalWS, unity_SHAr, unity_SHAg, unity_SHAb); |
|||
return max(half3(0, 0, 0), L2Term + L0L1Term); |
|||
#endif |
|||
|
|||
// Default: Evaluate SH fully per-pixel |
|||
return SampleSH(normalWS); |
|||
} |
|||
|
|||
// Sample baked lightmap. Non-Direction and Directional if available. |
|||
// Realtime GI is not supported. |
|||
half3 SampleLightmap(float2 lightmapUV, half3 normalWS) |
|||
{ |
|||
#ifdef UNITY_LIGHTMAP_FULL_HDR |
|||
bool encodedLightmap = false; |
|||
#else |
|||
bool encodedLightmap = true; |
|||
#endif |
|||
|
|||
// The shader library sample lightmap functions transform the lightmap uv coords to apply bias and scale. |
|||
// However, lightweight pipeline already transformed those coords in vertex. We pass half4(1, 1, 0, 0) and |
|||
// the compiler will optimize the transform away. |
|||
half4 transformCoords = half4(1, 1, 0, 0); |
|||
|
|||
#ifdef DIRLIGHTMAP_COMBINED |
|||
return SampleDirectionalLightmap(TEXTURE2D_PARAM(unity_Lightmap, samplerunity_Lightmap), |
|||
TEXTURE2D_PARAM(unity_LightmapInd, samplerunity_Lightmap), |
|||
lightmapUV, transformCoords, normalWS, encodedLightmap); |
|||
#else |
|||
return SampleSingleLightmap(TEXTURE2D_PARAM(unity_Lightmap, samplerunity_Lightmap), lightmapUV, transformCoords, encodedLightmap); |
|||
#endif |
|||
} |
|||
|
|||
// We either sample GI from baked lightmap or from probes. |
|||
// If lightmap: sampleData.xy = lightmapUV |
|||
// If probe: sampleData.xyz = L2 SH terms |
|||
half3 SampleGI(float4 sampleData, half3 normalWS) |
|||
{ |
|||
#ifdef LIGHTMAP_ON |
|||
return SampleLightmap(sampleData.xy, normalWS); |
|||
#endif |
|||
|
|||
// If lightmap is not enabled we sample GI from SH |
|||
return SampleSHPixel(sampleData.xyz, normalWS); |
|||
} |
|||
|
|||
half3 GlossyEnvironmentReflection(half3 reflectVector, half perceptualRoughness, half occlusion) |
|||
{ |
|||
#if !defined(_GLOSSYREFLECTIONS_OFF) |
|||
half mip = PerceptualRoughnessToMipmapLevel(perceptualRoughness); |
|||
half4 encodedIrradiance = SAMPLE_TEXTURECUBE_LOD(unity_SpecCube0, samplerunity_SpecCube0, reflectVector, mip); |
|||
|
|||
#if !defined(UNITY_USE_NATIVE_HDR) |
|||
half3 irradiance = DecodeHDREnvironment(encodedIrradiance, unity_SpecCube0_HDR); |
|||
#else |
|||
half3 irradiance = encodedIrradiance.rbg; |
|||
#endif |
|||
|
|||
return irradiance * occlusion; |
|||
#endif // GLOSSY_REFLECTIONS |
|||
|
|||
return _GlossyEnvironmentColor.rgb * occlusion; |
|||
} |
|||
|
|||
half3 SubtractDirectMainLightFromLightmap(Light mainLight, half3 normalWS, half3 bakedGI) |
|||
{ |
|||
// Let's try to make realtime shadows work on a surface, which already contains |
|||
// baked lighting and shadowing from the main sun light. |
|||
// Summary: |
|||
// 1) Calculate possible value in the shadow by subtracting estimated light contribution from the places occluded by realtime shadow: |
|||
// a) preserves other baked lights and light bounces |
|||
// b) eliminates shadows on the geometry facing away from the light |
|||
// 2) Clamp against user defined ShadowColor. |
|||
// 3) Pick original lightmap value, if it is the darkest one. |
|||
|
|||
// 1) Gives good estimate of illumination as if light would've been shadowed during the bake. |
|||
// Preserves bounce and other baked lights |
|||
// No shadows on the geometry facing away from the light |
|||
half shadowStrength = GetShadowStrength(); |
|||
half NdotL = saturate(dot(mainLight.direction, normalWS)); |
|||
half3 lambert = mainLight.color * NdotL; |
|||
half3 estimatedLightContributionMaskedByInverseOfShadow = lambert * (1.0 - mainLight.attenuation); |
|||
half3 subtractedLightmap = bakedGI - estimatedLightContributionMaskedByInverseOfShadow; |
|||
|
|||
// 2) Allows user to define overall ambient of the scene and control situation when realtime shadow becomes too dark. |
|||
half3 realtimeShadow = max(subtractedLightmap, _SubtractiveShadowColor.xyz); |
|||
realtimeShadow = lerp(bakedGI, realtimeShadow, shadowStrength); |
|||
|
|||
// 3) Pick darkest color |
|||
return min(bakedGI, realtimeShadow); |
|||
} |
|||
|
|||
half3 GlobalIllumination(BRDFData brdfData, half3 bakedGI, half occlusion, half3 normalWS, half3 viewDirectionWS) |
|||
{ |
|||
half3 reflectVector = reflect(-viewDirectionWS, normalWS); |
|||
half fresnelTerm = Pow4(1.0 - saturate(dot(normalWS, viewDirectionWS))); |
|||
|
|||
half3 indirectDiffuse = bakedGI * occlusion; |
|||
half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, occlusion); |
|||
|
|||
return EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm); |
|||
} |
|||
|
|||
void MixRealtimeAndBakedGI(inout Light light, half3 normalWS, inout half3 bakedGI, half4 shadowMask) |
|||
{ |
|||
#if defined(_MAIN_LIGHT_DIRECTIONAL) && defined(_MIXED_LIGHTING_SUBTRACTIVE) && defined(LIGHTMAP_ON) && defined(_SHADOWS_ENABLED) |
|||
bakedGI = SubtractDirectMainLightFromLightmap(light, normalWS, bakedGI); |
|||
#endif |
|||
|
|||
#if defined(LIGHTMAP_ON) |
|||
#if defined(_MIXED_LIGHTING_SHADOWMASK) |
|||
// TODO: |
|||
#elif defined(_MIXED_LIGHTING_SUBTRACTIVE) |
|||
// Subtractive Light mode has direct light contribution baked into lightmap for mixed lights. |
|||
// We need to remove direct realtime contribution from mixed lights |
|||
// subtractiveModeBakedOcclusion is set 0.0 if this light occlusion was baked in the lightmap, 1.0 otherwise. |
|||
light.attenuation *= light.subtractiveModeAttenuation; |
|||
#endif |
|||
#endif |
|||
} |
|||
|
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
// Lighting Functions // |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
half3 LightingLambert(half3 lightColor, half3 lightDir, half3 normal) |
|||
{ |
|||
half NdotL = saturate(dot(normal, lightDir)); |
|||
return lightColor * NdotL; |
|||
} |
|||
|
|||
half3 LightingSpecular(half3 lightColor, half3 lightDir, half3 normal, half3 viewDir, half4 specularGloss, half shininess) |
|||
{ |
|||
half3 halfVec = SafeNormalize(lightDir + viewDir); |
|||
half NdotH = saturate(dot(normal, halfVec)); |
|||
half3 specularReflection = specularGloss.rgb * pow(NdotH, shininess) * specularGloss.a; |
|||
return lightColor * specularReflection; |
|||
} |
|||
|
|||
half3 LightingPhysicallyBased(BRDFData brdfData, half3 lightColor, half3 lightDirectionWS, half3 lightAttenuation, half3 normalWS, half3 viewDirectionWS) |
|||
{ |
|||
half3 NdotL = DiffuseScattering(brdfData, normalWS, normalWS, lightDirectionWS); |
|||
half3 radiance = lightColor * (lightAttenuation * NdotL); |
|||
return DirectBDRF(brdfData, normalWS, lightDirectionWS, viewDirectionWS) * radiance; |
|||
} |
|||
|
|||
half3 LightingPhysicallyBased(BRDFData brdfData, Light light, half3 normalWS, half3 viewDirectionWS) |
|||
{ |
|||
return LightingPhysicallyBased(brdfData, light.color, light.direction, light.attenuation, normalWS, viewDirectionWS); |
|||
} |
|||
|
|||
half3 VertexLighting(float3 positionWS, half3 normalWS) |
|||
{ |
|||
half3 vertexLightColor = half3(0.0, 0.0, 0.0); |
|||
|
|||
#if defined(_VERTEX_LIGHTS) |
|||
int vertexLightStart = _AdditionalLightCount.x; |
|||
int vertexLightEnd = min(_AdditionalLightCount.y, unity_LightIndicesOffsetAndCount.y); |
|||
for (int lightIter = vertexLightStart; lightIter < vertexLightEnd; ++lightIter) |
|||
{ |
|||
Light light = GetLight(lightIter, positionWS); |
|||
|
|||
half3 lightColor = light.color * light.attenuation; |
|||
vertexLightColor += LightingLambert(lightColor, light.direction, normalWS); |
|||
} |
|||
#endif |
|||
|
|||
return vertexLightColor; |
|||
} |
|||
|
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
// Fragment Functions // |
|||
// Used by ShaderGraph and others builtin renderers // |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
half4 LightweightFragmentPBR(InputData inputData, half3 albedo, half metallic, half3 specular, |
|||
half smoothness, half occlusion, half3 emission, half alpha, half curvature) |
|||
{ |
|||
BRDFData brdfData; |
|||
InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, curvature, brdfData); |
|||
|
|||
Light mainLight = GetMainLight(inputData.positionWS); |
|||
|
|||
#ifdef _SHADOWS_ENABLED |
|||
half shadow = RealtimeShadowAttenuation(inputData.shadowCoord); |
|||
mainLight.attenuation *= ShadowScattering(shadow, dot(mainLight.direction, inputData.normalWS)); |
|||
#endif |
|||
|
|||
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0)); |
|||
|
|||
half3 color = GlobalIllumination (brdfData, inputData.bakedGI, occlusion, inputData.normalWS, inputData.viewDirectionWS); |
|||
color += LightingPhysicallyBased (brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS); |
|||
//color += Transmittance (inputData.positionWS, inputData.normalWS, albedo, mainLight); |
|||
|
|||
#ifdef _ADDITIONAL_LIGHTS |
|||
int pixelLightCount = GetPixelLightCount(); |
|||
for (int i = 0; i < pixelLightCount; ++i) |
|||
{ |
|||
//TODO: DICE Approximation |
|||
Light light = GetLight(i, inputData.positionWS); |
|||
color += LightingPhysicallyBased(brdfData, light, inputData.normalWS, inputData.viewDirectionWS); |
|||
} |
|||
#endif |
|||
|
|||
color += inputData.vertexLighting * brdfData.diffuse; |
|||
color += emission; |
|||
return half4(color, alpha); |
|||
} |
|||
|
|||
half4 LightweightFragmentBlinnPhong(InputData inputData, half3 diffuse, half4 specularGloss, half shininess, half3 emission, half alpha) |
|||
{ |
|||
Light mainLight = GetMainLight(inputData.positionWS); |
|||
|
|||
#ifdef _SHADOWS_ENABLED |
|||
mainLight.attenuation *= RealtimeShadowAttenuation(inputData.shadowCoord); |
|||
#endif |
|||
|
|||
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0)); |
|||
|
|||
half3 attenuatedLightColor = mainLight.color * mainLight.attenuation; |
|||
half3 diffuseColor = inputData.bakedGI + LightingLambert(attenuatedLightColor, mainLight.direction, inputData.normalWS); |
|||
half3 specularColor = LightingSpecular(attenuatedLightColor, mainLight.direction, inputData.normalWS, inputData.viewDirectionWS, specularGloss, shininess); |
|||
|
|||
#ifdef _ADDITIONAL_LIGHTS |
|||
int pixelLightCount = GetPixelLightCount(); |
|||
for (int i = 0; i < pixelLightCount; ++i) |
|||
{ |
|||
Light light = GetLight(i, inputData.positionWS); |
|||
half3 attenuatedLightColor = light.color * light.attenuation; |
|||
diffuseColor += LightingLambert(attenuatedLightColor, light.direction, inputData.normalWS); |
|||
specularColor += LightingSpecular(attenuatedLightColor, light.direction, inputData.normalWS, inputData.viewDirectionWS, specularGloss, shininess); |
|||
} |
|||
#endif |
|||
|
|||
half3 finalColor = diffuseColor * diffuse + emission; |
|||
finalColor += inputData.vertexLighting * diffuse; |
|||
|
|||
#if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR) |
|||
finalColor += specularColor; |
|||
#endif |
|||
|
|||
ApplyFog(finalColor, inputData.fogCoord); |
|||
return half4(finalColor, alpha); |
|||
} |
|||
#endif |
|
|||
fileFormatVersion: 2 |
|||
guid: c263964f3b4d79649b965f0ec765696c |
|||
ShaderImporter: |
|||
externalObjects: {} |
|||
defaultTextures: [] |
|||
nonModifiableTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#ifndef LIGHTWEIGHT_PASS_LIT_INCLUDED |
|||
#define LIGHTWEIGHT_PASS_LIT_INCLUDED |
|||
|
|||
#include "LWRP/ShaderLibrary/InputSurface.hlsl" |
|||
#include "Lighting.hlsl" |
|||
|
|||
struct LightweightVertexInput |
|||
{ |
|||
float4 vertex : POSITION; |
|||
float3 normal : NORMAL; |
|||
float4 tangent : TANGENT; |
|||
float2 texcoord : TEXCOORD0; |
|||
float2 lightmapUV : TEXCOORD1; |
|||
UNITY_VERTEX_INPUT_INSTANCE_ID |
|||
|
|||
}; |
|||
|
|||
struct LightweightVertexOutput |
|||
{ |
|||
float2 uv : TEXCOORD0; |
|||
float4 lightmapUVOrVertexSH : TEXCOORD1; // holds either lightmapUV or vertex SH. depending on LIGHTMAP_ON |
|||
float3 posWS : TEXCOORD2; |
|||
half3 normal : TEXCOORD3; |
|||
|
|||
#ifdef _NORMALMAP |
|||
half3 tangent : TEXCOORD4; |
|||
half3 binormal : TEXCOORD5; |
|||
#endif |
|||
|
|||
half3 viewDir : TEXCOORD6; |
|||
half4 fogFactorAndVertexLight : TEXCOORD7; // x: fogFactor, yzw: vertex light |
|||
|
|||
#ifdef _SHADOWS_ENABLED |
|||
float4 shadowCoord : TEXCOORD8; |
|||
#endif |
|||
|
|||
float4 clipPos : SV_POSITION; |
|||
UNITY_VERTEX_INPUT_INSTANCE_ID |
|||
}; |
|||
|
|||
void InitializeInputData(LightweightVertexOutput IN, half3 normalTS, out InputData inputData) |
|||
{ |
|||
inputData.positionWS = IN.posWS.xyz; |
|||
|
|||
#ifdef _NORMALMAP |
|||
inputData.normalWS = TangentToWorldNormal(normalTS, IN.tangent, IN.binormal, IN.normal); |
|||
#else |
|||
inputData.normalWS = normalize(IN.normal); |
|||
#endif |
|||
|
|||
#ifdef SHADER_API_MOBILE |
|||
// viewDirection should be normalized here, but we avoid doing it as it's close enough and we save some ALU. |
|||
inputData.viewDirectionWS = IN.viewDir; |
|||
#else |
|||
inputData.viewDirectionWS = normalize(IN.viewDir); |
|||
#endif |
|||
|
|||
#ifdef _SHADOWS_ENABLED |
|||
inputData.shadowCoord = IN.shadowCoord; |
|||
#else |
|||
inputData.shadowCoord = float4(0, 0, 0, 0); |
|||
#endif |
|||
|
|||
inputData.fogCoord = IN.fogFactorAndVertexLight.x; |
|||
inputData.vertexLighting = IN.fogFactorAndVertexLight.yzw; |
|||
inputData.bakedGI = SampleGI(IN.lightmapUVOrVertexSH, inputData.normalWS); |
|||
} |
|||
|
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
// Vertex and Fragment functions // |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
|
|||
// Vertex: Used for Standard and StandardSimpleLighting shaders |
|||
LightweightVertexOutput LitPassVertex(LightweightVertexInput v) |
|||
{ |
|||
LightweightVertexOutput o = (LightweightVertexOutput)0; |
|||
|
|||
UNITY_SETUP_INSTANCE_ID(v); |
|||
UNITY_TRANSFER_INSTANCE_ID(v, o); |
|||
|
|||
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); |
|||
|
|||
o.posWS = TransformObjectToWorld(v.vertex.xyz); |
|||
o.clipPos = TransformWorldToHClip(o.posWS); |
|||
o.viewDir = SafeNormalize(GetCameraPositionWS() - o.posWS); |
|||
|
|||
// initializes o.normal and if _NORMALMAP also o.tangent and o.binormal |
|||
OUTPUT_NORMAL(v, o); |
|||
|
|||
// We either sample GI from lightmap or SH. lightmap UV and vertex SH coefficients |
|||
// are packed in lightmapUVOrVertexSH to save interpolator. |
|||
// The following funcions initialize |
|||
OUTPUT_LIGHTMAP_UV(v.lightmapUV, unity_LightmapST, o.lightmapUVOrVertexSH); |
|||
OUTPUT_SH(o.normal, o.lightmapUVOrVertexSH); |
|||
|
|||
half3 vertexLight = VertexLighting(o.posWS, o.normal); |
|||
half fogFactor = ComputeFogFactor(o.clipPos.z); |
|||
o.fogFactorAndVertexLight = half4(fogFactor, vertexLight); |
|||
|
|||
#ifdef _SHADOWS_ENABLED |
|||
o.shadowCoord = ComputeScreenPos(o.clipPos); |
|||
#endif |
|||
|
|||
return o; |
|||
} |
|||
|
|||
// Used for Standard shader |
|||
half4 LitPassFragment(LightweightVertexOutput IN) : SV_Target |
|||
{ |
|||
UNITY_SETUP_INSTANCE_ID(IN); |
|||
|
|||
SurfaceData surfaceData; |
|||
InitializeStandardLitSurfaceData(IN.uv, surfaceData); |
|||
|
|||
InputData inputData; |
|||
InitializeInputData(IN, surfaceData.normalTS, inputData); |
|||
|
|||
half3 normalLowWS = UnpackNormalScale(SAMPLE_TEXTURE2D_LOD(_BumpMap, sampler_BumpMap, IN.uv, 3), _BumpScale); |
|||
#ifdef _NORMALMAP |
|||
normalLowWS = TangentToWorldNormal(normalLowWS, IN.tangent, IN.binormal, IN.normal); |
|||
#endif |
|||
|
|||
//TODO: UI Element |
|||
//NOTE: Maybe find a working relationship b/w curvature/translucency then scale one term? |
|||
float curvature = ( (length(fwidth(IN.normal))) / |
|||
(length(fwidth(inputData.positionWS))) * 0.05 ); |
|||
|
|||
Light light = GetMainLight(inputData.positionWS); |
|||
|
|||
half4 color = LightweightFragmentPBR(inputData, surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.occlusion, surfaceData.emission, surfaceData.alpha, curvature); |
|||
|
|||
ApplyFog(color.rgb, inputData.fogCoord); |
|||
|
|||
return half4(color.rgb, 1); |
|||
} |
|||
|
|||
// Used for StandardSimpleLighting shader |
|||
half4 LitPassFragmentSimple(LightweightVertexOutput IN) : SV_Target |
|||
{ |
|||
UNITY_SETUP_INSTANCE_ID(IN); |
|||
|
|||
float2 uv = IN.uv; |
|||
half4 diffuseAlpha = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv); |
|||
half3 diffuse = diffuseAlpha.rgb * _Color.rgb; |
|||
|
|||
#ifdef _GLOSSINESS_FROM_BASE_ALPHA |
|||
half alpha = _Color.a; |
|||
#else |
|||
half alpha = diffuseAlpha.a * _Color.a; |
|||
#endif |
|||
|
|||
AlphaDiscard(alpha, _Cutoff); |
|||
|
|||
#ifdef _NORMALMAP |
|||
half3 normalTS = Normal(uv); |
|||
#else |
|||
half3 normalTS = half3(0, 0, 1); |
|||
#endif |
|||
|
|||
half3 emission = Emission(uv); |
|||
half4 specularGloss = SpecularGloss(uv, diffuseAlpha.a); |
|||
half shininess = _Shininess * 128.0h; |
|||
|
|||
InputData inputData; |
|||
InitializeInputData(IN, normalTS, inputData); |
|||
|
|||
return LightweightFragmentBlinnPhong(inputData, diffuse, specularGloss, shininess, emission, alpha); |
|||
}; |
|||
|
|||
#endif |
|
|||
fileFormatVersion: 2 |
|||
guid: 0f682a49c4fb6784791b9af0734a6542 |
|||
ShaderImporter: |
|||
externalObjects: {} |
|||
defaultTextures: [] |
|||
nonModifiableTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "LightweightPipeline/LightweightSubsurface" |
|||
{ |
|||
Properties |
|||
{ |
|||
// Specular vs Metallic workflow |
|||
[HideInInspector] _WorkflowMode("WorkflowMode", Float) = 1.0 |
|||
|
|||
_Color("Color", Color) = (1,1,1,1) |
|||
_MainTex("Albedo", 2D) = "white" {} |
|||
|
|||
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 |
|||
|
|||
_Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5 |
|||
_GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0 |
|||
_SmoothnessTextureChannel("Smoothness texture channel", Float) = 0 |
|||
|
|||
[Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0 |
|||
_MetallicGlossMap("Metallic", 2D) = "white" {} |
|||
|
|||
_SpecColor("Specular", Color) = (0.2, 0.2, 0.2) |
|||
_SpecGlossMap("Specular", 2D) = "white" {} |
|||
|
|||
[ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0 |
|||
[ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 1.0 |
|||
|
|||
_BumpScale("Scale", Float) = 1.0 |
|||
_BumpMap("Normal Map", 2D) = "bump" {} |
|||
|
|||
_Parallax("Height Scale", Range(0.005, 0.08)) = 0.02 |
|||
_ParallaxMap("Height Map", 2D) = "black" {} |
|||
|
|||
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0 |
|||
_OcclusionMap("Occlusion", 2D) = "white" {} |
|||
|
|||
_EmissionColor("Color", Color) = (0,0,0) |
|||
_EmissionMap("Emission", 2D) = "white" {} |
|||
|
|||
_DetailMask("Detail Mask", 2D) = "white" {} |
|||
|
|||
_DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {} |
|||
_DetailNormalMapScale("Scale", Float) = 1.0 |
|||
_DetailNormalMap("Normal Map", 2D) = "bump" {} |
|||
|
|||
//Subsurface |
|||
_DiffusionProfile("Diffusion Profile", Int) = 0 |
|||
|
|||
[Enum(UV0,0,UV1,1)] _UVSec("UV Set for secondary textures", Float) = 0 |
|||
|
|||
// Blending state |
|||
[HideInInspector] _Mode("__mode", Float) = 0.0 |
|||
[HideInInspector] _SrcBlend("__src", Float) = 1.0 |
|||
[HideInInspector] _DstBlend("__dst", Float) = 0.0 |
|||
[HideInInspector] _ZWrite("__zw", Float) = 1.0 |
|||
} |
|||
|
|||
SubShader |
|||
{ |
|||
// Lightweight Pipeline tag is required. If Lightweight pipeline is not set in the graphics settings |
|||
// this Subshader will fail. One can add a subshader below or fallback to Standard built-in to make this |
|||
// material work with both Lightweight Pipeline and Builtin Unity Pipeline |
|||
Tags{"RenderType" = "Opaque" "RenderPipeline" = "LightweightPipeline"} |
|||
LOD 300 |
|||
|
|||
// ------------------------------------------------------------------ |
|||
// Forward pass. Shades all light in a single pass. GI + emission + Fog |
|||
Pass |
|||
{ |
|||
// Lightmode matches the ShaderPassName set in LightweightPipeline.cs. SRPDefaultUnlit and passes with |
|||
// no LightMode tag are also rendered by Lightweight Pipeline |
|||
Tags{"LightMode" = "LightweightForward"} |
|||
|
|||
Blend[_SrcBlend][_DstBlend] |
|||
ZWrite[_ZWrite] |
|||
|
|||
HLSLPROGRAM |
|||
// Required to compile gles 2.0 with standard SRP library |
|||
// All shaders must be compiled with HLSLcc and currently only gles is not using HLSLcc by default |
|||
#pragma prefer_hlslcc gles |
|||
#pragma target 3.0 |
|||
|
|||
// ------------------------------------- |
|||
// Material Keywords |
|||
#pragma shader_feature _NORMALMAP |
|||
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON |
|||
#pragma shader_feature _EMISSION |
|||
#pragma shader_feature _METALLICSPECGLOSSMAP |
|||
#pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A |
|||
#pragma shader_feature _OCCLUSIONMAP |
|||
|
|||
#pragma shader_feature _SPECULARHIGHLIGHTS_OFF |
|||
#pragma shader_feature _GLOSSYREFLECTIONS_OFF |
|||
#pragma shader_feature _SPECULAR_SETUP |
|||
|
|||
// ------------------------------------- |
|||
// Lightweight Pipeline keywords |
|||
#pragma multi_compile _MAIN_LIGHT_DIRECTIONAL _MAIN_LIGHT_SPOT |
|||
#pragma multi_compile _ _SHADOWS_ENABLED |
|||
#pragma multi_compile _ _MAIN_LIGHT_COOKIE |
|||
#pragma multi_compile _ _ADDITIONAL_LIGHTS |
|||
#pragma multi_compile _ _VERTEX_LIGHTS |
|||
#pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE |
|||
#pragma multi_compile _ FOG_LINEAR FOG_EXP2 |
|||
|
|||
// ------------------------------------- |
|||
// Unity defined keywords |
|||
#pragma multi_compile _ UNITY_SINGLE_PASS_STEREO STEREO_INSTANCING_ON STEREO_MULTIVIEW_ON |
|||
#pragma multi_compile _ DIRLIGHTMAP_COMBINED LIGHTMAP_ON |
|||
|
|||
//-------------------------------------- |
|||
// GPU Instancing |
|||
#pragma multi_compile_instancing |
|||
|
|||
// LW doesn't support dynamic GI. So we save 30% shader variants if we assume |
|||
// LIGHTMAP_ON when DIRLIGHTMAP_COMBINED is set |
|||
#ifdef DIRLIGHTMAP_COMBINED |
|||
#define LIGHTMAP_ON |
|||
#endif |
|||
|
|||
#define SUBSURFACE_SCATTERING |
|||
|
|||
#pragma vertex LitPassVertex |
|||
#pragma fragment LitPassFragment |
|||
|
|||
#include "LightweightPassLit.hlsl" |
|||
ENDHLSL |
|||
} |
|||
|
|||
Pass |
|||
{ |
|||
Tags{"LightMode" = "ShadowCaster"} |
|||
|
|||
ZWrite On ZTest LEqual |
|||
|
|||
HLSLPROGRAM |
|||
// Required to compile gles 2.0 with standard srp library |
|||
#pragma prefer_hlslcc gles |
|||
#pragma target 2.0 |
|||
|
|||
//-------------------------------------- |
|||
// GPU Instancing |
|||
#pragma multi_compile_instancing |
|||
|
|||
#pragma vertex ShadowPassVertex |
|||
#pragma fragment ShadowPassFragment |
|||
|
|||
#include "LWRP/ShaderLibrary/LightweightPassShadow.hlsl" |
|||
ENDHLSL |
|||
} |
|||
|
|||
Pass |
|||
{ |
|||
Tags{"LightMode" = "DepthOnly"} |
|||
|
|||
ZWrite On |
|||
ColorMask 0 |
|||
|
|||
HLSLPROGRAM |
|||
// Required to compile gles 2.0 with standard srp library |
|||
#pragma prefer_hlslcc gles |
|||
#pragma target 2.0 |
|||
#pragma vertex vert |
|||
#pragma fragment frag |
|||
|
|||
#include "LWRP/ShaderLibrary/Core.hlsl" |
|||
|
|||
float4 vert(float4 pos : POSITION) : SV_POSITION |
|||
{ |
|||
return TransformObjectToHClip(pos.xyz); |
|||
} |
|||
|
|||
half4 frag() : SV_TARGET |
|||
{ |
|||
return 0; |
|||
} |
|||
ENDHLSL |
|||
} |
|||
|
|||
// This pass it not used during regular rendering, only for lightmap baking. |
|||
Pass |
|||
{ |
|||
Tags{"LightMode" = "Meta"} |
|||
|
|||
Cull Off |
|||
|
|||
HLSLPROGRAM |
|||
// Required to compile gles 2.0 with standard srp library |
|||
#pragma prefer_hlslcc gles |
|||
|
|||
#pragma vertex LightweightVertexMeta |
|||
#pragma fragment LightweightFragmentMeta |
|||
|
|||
#pragma shader_feature _SPECULAR_SETUP |
|||
#pragma shader_feature _EMISSION |
|||
#pragma shader_feature _METALLICSPECGLOSSMAP |
|||
#pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A |
|||
#pragma shader_feature EDITOR_VISUALIZATION |
|||
|
|||
#pragma shader_feature _SPECGLOSSMAP |
|||
|
|||
#include "LWRP/ShaderLibrary/LightweightPassMeta.hlsl" |
|||
ENDHLSL |
|||
} |
|||
|
|||
} |
|||
FallBack "Hidden/InternalErrorShader" |
|||
CustomEditor "LightweightSubsurfaceGUI" |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 79b860b25be008744beb48e2d6c4db0c |
|||
ShaderImporter: |
|||
externalObjects: {} |
|||
defaultTextures: [] |
|||
nonModifiableTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#ifndef SUBSURFACE_SCATTERING |
|||
#define SUBSURFACE_SCATTERING |
|||
|
|||
#include "Lighting.hlsl" |
|||
|
|||
TEXTURE2D(_PreintegratedDiffuseScatteringTex); |
|||
SAMPLER(sampler_PreintegratedDiffuseScatteringTex); |
|||
|
|||
TEXTURE2D(_PreintegratedShadowScatteringTex); |
|||
SAMPLER(sampler_PreintegratedShadowScatteringTex); |
|||
|
|||
// This function can be precomputed for efficiency |
|||
float3 T(float s) { |
|||
return float3(0.233, 0.455, 0.649) * exp(s / 0.0064) + |
|||
float3(0.1, 0.336, 0.344) * exp(s / 0.0484) + |
|||
float3(0.118, 0.198, 0.0) * exp(s / 0.187) + |
|||
float3(0.113, 0.007, 0.007) * exp(s / 0.567) + |
|||
float3(0.358, 0.004, 0.0) * exp(s / 1.99) + |
|||
float3(0.078, 0.0, 0.0) * exp(s / 7.41); |
|||
} |
|||
|
|||
float4 _ShadowBiasForTransmission[4]; |
|||
half3 Transmittance(float3 positionWS, float3 normalWS, float3 albedo, Light light) |
|||
{ |
|||
//TODO: Normal bias here? |
|||
positionWS = positionWS - 0.025 * normalWS; |
|||
|
|||
//Calculate the distance traveled through the media. |
|||
half cascadeIndex = ComputeCascadeIndex(positionWS); |
|||
float4 shadowCoord = mul(_WorldToShadow[cascadeIndex], float4(positionWS, 1.0)); |
|||
|
|||
//NOTE: Need to counteract the bias done during shadowmap construction, to properly calculate thickness |
|||
shadowCoord.z += _ShadowBiasForTransmission[cascadeIndex].x; |
|||
|
|||
half d1 = SAMPLE_TEXTURE2D(_ShadowMap, sampler_ShadowMap, shadowCoord.xyz); |
|||
half d2 = shadowCoord.z; |
|||
|
|||
float translucency = 0.001; |
|||
float scale = 8.25 * (1.0 - translucency) / 0.005; |
|||
float s = abs(d1 - d2) * scale; |
|||
|
|||
float irradiance = max(0.3 + dot(-normalWS, light.direction), 0.0); |
|||
|
|||
return T(-s * s) * irradiance * light.color; |
|||
} |
|||
|
|||
|
|||
half3 ScatterShadow(float shadow, float width) |
|||
{ |
|||
return SAMPLE_TEXTURE2D(_PreintegratedShadowScatteringTex, sampler_PreintegratedShadowScatteringTex, float2(shadow, width)); |
|||
} |
|||
|
|||
/* |
|||
half3 DiffuseScattering(BRDFData brdfData, half3 normalHighWS, half3 normalLowWS, half3 lightDirectionWS) |
|||
{ |
|||
float c = 1.0 - float3(0.5, 0.3, 0.22); //TODO: BRDFData? |
|||
float3 rN = lerp(normalHighWS, normalLowWS, c.r); |
|||
float3 gN = lerp(normalHighWS, normalLowWS, c.g); |
|||
float3 bN = lerp(normalHighWS, normalLowWS, c.b); |
|||
|
|||
float3 NdotL = float3(dot(rN, lightDirectionWS), |
|||
dot(gN, lightDirectionWS), |
|||
dot(bN, lightDirectionWS)); |
|||
NdotL = 0.5 * NdotL + 0.5; //Scale to 0..1 for lookup. |
|||
|
|||
float3 scatteredDiffuse; |
|||
scatteredDiffuse.r = SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(NdotL.r, curvature)).r; |
|||
scatteredDiffuse.g = SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(NdotL.g, curvature)).g; |
|||
scatteredDiffuse.b = SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(NdotL.b, curvature)).b; |
|||
return scatteredDiffuse; |
|||
} |
|||
*/ |
|||
|
|||
half3 DiffuseScattering() |
|||
{ |
|||
return half3(1, 0, 0); |
|||
} |
|||
|
|||
half3 SubsurfaceScatteringTest(InputData input, float curvature, half3 normalLowWS) |
|||
{ |
|||
Light light = GetMainLight(input.positionWS); |
|||
|
|||
float MainNdotL = saturate(dot(input.normalWS, light.direction)); |
|||
|
|||
float3 c = 1 - pow(float3(0.4, 0.25, 0.2), 2.2); |
|||
|
|||
//TODO: Only when there are normal maps. |
|||
//TODO: The second mipped normal needs to be clamped, to prevent aliasing at further distances. |
|||
float3 rN = lerp(input.normalWS, normalLowWS, c.r); |
|||
float3 gN = lerp(input.normalWS, normalLowWS, c.g); |
|||
float3 bN = lerp(input.normalWS, normalLowWS, c.b); |
|||
|
|||
float3 NdotL = float3(dot(rN, light.direction), dot(gN, light.direction), dot(bN, light.direction)); |
|||
|
|||
//Fetch the preintegrated diffuse scattering. |
|||
float3 lookup = NdotL * 0.5 + 0.5; |
|||
|
|||
float3 diffuse; |
|||
diffuse.r = SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(lookup.r, curvature)).r; |
|||
diffuse.g = SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(lookup.g, curvature)).g; |
|||
diffuse.b = SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(lookup.b, curvature)).b; |
|||
|
|||
#ifdef _SHADOWS_ENABLED |
|||
//half3 shadow = RealtimeShadowAttenuation(input.shadowCoord); |
|||
|
|||
//NOTE: At the moment, we are looking up into the preintegrated diffuse scattering with our penumbra, but really should be using a second LUT based on our penumbra. |
|||
half3 shadow = SAMPLE_TEXTURE2D(_PreintegratedDiffuseScatteringTex, sampler_PreintegratedDiffuseScatteringTex, float2(RealtimeShadowAttenuation(input.shadowCoord), 0.5 * MainNdotL + 0.5)); |
|||
#else |
|||
half3 shadow = 1; |
|||
#endif |
|||
return (diffuse * light.color * shadow); |
|||
} |
|||
*/ |
|||
|
|||
#endif |
|
|||
fileFormatVersion: 2 |
|||
guid: e0624d7478674734f906a149ad589f4e |
|||
ShaderImporter: |
|||
externalObjects: {} |
|||
defaultTextures: [] |
|||
nonModifiableTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue