浏览代码

Initial integration of diffusion profile settings to sss

/projects-TheLastStand
John 7 年前
当前提交
e5aff3eb
共有 35 个文件被更改,包括 2626 次插入0 次删除
  1. 7
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Data/LightweightPipelineAsset.cs
  2. 7
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/LightweightAssetEditor.cs
  3. 35
      ScriptableRenderPipeline/LightweightPipeline/LWRP/LightweightPipeline.cs
  4. 8
      ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile.meta
  5. 8
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile.meta
  6. 388
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/ShaderGUI/LightweightSubsurfaceGUI.cs
  7. 11
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/ShaderGUI/LightweightSubsurfaceGUI.cs.meta
  8. 8
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface.meta
  9. 350
      ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs
  10. 16
      ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs.hlsl
  11. 9
      ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs.hlsl.meta
  12. 11
      ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs.meta
  13. 31
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.MenuItem.cs
  14. 11
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.MenuItem.cs.meta
  15. 64
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.Styles.cs
  16. 11
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.Styles.cs.meta
  17. 241
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.cs
  18. 11
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.cs.meta
  19. 8
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile.meta
  20. 79
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/DrawTransmittanceGraph.shader
  21. 9
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/DrawTransmittanceGraph.shader.meta
  22. 99
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/PreintegratedScatter.shader
  23. 9
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/PreintegratedScatter.shader.meta
  24. 667
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/Lighting.hlsl
  25. 9
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/Lighting.hlsl.meta
  26. 170
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightPassLit.hlsl
  27. 9
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightPassLit.hlsl.meta
  28. 207
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightSubsurface.shader
  29. 9
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightSubsurface.shader.meta
  30. 115
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/SubsurfaceScattering.hlsl
  31. 9
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/SubsurfaceScattering.hlsl.meta

7
ScriptableRenderPipeline/LightweightPipeline/LWRP/Data/LightweightPipelineAsset.cs


[SerializeField] private ShadowCascades m_ShadowCascades = ShadowCascades.FOUR_CASCADES;
[SerializeField] private float m_Cascade2Split = 0.25f;
[SerializeField] private Vector3 m_Cascade4Split = new Vector3(0.067f, 0.2f, 0.467f);
[SerializeField] private DiffusionProfileSettings m_DiffusionProfileSettings;
[SerializeField]
private LightweightPipelineResources m_ResourcesAsset;

{
get { return m_Cascade4Split; }
private set { m_Cascade4Split = value; }
}
public DiffusionProfileSettings DiffusionProfileSettings
{
get { return m_DiffusionProfileSettings; }
private set { m_DiffusionProfileSettings = value; }
}
public override Material GetDefaultMaterial()

7
ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/LightweightAssetEditor.cs


public static string[] shadowTypeOptions = {"No Shadows", "Hard Shadows", "Hard and Soft Shadows"};
public static string[] shadowCascadeOptions = {"No Cascades", "Two Cascades", "Four Cascades"};
public static GUIContent diffusionProfileSettings = new GUIContent("Diffusion Profile Settings", "");
}
AnimBool m_ShowSoftParticles = new AnimBool();

private SerializedProperty m_ShadowCascadesProp;
private SerializedProperty m_ShadowCascade2SplitProp;
private SerializedProperty m_ShadowCascade4SplitProp;
private SerializedProperty m_DiffusionProfileSettingsProp;
private SerializedProperty m_HDR;
private SerializedProperty m_MSAA;

m_ShadowCascadesProp = serializedObject.FindProperty("m_ShadowCascades");
m_ShadowCascade2SplitProp = serializedObject.FindProperty("m_Cascade2Split");
m_ShadowCascade4SplitProp = serializedObject.FindProperty("m_Cascade4Split");
m_DiffusionProfileSettingsProp = serializedObject.FindProperty("m_DiffusionProfileSettings");
m_HDR = serializedObject.FindProperty("m_SupportsHDR");
m_MSAA = serializedObject.FindProperty("m_MSAA");

DrawAnimatedProperty(m_RequireSoftParticlesProp, Styles.requireSoftParticles, m_ShowSoftParticles);
EditorGUILayout.PropertyField(m_HDR, Styles.hdrContent);
EditorGUILayout.PropertyField(m_MSAA, Styles.msaaContent);
EditorGUILayout.PropertyField(m_DiffusionProfileSettingsProp, Styles.diffusionProfileSettings);
EditorGUI.indentLevel--;
EditorGUILayout.Space();

35
ScriptableRenderPipeline/LightweightPipeline/LWRP/LightweightPipeline.cs


