浏览代码

Revert diffusion profile refactor

/projects-TheLastStand
John Parsaie 7 年前
当前提交
7389385b
共有 13 个文件被更改,包括 921 次插入932 次删除
  1. 5
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Data/LightweightPipelineAsset.cs
  2. 15
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Data/LightweightPipelineResources.cs
  3. 705
      ScriptableRenderPipeline/LightweightPipeline/LWRP/DiffusionProfile/DiffusionProfileSettings.cs
  4. 475
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/DiffusionProfileSettingsEditor.cs
  5. 1
      ScriptableRenderPipeline/LightweightPipeline/LWRP/LightweightPipeline.cs
  6. 238
      ScriptableRenderPipeline/LightweightPipeline/LWRP/ShaderLibrary/CoreFunctions.hlsl
  7. 158
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/DrawTransmittanceGraph.shader
  8. 18
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/DrawTransmittanceGraph.shader.meta
  9. 204
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/PreintegratedScatter.shader
  10. 18
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile/PreintegratedScatter.shader.meta
  11. 8
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile.meta
  12. 8
      ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/Shader.meta
  13. 0
      /ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile

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


{
get { return resources != null ? resources.ScreenSpaceShadowShader : null; }
}
public Shader PreintegratedScatterShader
{
get { return resources != null ? resources.PreintegratedScatterShader : null; }
}
}
}

15
ScriptableRenderPipeline/LightweightPipeline/LWRP/Data/LightweightPipelineResources.cs


using UnityEngine;
public class LightweightPipelineResources : ScriptableObject
{
public Shader BlitShader;
public Shader CopyDepthShader;
public Shader ScreenSpaceShadowShader;
using UnityEngine;
public class LightweightPipelineResources : ScriptableObject
{
public Shader BlitShader;
public Shader CopyDepthShader;
public Shader ScreenSpaceShadowShader;
public Shader PreintegratedScatterShader;
}

705
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 = 9;
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");
//Addition Runtime Constants
public static readonly int _PreintegratedDiffuseScatteringTextures = Shader.PropertyToID("_PreintegratedDiffuseScatteringTextures");
public static readonly int _HalfRcpVariancesAndWeights = Shader.PropertyToID("_HalfRcpVariancesAndWeights");
}
[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 Vector4[] filterKernel { get; private set; }
public Vector4 halfRcpWeightedVariances { get; private set; }
public Texture2D preintegratedLUT;
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);
preintegratedLUT = new Texture2D(128, 128, TextureFormat.ARGB32, false, true);
}
public void Validate()
{
thicknessRemap.y = Mathf.Max(thicknessRemap.y, 0f);
thicknessRemap.x = Mathf.Clamp(thicknessRemap.x, 0f, thicknessRemap.y);
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();
BakeLUT();
}
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));
}
public void BakeLUT()
{
CommandBuffer cmd = new CommandBuffer() { name = "BuildLUT" };
Material preintegration = CoreUtils.CreateEngineMaterial("Hidden/LightweightPipeline/PreintegratedScatter");
var stdDev1 = ((1f / 3f) * DiffusionProfileConstants.SSS_DISTANCE_SCALE) * (Vector4)scatterDistance1;
var stdDev2 = ((1f / 3f) * DiffusionProfileConstants.SSS_DISTANCE_SCALE) * (Vector4)scatterDistance2;
preintegration.SetVector(DiffusionProfileShaderIDs._StdDev1, stdDev1);
preintegration.SetVector(DiffusionProfileShaderIDs._StdDev2, stdDev2);
preintegration.SetFloat (DiffusionProfileShaderIDs._LerpWeight, lerpWeight);
RTHandle preintegratedScatterRT = RTHandle.Alloc(128, 128, 1, DepthBits.None, RenderTextureFormat.ARGB32, FilterMode.Point, TextureWrapMode.Clamp, TextureDimension.Tex2D, false);
cmd.Blit(null, preintegratedScatterRT, preintegration);
if(preintegratedLUT == null)
preintegratedLUT = new Texture2D(128, 128, TextureFormat.ARGB32, false, true);
cmd.CopyTexture(preintegratedScatterRT, preintegratedLUT);
preintegratedLUT.Apply();
Graphics.ExecuteCommandBuffer(cmd);
cmd.Dispose();
CoreUtils.Destroy(preintegration);
}
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;
[NonSerialized] public TextureCache2D 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.");
preintegratedScatterLUTs = new TextureCache2D();
preintegratedScatterLUTs.AllocTextureArray(DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT, 128, 128, TextureFormat.ARGB32, false);
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;
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;
}
}
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);
// 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
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];
}
CommandBuffer cmd = new CommandBuffer() { name = "BuildLUT_" + p };
preintegratedScatterLUTs.TransferToSlice(cmd, i, profiles[p].preintegratedLUT);
Graphics.ExecuteCommandBuffer(cmd);
}
}
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 = 9;
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");
//Addition Runtime Constants
public static readonly int _PreintegratedDiffuseScatteringTextures = Shader.PropertyToID("_PreintegratedDiffuseScatteringTextures");
public static readonly int _HalfRcpVariancesAndWeights = Shader.PropertyToID("_HalfRcpVariancesAndWeights");
}
[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 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);
}
public void Validate()
{
thicknessRemap.y = Mathf.Max(thicknessRemap.y, 0f);
thicknessRemap.x = Mathf.Clamp(thicknessRemap.x, 0f, thicknessRemap.y);
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;
[NonSerialized] private Material preintegration;
[NonSerialized] public TextureCache2D 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.");
//NOTE: We can't get reference to the render pipeline assets from scriptable object, but we add it to the assets anyways so they get included in build.
// Searching here is safe because we linked already in the LW resources, forcing it to be included in build.
preintegration = CoreUtils.CreateEngineMaterial("Hidden/LightweightPipeline/PreintegratedScatter");
preintegratedScatterLUTs = new TextureCache2D();
preintegratedScatterLUTs.AllocTextureArray(DiffusionProfileConstants.DIFFUSION_PROFILE_COUNT, 128, 128, TextureFormat.ARGB32, false);
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;
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;
}
}
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);
// 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];
}
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);
CommandBuffer cmd = new CommandBuffer() { name = "BuildLUT_" + p };
cmd.Blit(null, preintegratedScatterRT, preintegration);
preintegratedScatterLUTs.TransferToSlice(cmd, i, preintegratedScatterRT);
Graphics.ExecuteCommandBuffer(cmd);
}
}
}

475
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 scatterDistance1;
internal SerializedProperty scatterDistance2;
internal SerializedProperty lerpWeight;
internal SerializedProperty preintegratedLUT;
// 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),
scatterDistance1 = rp.Find(x => x.scatterDistance1),
scatterDistance2 = rp.Find(x => x.scatterDistance2),
lerpWeight = rp.Find(x => x.lerpWeight),
preintegratedLUT = rp.Find(x => x.preintegratedLUT)
};
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.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.DrawTextureTransparent(GUILayoutUtility.GetRect(128f, 128f), (Texture2D)profile.preintegratedLUT.objectReferenceValue, 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);
}
}
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 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),
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.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);
}
}
}

1
ScriptableRenderPipeline/LightweightPipeline/LWRP/LightweightPipeline.cs


m_BlitMaterial = CoreUtils.CreateEngineMaterial(m_Asset.BlitShader);
m_CopyDepthMaterial = CoreUtils.CreateEngineMaterial(m_Asset.CopyDepthShader);
m_ScreenSpaceShadowsMaterial = CoreUtils.CreateEngineMaterial(m_Asset.ScreenSpaceShadowShader);
m_PreintegratedScatterMaterial = CoreUtils.CreateEngineMaterial(m_Asset.PreintegratedScatterShader);
m_ErrorMaterial = CoreUtils.CreateEngineMaterial("Hidden/InternalErrorShader");
}

238
ScriptableRenderPipeline/LightweightPipeline/LWRP/ShaderLibrary/CoreFunctions.hlsl