{
private readonly LightweightPipelineAsset m_Asset;
DiffusionProfileSettings m_InternalSSSAsset;
public DiffusionProfileSettings DiffusionProfileSettings
{
get
{
// If no SSS asset is set, build / reuse an internal one for simplicity
var asset = m_Asset.DiffusionProfileSettings;
if (asset == null)
{
if (m_InternalSSSAsset == null)
m_InternalSSSAsset = ScriptableObject.CreateInstance<DiffusionProfileSettings>();
asset = m_InternalSSSAsset;
}
return asset;
}
}
public bool IsInternalDiffusionProfile(DiffusionProfileSettings profile)
{
return m_InternalSSSAsset == profile;
}
// Maximum amount of visible lights the shader can process. This controls the constant global light buffer size.
// It must match the MAX_VISIBLE_LIGHTS in LightweightInput.cginc
private static readonly int kMaxVisibleLights = 16;

{
CommandBuffer cmd = CommandBufferPool.Get("SetupShaderConstants");
SetupShaderLightConstants(cmd, visibleLights, ref lightData);
SetupShaderSubsurfaceConstants(cmd);
SetShaderKeywords(cmd, ref lightData, visibleLights);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);

// Lightweight pipeline also supports only a single shadow light, if available it will be the main light.
SetupMainLightConstants(cmd, lights, lightData.mainLightIndex);
SetupAdditionalListConstants(cmd, lights, ref lightData);
}
private void SetupShaderSubsurfaceConstants(CommandBuffer cmd)
{
//TODO: Safety check
cmd.SetGlobalTexture("_PreintegratedDiffuseScatteringTextures", DiffusionProfileSettings.preintegratedScatterLUTs);
//TODO: Transmission
}
private void SetupMainLightConstants(CommandBuffer cmd, List<VisibleLight> lights, int lightIndex)

8
ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile.meta


fileFormatVersion: 2
guid: 2cff1682e26d69446a8e653056d517bb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile.meta


fileFormatVersion: 2
guid: 27f2302b1772a3e40bbb5eab46e29919
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

388
ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/ShaderGUI/LightweightSubsurfaceGUI.cs


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);
}
}
}
}

11
ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/ShaderGUI/LightweightSubsurfaceGUI.cs.meta


fileFormatVersion: 2
guid: 5dffac7b065436640b6936d57dfedd75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface.meta


fileFormatVersion: 2
guid: 87e0edbcda41b5745a77ba54a215b20b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

350
ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs


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);
}
}
}

16
ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs.hlsl


//
// 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

9
ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs.hlsl.meta


fileFormatVersion: 2
guid: 353718a28c425b44587b4499bd1c21f9
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

11
ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs.meta


fileFormatVersion: 2
guid: 28b82677dd641e24d8383f91eaff8df5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

31
ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.MenuItem.cs


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);
}
}
}

11
ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.MenuItem.cs.meta


fileFormatVersion: 2
guid: 7368191fdab83514aaada1bce4a1b2ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

64
ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.Styles.cs


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();
}
}
}

11
ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.Styles.cs.meta


fileFormatVersion: 2
guid: 055ed0a936f136d4c899453811ccaf04
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

241
ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.cs


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);
}
}
}

11
ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.cs.meta


fileFormatVersion: 2
guid: 504eaede1bddcaf49abd69af8188ce17
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile.meta


fileFormatVersion: 2
guid: 1790e4a0d6a8b8443b88dfcff73cc9fe
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

79
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/DrawTransmittanceGraph.shader


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
}

9
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/DrawTransmittanceGraph.shader.meta


fileFormatVersion: 2
guid: db930d68409d0d94ebbb2a14fa07f3ee
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

99
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/PreintegratedScatter.shader


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
}

9
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/PreintegratedScatter.shader.meta


fileFormatVersion: 2
guid: c509c4fbe7201384f9bfea1d062070a6
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

667
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/Lighting.hlsl


#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

9
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/Lighting.hlsl.meta


fileFormatVersion: 2
guid: c263964f3b4d79649b965f0ec765696c
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

170
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightPassLit.hlsl


#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

9
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightPassLit.hlsl.meta


fileFormatVersion: 2
guid: 0f682a49c4fb6784791b9af0734a6542
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

207
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightSubsurface.shader


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"
}

9
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/LightweightSubsurface.shader.meta


fileFormatVersion: 2
guid: 79b860b25be008744beb48e2d6c4db0c
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

115
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/SubsurfaceScattering.hlsl


#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

9
ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/SubsurfaceScattering.hlsl.meta


fileFormatVersion: 2
guid: e0624d7478674734f906a149ad589f4e
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存