#ifndef UNITY_SHADER_VARIABLES_FUNCTIONS_INCLUDED
#define UNITY_SHADER_VARIABLES_FUNCTIONS_INCLUDED
float3 GetCameraPositionWS()
{
return _WorldSpaceCameraPos;
}
float4x4 GetWorldToViewMatrix()
{
return UNITY_MATRIX_V;
}
float4x4 GetObjectToWorldMatrix()
{
return UNITY_MATRIX_M;
}
float4x4 GetWorldToObjectMatrix()
{
return UNITY_MATRIX_I_M;
}
// Transform to homogenous clip space
float4x4 GetWorldToHClipMatrix()
{
return UNITY_MATRIX_VP;
}
real GetOddNegativeScale()
{
return unity_WorldTransformParams.w;
}
float3 TransformWorldToView(float3 positionWS)
{
return mul(GetWorldToViewMatrix(), real4(positionWS, 1.0)).xyz;
}
float3 TransformObjectToWorld(float3 positionOS)
{
return mul(GetObjectToWorldMatrix(), real4(positionOS, 1.0)).xyz;
}
float3 TransformWorldToObject(float3 positionWS)
{
return mul(GetWorldToObjectMatrix(), float4(positionWS, 1.0)).xyz;
}
real3 TransformObjectToWorldDir(real3 dirOS)
{
// Normalize to support uniform scaling
return normalize(mul((real3x3)GetObjectToWorldMatrix(), dirOS));
}
real3 TransformWorldToObjectDir(real3 dirWS)
{
// Normalize to support uniform scaling
return normalize(mul((real3x3)GetWorldToObjectMatrix(), dirWS));
}
// Transforms normal from object to world space
real3 TransformObjectToWorldNormal(real3 normalOS)
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
return UnityObjectToWorldDir(normalOS);
#else
// Normal need to be multiply by inverse transpose
// mul(IT_M, norm) => mul(norm, I_M) => {dot(norm, I_M.col0), dot(norm, I_M.col1), dot(norm, I_M.col2)}
return normalize(mul(normalOS, (real3x3)GetWorldToObjectMatrix()));
#endif
}
// Transforms position from object space to homogenous space
float4 TransformObjectToHClip(float3 positionOS)
{
// More efficient than computing M*VP matrix product
return mul(GetWorldToHClipMatrix(), mul(GetObjectToWorldMatrix(), float4(positionOS, 1.0)));
}
// Tranforms position from world space to homogenous space
float4 TransformWorldToHClip(float3 positionWS)
{
return mul(GetWorldToHClipMatrix(), float4(positionWS, 1.0));
}
real3x3 CreateWorldToTangent(real3 normal, real3 tangent, real flipSign)
{
// For odd-negative scale transforms we need to flip the sign
real sgn = flipSign * GetOddNegativeScale();
real3 bitangent = cross(normal, tangent) * sgn;
return real3x3(tangent, bitangent, normal);
}
real3 TransformTangentToWorld(real3 dirTS, real3x3 worldToTangent)
{
// Use transpose transformation to go from tangent to world as the matrix is orthogonal
return mul(dirTS, worldToTangent);
}
real3 TransformWorldToTangent(real3 dirWS, real3x3 worldToTangent)
{
return mul(worldToTangent, dirWS);
}
real3 TransformTangentToObject(real3 dirTS, real3x3 worldToTangent)
{
// Use transpose transformation to go from tangent to world as the matrix is orthogonal
real3 normalWS = mul(dirTS, worldToTangent);
return mul((real3x3)GetWorldToObjectMatrix(), normalWS);
}
real3 TransformObjectToTangent(real3 dirOS, real3x3 worldToTangent)
{
return mul(worldToTangent, TransformObjectToWorldDir(dirOS));
}
#endif // UNITY_SHADER_VARIABLES_FUNCTIONS_INCLUDED
#ifndef UNITY_SHADER_VARIABLES_FUNCTIONS_INCLUDED
#define UNITY_SHADER_VARIABLES_FUNCTIONS_INCLUDED
float3 GetCameraPositionWS()
{
return _WorldSpaceCameraPos;
}
float4x4 GetWorldToViewMatrix()
{
return UNITY_MATRIX_V;
}
float4x4 GetObjectToWorldMatrix()
{
return UNITY_MATRIX_M;
}
float4x4 GetWorldToObjectMatrix()
{
return UNITY_MATRIX_I_M;
}
// Transform to homogenous clip space
float4x4 GetWorldToHClipMatrix()
{
return UNITY_MATRIX_VP;
}
real GetOddNegativeScale()
{
return unity_WorldTransformParams.w;
}
float3 TransformWorldToView(float3 positionWS)
{
return mul(GetWorldToViewMatrix(), real4(positionWS, 1.0)).xyz;
}
float3 TransformObjectToWorld(float3 positionOS)
{
return mul(GetObjectToWorldMatrix(), real4(positionOS, 1.0)).xyz;
}
float3 TransformWorldToObject(float3 positionWS)
{
return mul(GetWorldToObjectMatrix(), float4(positionWS, 1.0)).xyz;
}
real3 TransformObjectToWorldDir(real3 dirOS)
{
// Normalize to support uniform scaling
return normalize(mul((real3x3)GetObjectToWorldMatrix(), dirOS));
}
real3 TransformWorldToObjectDir(real3 dirWS)
{
// Normalize to support uniform scaling
return normalize(mul((real3x3)GetWorldToObjectMatrix(), dirWS));
}
// Transforms normal from object to world space
real3 TransformObjectToWorldNormal(real3 normalOS)
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
return UnityObjectToWorldDir(normalOS);
#else
// Normal need to be multiply by inverse transpose
// mul(IT_M, norm) => mul(norm, I_M) => {dot(norm, I_M.col0), dot(norm, I_M.col1), dot(norm, I_M.col2)}
return normalize(mul(normalOS, (real3x3)GetWorldToObjectMatrix()));
#endif
}
// Transforms position from object space to homogenous space
float4 TransformObjectToHClip(float3 positionOS)
{
// More efficient than computing M*VP matrix product
return mul(GetWorldToHClipMatrix(), mul(GetObjectToWorldMatrix(), float4(positionOS, 1.0)));
}
// Tranforms position from world space to homogenous space
float4 TransformWorldToHClip(float3 positionWS)
{
return mul(GetWorldToHClipMatrix(), float4(positionWS, 1.0));
}
real3x3 CreateWorldToTangent(real3 normal, real3 tangent, real flipSign)
{
// For odd-negative scale transforms we need to flip the sign
real sgn = flipSign * GetOddNegativeScale();
real3 bitangent = cross(normal, tangent) * sgn;
return real3x3(tangent, bitangent, normal);
}
real3 TransformTangentToWorld(real3 dirTS, real3x3 worldToTangent)
{
// Use transpose transformation to go from tangent to world as the matrix is orthogonal
return mul(dirTS, worldToTangent);
}
real3 TransformWorldToTangent(real3 dirWS, real3x3 worldToTangent)
{
return mul(worldToTangent, dirWS);
}
real3 TransformTangentToObject(real3 dirTS, real3x3 worldToTangent)
{
// Use transpose transformation to go from tangent to world as the matrix is orthogonal
real3 normalWS = mul(dirTS, worldToTangent);
return mul((real3x3)GetWorldToObjectMatrix(), normalWS);
}
real3 TransformObjectToTangent(real3 dirOS, real3x3 worldToTangent)
{
return mul(worldToTangent, TransformObjectToWorldDir(dirOS));
}
#endif // UNITY_SHADER_VARIABLES_FUNCTIONS_INCLUDED

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

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


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

204
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
//-------------------------------------------------------------------------------------
#define PI 3.14159265358979323846
#define TWO_PI 6.28318530717958647693
float4x4 unity_MatrixVP;
#define DIFFUSION_PROFILE_COUNT (4)
#define DIFFUSION_PROFILE_NEUTRAL_ID (0)
#define SSS_N_SAMPLES (11)
#define SSS_DISTANCE_SCALE (3)
//-------------------------------------------------------------------------------------
// Inputs & outputs
//-------------------------------------------------------------------------------------
float4 _StdDev1, _StdDev2;
float _LerpWeight; // 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 = mul(unity_MatrixVP, float4(input.vertex.xyz, 1)); //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
}
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
//-------------------------------------------------------------------------------------
#define PI 3.14159265358979323846
#define TWO_PI 6.28318530717958647693
float4x4 unity_MatrixVP;
#define DIFFUSION_PROFILE_COUNT (4)
#define DIFFUSION_PROFILE_NEUTRAL_ID (0)
#define SSS_N_SAMPLES (11)
#define SSS_DISTANCE_SCALE (3)
//-------------------------------------------------------------------------------------
// Inputs & outputs
//-------------------------------------------------------------------------------------
float4 _StdDev1, _StdDev2;
float _LerpWeight; // 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 = mul(unity_MatrixVP, float4(input.vertex.xyz, 1)); //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
}

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


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

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


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

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


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

/ScriptableRenderPipeline/LightweightPipeline/LWRP/Editor/DiffusionProfile/Shader → /ScriptableRenderPipeline/LightweightPipeline/LWRP/Shaders/Subsurface/DiffusionProfile

正在加载...
取消
保存