Thomas
7 年前
当前提交
3d4686f1
共有 31 个文件被更改,包括 1132 次插入 和 1858 次删除
-
38ScriptableRenderPipeline/Core/Editor/CoreEditorUtils.cs
-
58ScriptableRenderPipeline/Core/Editor/PropertyFetcher.cs
-
70ScriptableRenderPipeline/HDRenderPipeline/Editor/HDRenderPipelineInspector.cs
-
16ScriptableRenderPipeline/HDRenderPipeline/Editor/HDRenderPipelineMenuItems.cs
-
26ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.cs
-
601ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipelineAsset.asset
-
3ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipelineAsset.cs
-
1ScriptableRenderPipeline/HDRenderPipeline/HDStringConstants.cs
-
42ScriptableRenderPipeline/HDRenderPipeline/Lighting/Editor/HDLightEditor.cs
-
64ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/LitUI.cs
-
5ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/SubsurfaceScatteringSettingsEditor.Styles.cs
-
4ScriptableRenderPipeline/LightweightPipeline/Editor/ShaderGUI/LightweightStandardGUI.cs
-
53ScriptableRenderPipeline/HDRenderPipeline/Editor/HDBaseEditor.cs
-
13ScriptableRenderPipeline/HDRenderPipeline/Editor/HDBaseEditor.cs.meta
-
13ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/SubsurfaceScatteringSettingsEditor.Styles.cs.meta
-
258ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/SubsurfaceScatteringSettingsEditor.cs
-
13ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/SubsurfaceScatteringSettingsEditor.cs.meta
-
85ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SSSProfile/SSS Settings.asset
-
10ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SSSProfile/SSS Settings.asset.meta
-
508ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SubsurfaceScatteringSettings.cs
-
13ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SubsurfaceScatteringSettings.cs.meta
-
13ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/SubsurfaceScatteringProfileEditor.Styles.cs.meta
-
13ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/SubsurfaceScatteringProfileEditor.cs.meta
-
171ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/SubsurfaceScatteringProfileEditor.cs
-
9ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SSSProfile/FoliageSSSProfile.asset.meta
-
9ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SSSProfile/SkinSSSProfile.asset.meta
-
116ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SSSProfile/FoliageSSSProfile.asset
-
116ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SSSProfile/SkinSSSProfile.asset
-
12ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SubsurfaceScatteringProfile.cs.meta
-
637ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SubsurfaceScatteringProfile.cs
-
0/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/SubsurfaceScatteringSettingsEditor.Styles.cs
|
|||
using UnityEngine.Experimental.Rendering; |
|||
using UnityEngine.Experimental.Rendering.HDPipeline; |
|||
|
|||
namespace UnityEditor.Experimental.Rendering.HDPipeline |
|||
{ |
|||
using UnityObject = UnityEngine.Object; |
|||
|
|||
//
|
|||
// Sample use:
|
|||
//
|
|||
// [CustomEditor(typeof(TestComponent)]
|
|||
// class TestEditor : HDBaseEditor<TestComponent>
|
|||
// {
|
|||
// SerializedProperty m_MyFloat;
|
|||
//
|
|||
// protected override void OnEnable()
|
|||
// {
|
|||
// base.OnEnable();
|
|||
// m_MyFloat = properties.Find(x => x.myFloat);
|
|||
// }
|
|||
//
|
|||
// public override void OnInspectorGUI()
|
|||
// {
|
|||
// EditorGUILayout.PropertyField(m_MyFloat);
|
|||
// }
|
|||
// }
|
|||
//
|
|||
public class HDBaseEditor<T> : Editor |
|||
where T : UnityObject |
|||
{ |
|||
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 HDRenderPipeline m_HDPipeline |
|||
{ |
|||
get { return RenderPipelineManager.currentPipeline as HDRenderPipeline; } |
|||
} |
|||
|
|||
protected virtual void OnEnable() |
|||
{ |
|||
properties = new PropertyFetcher<T>(serializedObject); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 4db007e1d2c155d4a80e2a38f39b39ff |
|||
timeCreated: 1507624455 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 95517c3b2f4a014468289c3f5eb6d03c |
|||
timeCreated: 1507562538 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using UnityEngine.Experimental.Rendering; |
|||
using UnityEngine.Experimental.Rendering.HDPipeline; |
|||
|
|||
namespace UnityEditor.Experimental.Rendering.HDPipeline |
|||
{ |
|||
[CustomEditor(typeof(SubsurfaceScatteringSettings))] |
|||
public sealed partial class SubsurfaceScatteringSettingsEditor : HDBaseEditor<SubsurfaceScatteringSettings> |
|||
{ |
|||
sealed class Profile |
|||
{ |
|||
internal SerializedProperty self; |
|||
internal SubsurfaceScatteringProfile objReference; |
|||
|
|||
internal SerializedProperty name; |
|||
|
|||
internal SerializedProperty scatteringDistance; |
|||
internal SerializedProperty transmissionTint; |
|||
internal SerializedProperty texturingMode; |
|||
internal SerializedProperty transmissionMode; |
|||
internal SerializedProperty thicknessRemap; |
|||
internal SerializedProperty worldScale; |
|||
|
|||
// Old SSS Model >>>
|
|||
internal SerializedProperty scatterDistance1; |
|||
internal SerializedProperty scatterDistance2; |
|||
internal SerializedProperty lerpWeight; |
|||
// <<< Old SSS Model
|
|||
|
|||
// 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); |
|||
} |
|||
} |
|||
|
|||
SerializedProperty m_UseDisneySSS; |
|||
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/HDRenderPipeline/DrawSssProfile"); |
|||
m_TransmittanceMaterial = CoreUtils.CreateEngineMaterial("Hidden/HDRenderPipeline/DrawTransmittanceGraph"); |
|||
|
|||
int count = SssConstants.SSS_N_PROFILES - 1; |
|||
m_Profiles = new List<Profile>(); |
|||
|
|||
m_UseDisneySSS = properties.Find(x => x.useDisneySSS); |
|||
var serializedProfiles = properties.Find(x => x.profiles); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
var serializedProfile = serializedProfiles.GetArrayElementAtIndex(i); |
|||
var rp = new RelativePropertyFetcher<SubsurfaceScatteringProfile>(serializedProfile); |
|||
|
|||
var profile = new Profile |
|||
{ |
|||
self = serializedProfile, |
|||
objReference = m_Target.profiles[i], |
|||
|
|||
name = rp.Find(x => x.name), |
|||
|
|||
scatteringDistance = rp.Find(x => x.scatteringDistance), |
|||
transmissionTint = rp.Find(x => x.transmissionTint), |
|||
texturingMode = rp.Find(x => x.texturingMode), |
|||
transmissionMode = rp.Find(x => x.transmissionMode), |
|||
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_HDPipeline == null || m_HDPipeline.sssSettings != m_Target) |
|||
EditorGUILayout.HelpBox("These profiles aren't currently in use, assign this asset to the HD render pipeline asset to use them.", MessageType.Warning); |
|||
|
|||
serializedObject.Update(); |
|||
|
|||
using (var scope = new EditorGUI.ChangeCheckScope()) |
|||
{ |
|||
EditorGUILayout.PropertyField(m_UseDisneySSS, s_Styles.useDisneySSS); |
|||
|
|||
if (scope.changed && m_HDPipeline != null) |
|||
m_HDPipeline.CreateSssMaterials(); |
|||
} |
|||
|
|||
EditorGUILayout.Space(); |
|||
|
|||
if (m_Profiles == null || m_Profiles.Count == 0) |
|||
return; |
|||
|
|||
bool useDisneySSS = m_UseDisneySSS.boolValue; |
|||
|
|||
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()) |
|||
{ |
|||
if (useDisneySSS) |
|||
{ |
|||
EditorGUILayout.PropertyField(profile.scatteringDistance, s_Styles.profileScatteringDistance); |
|||
|
|||
using (new EditorGUI.DisabledScope(true)) |
|||
EditorGUILayout.FloatField(s_Styles.profileMaxRadius, profile.objReference.maxRadius); |
|||
} |
|||
else |
|||
{ |
|||
EditorGUILayout.PropertyField(profile.scatterDistance1, s_Styles.profileScatterDistance1); |
|||
EditorGUILayout.PropertyField(profile.scatterDistance2, s_Styles.profileScatterDistance2); |
|||
EditorGUILayout.PropertyField(profile.lerpWeight, s_Styles.profileLerpWeight); |
|||
} |
|||
|
|||
profile.texturingMode.intValue = EditorGUILayout.Popup(s_Styles.texturingMode, profile.texturingMode.intValue, s_Styles.texturingModeOptions); |
|||
profile.transmissionMode.intValue = EditorGUILayout.Popup(s_Styles.profileTransmissionMode, profile.transmissionMode.intValue, s_Styles.transmissionModeOptions); |
|||
|
|||
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.PropertyField(profile.worldScale, s_Styles.profileWorldScale); |
|||
|
|||
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, useDisneySSS); |
|||
|
|||
EditorGUILayout.Space(); |
|||
EditorGUI.indentLevel--; |
|||
} |
|||
|
|||
profile.self.isExpanded = state; |
|||
} |
|||
|
|||
CoreEditorUtils.DrawSplitter(); |
|||
|
|||
serializedObject.ApplyModifiedProperties(); |
|||
} |
|||
|
|||
void RenderPreview(Profile profile, bool useDisneySSS) |
|||
{ |
|||
var obj = profile.objReference; |
|||
float r = obj.maxRadius; |
|||
var S = obj.shapeParam; |
|||
var T = (Vector4)profile.transmissionTint.colorValue; |
|||
var R = profile.thicknessRemap.vector2Value; |
|||
bool transmissionEnabled = profile.transmissionMode.intValue != (int)SubsurfaceScatteringProfile.TransmissionMode.None; |
|||
|
|||
m_ProfileMaterial.SetFloat(HDShaderIDs._MaxRadius, r); |
|||
m_ProfileMaterial.SetVector(HDShaderIDs._ShapeParam, S); |
|||
|
|||
// Old SSS Model >>>
|
|||
CoreUtils.SelectKeyword(m_ProfileMaterial, "SSS_MODEL_DISNEY", "SSS_MODEL_BASIC", useDisneySSS); |
|||
|
|||
// Apply the three-sigma rule, and rescale.
|
|||
float s = (1f / 3f) * SssConstants.SSS_BASIC_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(HDShaderIDs._StdDev1, stdDev1); |
|||
m_ProfileMaterial.SetVector(HDShaderIDs._StdDev2, stdDev2); |
|||
m_ProfileMaterial.SetFloat(HDShaderIDs._LerpWeight, profile.lerpWeight.floatValue); |
|||
m_ProfileMaterial.SetFloat(HDShaderIDs._MaxRadius, rMax); |
|||
// <<< Old SSS Model
|
|||
|
|||
// Draw the profile.
|
|||
EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(256f, 256f), 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(); |
|||
|
|||
// Old SSS Model >>>
|
|||
// Multiply by 0.1 to convert from millimeters to centimeters. Apply the distance scale.
|
|||
float a = 0.1f * SssConstants.SSS_BASIC_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(HDShaderIDs._HalfRcpVarianceAndWeight1, halfRcpVarianceAndWeight1); |
|||
m_TransmittanceMaterial.SetVector(HDShaderIDs._HalfRcpVarianceAndWeight2, halfRcpVarianceAndWeight2); |
|||
// <<< Old SSS Model
|
|||
|
|||
m_TransmittanceMaterial.SetVector(HDShaderIDs._ShapeParam, S); |
|||
m_TransmittanceMaterial.SetVector(HDShaderIDs._TransmissionTint, transmissionEnabled ? T : Vector4.zero); |
|||
m_TransmittanceMaterial.SetVector(HDShaderIDs._ThicknessRemap, R); |
|||
|
|||
// Draw the transmittance graph.
|
|||
EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(16f, 16f), profile.transmittanceRT, m_TransmittanceMaterial, ScaleMode.ScaleToFit, 16f); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a61a2437dcee4e54393f8f65ccf726ab |
|||
timeCreated: 1507562486 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
%YAML 1.1 |
|||
%TAG !u! tag:unity3d.com,2011: |
|||
--- !u!114 &11400000 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_PrefabParentObject: {fileID: 0} |
|||
m_PrefabInternal: {fileID: 0} |
|||
m_GameObject: {fileID: 0} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 11500000, guid: 3418a08abd15e9a49af3ccbc9e15b5ea, type: 3} |
|||
m_Name: SSS Settings |
|||
m_EditorClassIdentifier: |
|||
useDisneySSS: 1 |
|||
profiles: |
|||
- name: Skin |
|||
scatteringDistance: {r: 0.7568628, g: 0.32156864, b: 0.20000002, a: 1} |
|||
transmissionTint: {r: 0.7568628, g: 0.32156864, b: 0.20000002, a: 1} |
|||
texturingMode: 0 |
|||
transmissionMode: 2 |
|||
thicknessRemap: {x: 0, y: 8.152544} |
|||
worldScale: 1 |
|||
scatterDistance1: {r: 0.3019608, g: 0.20000002, b: 0.20000002, a: 0} |
|||
scatterDistance2: {r: 0.6, g: 0.20000002, b: 0.20000002, a: 0} |
|||
lerpWeight: 0.5 |
|||
- name: Foliage |
|||
scatteringDistance: {r: 0.7568628, g: 0.7019608, b: 0.24313727, a: 1} |
|||
transmissionTint: {r: 1, g: 1, b: 1, a: 1} |
|||
texturingMode: 0 |
|||
transmissionMode: 1 |
|||
thicknessRemap: {x: 0, y: 0.2873168} |
|||
worldScale: 1 |
|||
scatterDistance1: {r: 0.3, g: 0.3, b: 0.3, a: 0} |
|||
scatterDistance2: {r: 0.5, g: 0.5, b: 0.5, a: 0} |
|||
lerpWeight: 0.5 |
|||
- name: Profile 2 |
|||
scatteringDistance: {r: 0.5, g: 0.5, b: 0.5, a: 1} |
|||
transmissionTint: {r: 1, g: 1, b: 1, a: 1} |
|||
texturingMode: 0 |
|||
transmissionMode: 0 |
|||
thicknessRemap: {x: 0, y: 5} |
|||
worldScale: 1 |
|||
scatterDistance1: {r: 0.3, g: 0.3, b: 0.3, a: 0} |
|||
scatterDistance2: {r: 0.5, g: 0.5, b: 0.5, a: 0} |
|||
lerpWeight: 1 |
|||
- name: Profile 3 |
|||
scatteringDistance: {r: 0.5, g: 0.5, b: 0.5, a: 1} |
|||
transmissionTint: {r: 1, g: 1, b: 1, a: 1} |
|||
texturingMode: 0 |
|||
transmissionMode: 0 |
|||
thicknessRemap: {x: 0, y: 5} |
|||
worldScale: 1 |
|||
scatterDistance1: {r: 0.3, g: 0.3, b: 0.3, a: 0} |
|||
scatterDistance2: {r: 0.5, g: 0.5, b: 0.5, a: 0} |
|||
lerpWeight: 1 |
|||
- name: Profile 4 |
|||
scatteringDistance: {r: 0.5, g: 0.5, b: 0.5, a: 1} |
|||
transmissionTint: {r: 1, g: 1, b: 1, a: 1} |
|||
texturingMode: 0 |
|||
transmissionMode: 0 |
|||
thicknessRemap: {x: 0, y: 5} |
|||
worldScale: 1 |
|||
scatterDistance1: {r: 0.3, g: 0.3, b: 0.3, a: 0} |
|||
scatterDistance2: {r: 0.5, g: 0.5, b: 0.5, a: 0} |
|||
lerpWeight: 1 |
|||
- name: Profile 5 |
|||
scatteringDistance: {r: 0.5, g: 0.5, b: 0.5, a: 1} |
|||
transmissionTint: {r: 1, g: 1, b: 1, a: 1} |
|||
texturingMode: 0 |
|||
transmissionMode: 0 |
|||
thicknessRemap: {x: 0, y: 5} |
|||
worldScale: 1 |
|||
scatterDistance1: {r: 0.3, g: 0.3, b: 0.3, a: 0} |
|||
scatterDistance2: {r: 0.5, g: 0.5, b: 0.5, a: 0} |
|||
lerpWeight: 1 |
|||
- name: Profile 6 |
|||
scatteringDistance: {r: 0.5, g: 0.5, b: 0.5, a: 1} |
|||
transmissionTint: {r: 1, g: 1, b: 1, a: 1} |
|||
texturingMode: 0 |
|||
transmissionMode: 0 |
|||
thicknessRemap: {x: 0, y: 5} |
|||
worldScale: 1 |
|||
scatterDistance1: {r: 0.3, g: 0.3, b: 0.3, a: 0} |
|||
scatterDistance2: {r: 0.5, g: 0.5, b: 0.5, a: 0} |
|||
lerpWeight: 1 |
|
|||
fileFormatVersion: 2 |
|||
guid: 873499ce7a6f749408981f512a9683f7 |
|||
timeCreated: 1507649491 |
|||
licenseType: Pro |
|||
NativeFormatImporter: |
|||
externalObjects: {} |
|||
mainObjectFileID: 11400000 |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
|
|||
namespace UnityEngine.Experimental.Rendering.HDPipeline |
|||
{ |
|||
[GenerateHLSL] |
|||
public class SssConstants |
|||
{ |
|||
public const int SSS_N_PROFILES = 8; // Max. number of profiles, including the slot taken by the neutral profile
|
|||
public const int SSS_NEUTRAL_PROFILE_ID = SSS_N_PROFILES - 1; // Does not result in blurring
|
|||
public const int SSS_N_SAMPLES_NEAR_FIELD = 55; // Used for extreme close ups; must be a Fibonacci number
|
|||
public const int SSS_N_SAMPLES_FAR_FIELD = 21; // Used at a regular distance; must be a Fibonacci number
|
|||
public const int SSS_LOD_THRESHOLD = 4; // The LoD threshold of the near-field kernel (in pixels)
|
|||
public const int SSS_TRSM_MODE_NONE = 0; |
|||
public const int SSS_TRSM_MODE_THIN = 1; |
|||
// Old SSS Model >>>
|
|||
public const int SSS_BASIC_N_SAMPLES = 11; // Must be an odd number
|
|||
public const int SSS_BASIC_DISTANCE_SCALE = 3; // SSS distance units per centimeter
|
|||
// <<< Old SSS Model
|
|||
} |
|||
|
|||
[Serializable] |
|||
public sealed class SubsurfaceScatteringProfile |
|||
{ |
|||
public enum TexturingMode : uint |
|||
{ |
|||
PreAndPostScatter = 0, |
|||
PostScatter = 1 |
|||
} |
|||
|
|||
public enum TransmissionMode : uint |
|||
{ |
|||
None = SssConstants.SSS_TRSM_MODE_NONE, |
|||
ThinObject = SssConstants.SSS_TRSM_MODE_THIN, |
|||
Regular |
|||
} |
|||
|
|||
public string name; |
|||
|
|||
[ColorUsage(false, true, 0f, 8f, 0.125f, 3f)] |
|||
public Color scatteringDistance; // Per color channel (no meaningful units)
|
|||
[ColorUsage(false)] |
|||
public Color transmissionTint; // Color, 0 to 1
|
|||
public TexturingMode texturingMode; |
|||
public TransmissionMode transmissionMode; |
|||
public Vector2 thicknessRemap; // X = min, Y = max (in millimeters)
|
|||
public float worldScale; // Size of the world unit in meters
|
|||
|
|||
// Old SSS Model >>>
|
|||
[ColorUsage(false, true, 0f, 8f, 0.125f, 3f)] |
|||
public Color scatterDistance1; |
|||
[ColorUsage(false, true, 0f, 8f, 0.125f, 3f)] |
|||
public Color scatterDistance2; |
|||
[Range(0f, 1f)] |
|||
public float lerpWeight; |
|||
// <<< Old SSS Model
|
|||
|
|||
public Vector3 shapeParam { get; private set; } // RGB = shape parameter: S = 1 / D
|
|||
public float maxRadius { get; private set; } // In millimeters
|
|||
public Vector2[] filterKernelNearField { get; private set; } // X = radius, Y = reciprocal of the PDF
|
|||
public Vector2[] filterKernelFarField { get; private set; } // X = radius, Y = reciprocal of the PDF
|
|||
public Vector4 halfRcpWeightedVariances { get; private set; } |
|||
public Vector4[] filterKernelBasic { get; private set; } |
|||
|
|||
public SubsurfaceScatteringProfile(string name) |
|||
{ |
|||
this.name = name; |
|||
|
|||
scatteringDistance = Color.grey; |
|||
transmissionTint = Color.white; |
|||
texturingMode = TexturingMode.PreAndPostScatter; |
|||
transmissionMode = TransmissionMode.None; |
|||
thicknessRemap = new Vector2(0f, 5f); |
|||
worldScale = 1f; |
|||
|
|||
// Old SSS Model >>>
|
|||
scatterDistance1 = new Color(0.3f, 0.3f, 0.3f, 0f); |
|||
scatterDistance2 = new Color(0.5f, 0.5f, 0.5f, 0f); |
|||
lerpWeight = 1f; |
|||
// <<< Old SSS Model
|
|||
} |
|||
|
|||
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); |
|||
|
|||
// Old SSS Model >>>
|
|||
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 = 0f |
|||
}; |
|||
// <<< Old SSS Model
|
|||
|
|||
UpdateKernel(); |
|||
} |
|||
|
|||
// Ref: Approximate Reflectance Profiles for Efficient Subsurface Scattering by Pixar.
|
|||
public void UpdateKernel() |
|||
{ |
|||
if (filterKernelNearField == null || filterKernelNearField.Length != SssConstants.SSS_N_SAMPLES_NEAR_FIELD) |
|||
filterKernelNearField = new Vector2[SssConstants.SSS_N_SAMPLES_NEAR_FIELD]; |
|||
|
|||
if (filterKernelFarField == null || filterKernelFarField.Length != SssConstants.SSS_N_SAMPLES_FAR_FIELD) |
|||
filterKernelFarField = new Vector2[SssConstants.SSS_N_SAMPLES_FAR_FIELD]; |
|||
|
|||
// Clamp to avoid artifacts.
|
|||
shapeParam = new Vector3( |
|||
1f / Mathf.Max(0.001f, scatteringDistance.r), |
|||
1f / Mathf.Max(0.001f, scatteringDistance.g), |
|||
1f / Mathf.Max(0.001f, scatteringDistance.b) |
|||
); |
|||
|
|||
// We importance sample the color channel with the widest scattering distance.
|
|||
float s = Mathf.Min(shapeParam.x, shapeParam.y, shapeParam.z); |
|||
|
|||
// Importance sample the normalized diffusion profile for the computed value of 's'.
|
|||
// ------------------------------------------------------------------------------------
|
|||
// R(r, s) = s * (Exp[-r * s] + Exp[-r * s / 3]) / (8 * Pi * r)
|
|||
// PDF(r, s) = s * (Exp[-r * s] + Exp[-r * s / 3]) / 4
|
|||
// CDF(r, s) = 1 - 1/4 * Exp[-r * s] - 3/4 * Exp[-r * s / 3]
|
|||
// ------------------------------------------------------------------------------------
|
|||
|
|||
// Importance sample the near field kernel.
|
|||
for (int i = 0, n = SssConstants.SSS_N_SAMPLES_NEAR_FIELD; i < n; i++) |
|||
{ |
|||
float p = (i + 0.5f) * (1f / n); |
|||
float r = KernelCdfInverse(p, s); |
|||
|
|||
// N.b.: computation of normalized weights, and multiplication by the surface albedo
|
|||
// of the actual geometry is performed at runtime (in the shader).
|
|||
filterKernelNearField[i].x = r; |
|||
filterKernelNearField[i].y = 1f / KernelPdf(r, s); |
|||
} |
|||
|
|||
// Importance sample the far field kernel.
|
|||
for (int i = 0, n = SssConstants.SSS_N_SAMPLES_FAR_FIELD; i < n; i++) |
|||
{ |
|||
float p = (i + 0.5f) * (1f / n); |
|||
float r = KernelCdfInverse(p, s); |
|||
|
|||
// N.b.: computation of normalized weights, and multiplication by the surface albedo
|
|||
// of the actual geometry is performed at runtime (in the shader).
|
|||
filterKernelFarField[i].x = r; |
|||
filterKernelFarField[i].y = 1f / KernelPdf(r, s); |
|||
} |
|||
|
|||
maxRadius = filterKernelFarField[SssConstants.SSS_N_SAMPLES_FAR_FIELD - 1].x; |
|||
|
|||
// Old SSS Model >>>
|
|||
UpdateKernelAndVarianceData(); |
|||
// <<< Old SSS Model
|
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
public void UpdateKernelAndVarianceData() |
|||
{ |
|||
const int kNumSamples = SssConstants.SSS_BASIC_N_SAMPLES; |
|||
const int kDistanceScale = SssConstants.SSS_BASIC_DISTANCE_SCALE; |
|||
|
|||
if (filterKernelBasic == null || filterKernelBasic.Length != kNumSamples) |
|||
filterKernelBasic = 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.
|
|||
filterKernelBasic[i].x = val.x * (1 / pdf); |
|||
filterKernelBasic[i].y = val.y * (1 / pdf); |
|||
filterKernelBasic[i].z = val.z * (1 / pdf); |
|||
filterKernelBasic[i].w = pos; |
|||
|
|||
weightSum.x += filterKernelBasic[i].x; |
|||
weightSum.y += filterKernelBasic[i].y; |
|||
weightSum.z += filterKernelBasic[i].z; |
|||
} |
|||
|
|||
// Renormalize the weights to conserve energy.
|
|||
for (int i = 0; i < kNumSamples; i++) |
|||
{ |
|||
filterKernelBasic[i].x *= 1 / weightSum.x; |
|||
filterKernelBasic[i].y *= 1 / weightSum.y; |
|||
filterKernelBasic[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.
|
|||
halfRcpWeightedVariances.Set( |
|||
0.5f / (weightedStdDev.x * weightedStdDev.x), |
|||
0.5f / (weightedStdDev.y * weightedStdDev.y), |
|||
0.5f / (weightedStdDev.z * weightedStdDev.z), |
|||
0.5f / (weightedStdDev.w * weightedStdDev.w) |
|||
); |
|||
} |
|||
// <<< Old SSS Model
|
|||
|
|||
static float KernelVal(float r, float s) |
|||
{ |
|||
return s * (Mathf.Exp(-r * s) + Mathf.Exp(-r * s * (1.0f / 3.0f))) / (8.0f * Mathf.PI * r); |
|||
} |
|||
|
|||
// Computes the value of the integrand over a disk: (2 * PI * r) * KernelVal().
|
|||
static float KernelValCircle(float r, float s) |
|||
{ |
|||
return 0.25f * s * (Mathf.Exp(-r * s) + Mathf.Exp(-r * s * (1.0f / 3.0f))); |
|||
} |
|||
|
|||
static float KernelPdf(float r, float s) |
|||
{ |
|||
return KernelValCircle(r, s); |
|||
} |
|||
|
|||
static float KernelCdf(float r, float s) |
|||
{ |
|||
return 1.0f - 0.25f * Mathf.Exp(-r * s) - 0.75f * Mathf.Exp(-r * s * (1.0f / 3.0f)); |
|||
} |
|||
|
|||
static float KernelCdfDerivative1(float r, float s) |
|||
{ |
|||
return 0.25f * s * Mathf.Exp(-r * s) * (1.0f + Mathf.Exp(r * s * (2.0f / 3.0f))); |
|||
} |
|||
|
|||
static float KernelCdfDerivative2(float r, float s) |
|||
{ |
|||
return (-1.0f / 12.0f) * s * s * Mathf.Exp(-r * s) * (3.0f + Mathf.Exp(r * s * (2.0f / 3.0f))); |
|||
} |
|||
|
|||
// The CDF is not analytically invertible, so we use Halley's Method of root finding.
|
|||
// { f(r, s, p) = CDF(r, s) - p = 0 } with the initial guess { r = (10^p - 1) / s }.
|
|||
static float KernelCdfInverse(float p, float s) |
|||
{ |
|||
// Supply the initial guess.
|
|||
float r = (Mathf.Pow(10f, p) - 1f) / s; |
|||
float t = float.MaxValue; |
|||
|
|||
while (true) |
|||
{ |
|||
float f0 = KernelCdf(r, s) - p; |
|||
float f1 = KernelCdfDerivative1(r, s); |
|||
float f2 = KernelCdfDerivative2(r, s); |
|||
float dr = f0 / (f1 * (1f - f0 * f2 / (2f * f1 * f1))); |
|||
|
|||
if (Mathf.Abs(dr) < t) |
|||
{ |
|||
r = r - dr; |
|||
t = Mathf.Abs(dr); |
|||
} |
|||
else |
|||
{ |
|||
// Converged to the best result.
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
return r; |
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
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); |
|||
} |
|||
// <<< Old SSS Model
|
|||
} |
|||
|
|||
public sealed class SubsurfaceScatteringSettings : ScriptableObject |
|||
{ |
|||
public bool useDisneySSS = true; |
|||
public SubsurfaceScatteringProfile[] profiles; |
|||
|
|||
[NonSerialized] public int texturingModeFlags; // 1 bit/profile; 0 = PreAndPostScatter, 1 = PostScatter
|
|||
[NonSerialized] public int transmissionFlags; // 2 bit/profile; 0 = inf. thick, 1 = thin, 2 = regular
|
|||
[NonSerialized] public Vector4[] thicknessRemaps; // Remap: 0 = start, 1 = end - start
|
|||
[NonSerialized] public Vector4[] worldScales; // Size of the world unit in meters (only the X component is used)
|
|||
[NonSerialized] public Vector4[] shapeParams; // RGB = S = 1 / D, A = filter radius
|
|||
[NonSerialized] public Vector4[] transmissionTints; // RGB = color, A = unused
|
|||
[NonSerialized] public Vector4[] filterKernels; // XY = near field, ZW = far field; 0 = radius, 1 = reciprocal of the PDF
|
|||
|
|||
// Old SSS Model >>>
|
|||
[NonSerialized] public Vector4[] halfRcpWeightedVariances; |
|||
[NonSerialized] public Vector4[] halfRcpVariancesAndWeights; |
|||
[NonSerialized] public Vector4[] filterKernelsBasic; |
|||
// <<< Old SSS Model
|
|||
|
|||
public SubsurfaceScatteringProfile this[int index] |
|||
{ |
|||
get |
|||
{ |
|||
if (index >= SssConstants.SSS_N_PROFILES - 1) |
|||
throw new IndexOutOfRangeException("index"); |
|||
|
|||
return profiles[index]; |
|||
} |
|||
} |
|||
|
|||
void OnEnable() |
|||
{ |
|||
if (profiles != null && profiles.Length != SssConstants.SSS_NEUTRAL_PROFILE_ID) |
|||
Array.Resize(ref profiles, SssConstants.SSS_NEUTRAL_PROFILE_ID); |
|||
|
|||
if (profiles == null) |
|||
profiles = new SubsurfaceScatteringProfile[SssConstants.SSS_NEUTRAL_PROFILE_ID]; |
|||
|
|||
for (int i = 0; i < SssConstants.SSS_NEUTRAL_PROFILE_ID; i++) |
|||
{ |
|||
if (profiles[i] == null) |
|||
profiles[i] = new SubsurfaceScatteringProfile("Profile " + i); |
|||
|
|||
profiles[i].Validate(); |
|||
} |
|||
|
|||
ValidateArray(ref thicknessRemaps, SssConstants.SSS_N_PROFILES); |
|||
ValidateArray(ref worldScales, SssConstants.SSS_N_PROFILES); |
|||
ValidateArray(ref shapeParams, SssConstants.SSS_N_PROFILES); |
|||
ValidateArray(ref transmissionTints, SssConstants.SSS_N_PROFILES); |
|||
ValidateArray(ref filterKernels, SssConstants.SSS_N_PROFILES * SssConstants.SSS_N_SAMPLES_NEAR_FIELD); |
|||
|
|||
// Old SSS Model >>>
|
|||
ValidateArray(ref halfRcpWeightedVariances, SssConstants.SSS_N_PROFILES); |
|||
ValidateArray(ref halfRcpVariancesAndWeights, SssConstants.SSS_N_PROFILES * 2); |
|||
ValidateArray(ref filterKernelsBasic, SssConstants.SSS_N_PROFILES * SssConstants.SSS_BASIC_N_SAMPLES); |
|||
|
|||
Debug.Assert(SssConstants.SSS_NEUTRAL_PROFILE_ID < 16, "Transmission flags (32-bit integer) cannot support more than 16 profiles."); |
|||
|
|||
UpdateCache(); |
|||
} |
|||
|
|||
static void ValidateArray<T>(ref T[] array, int len) |
|||
{ |
|||
if (array == null || array.Length != len) |
|||
array = new T[len]; |
|||
} |
|||
|
|||
public void UpdateCache() |
|||
{ |
|||
texturingModeFlags = transmissionFlags = 0; |
|||
|
|||
for (int i = 0; i < SssConstants.SSS_N_PROFILES - 1; i++) |
|||
{ |
|||
UpdateCache(i); |
|||
} |
|||
|
|||
// Fill the neutral profile.
|
|||
int neutralId = SssConstants.SSS_NEUTRAL_PROFILE_ID; |
|||
|
|||
worldScales[neutralId] = Vector4.one; |
|||
shapeParams[neutralId] = Vector4.zero; |
|||
|
|||
for (int j = 0, n = SssConstants.SSS_N_SAMPLES_NEAR_FIELD; j < n; j++) |
|||
{ |
|||
filterKernels[n * neutralId + j].x = 0f; |
|||
filterKernels[n * neutralId + j].y = 1f; |
|||
filterKernels[n * neutralId + j].z = 0f; |
|||
filterKernels[n * neutralId + j].w = 1f; |
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
halfRcpWeightedVariances[neutralId] = Vector4.one; |
|||
|
|||
for (int j = 0, n = SssConstants.SSS_BASIC_N_SAMPLES; j < n; j++) |
|||
{ |
|||
filterKernelsBasic[n * neutralId + j] = Vector4.one; |
|||
filterKernelsBasic[n * neutralId + j].w = 0f; |
|||
} |
|||
// <<< Old SSS Model
|
|||
} |
|||
|
|||
public void UpdateCache(int i) |
|||
{ |
|||
texturingModeFlags |= (int)profiles[i].texturingMode << i; |
|||
transmissionFlags |= (int)profiles[i].transmissionMode << i * 2; |
|||
|
|||
thicknessRemaps[i] = new Vector4(profiles[i].thicknessRemap.x, profiles[i].thicknessRemap.y - profiles[i].thicknessRemap.x, 0f, 0f); |
|||
worldScales[i] = new Vector4(profiles[i].worldScale, 0f, 0f, 0f); |
|||
shapeParams[i] = profiles[i].shapeParam; |
|||
shapeParams[i].w = profiles[i].maxRadius; |
|||
transmissionTints[i] = profiles[i].transmissionTint * 0.25f; // Premultiplied
|
|||
|
|||
for (int j = 0, n = SssConstants.SSS_N_SAMPLES_NEAR_FIELD; j < n; j++) |
|||
{ |
|||
filterKernels[n * i + j].x = profiles[i].filterKernelNearField[j].x; |
|||
filterKernels[n * i + j].y = profiles[i].filterKernelNearField[j].y; |
|||
|
|||
if (j < SssConstants.SSS_N_SAMPLES_FAR_FIELD) |
|||
{ |
|||
filterKernels[n * i + j].z = profiles[i].filterKernelFarField[j].x; |
|||
filterKernels[n * i + j].w = profiles[i].filterKernelFarField[j].y; |
|||
} |
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
halfRcpWeightedVariances[i] = profiles[i].halfRcpWeightedVariances; |
|||
|
|||
var stdDev1 = ((1f / 3f) * SssConstants.SSS_BASIC_DISTANCE_SCALE) * (Vector4)profiles[i].scatterDistance1; |
|||
var stdDev2 = ((1f / 3f) * SssConstants.SSS_BASIC_DISTANCE_SCALE) * (Vector4)profiles[i].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 * SssConstants.SSS_BASIC_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[i].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[i].lerpWeight); |
|||
|
|||
for (int j = 0, n = SssConstants.SSS_BASIC_N_SAMPLES; j < n; j++) |
|||
{ |
|||
filterKernelsBasic[n * i + j] = profiles[i].filterKernelBasic[j]; |
|||
} |
|||
// <<< Old SSS Model
|
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 3418a08abd15e9a49af3ccbc9e15b5ea |
|||
timeCreated: 1507538042 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: fcb1bb4049093ff41855e3951f22e9cf |
|||
timeCreated: 1507119569 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 4e87e719e85a91846ad1d5aa15793685 |
|||
timeCreated: 1507118957 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using UnityEngine.Experimental.Rendering; |
|||
using UnityEngine.Experimental.Rendering.HDPipeline; |
|||
|
|||
namespace UnityEditor.Experimental.Rendering.HDPipeline |
|||
{ |
|||
[CustomEditor(typeof(SubsurfaceScatteringProfile))] |
|||
public partial class SubsurfaceScatteringProfileEditor : Editor |
|||
{ |
|||
RenderTexture m_ProfileImage; |
|||
RenderTexture m_TransmittanceImage; |
|||
Material m_ProfileMaterial; |
|||
Material m_TransmittanceMaterial; |
|||
SerializedProperty m_ScatteringDistance; |
|||
SerializedProperty m_MaxRadius; |
|||
SerializedProperty m_ShapeParam; |
|||
SerializedProperty m_TransmissionTint; |
|||
SerializedProperty m_TexturingMode; |
|||
SerializedProperty m_TransmissionMode; |
|||
SerializedProperty m_ThicknessRemap; |
|||
SerializedProperty m_WorldScale; |
|||
|
|||
// Old SSS Model >>>
|
|||
SerializedProperty m_ScatterDistance1; |
|||
SerializedProperty m_ScatterDistance2; |
|||
SerializedProperty m_LerpWeight; |
|||
// <<< Old SSS Model
|
|||
|
|||
void OnEnable() |
|||
{ |
|||
using (var o = new PropertyFetcher<SubsurfaceScatteringProfile>(serializedObject)) |
|||
{ |
|||
m_ScatteringDistance = o.FindProperty(x => x.scatteringDistance); |
|||
m_MaxRadius = o.FindProperty("m_MaxRadius"); |
|||
m_ShapeParam = o.FindProperty("m_ShapeParam"); |
|||
m_TransmissionTint = o.FindProperty(x => x.transmissionTint); |
|||
m_TexturingMode = o.FindProperty(x => x.texturingMode); |
|||
m_TransmissionMode = o.FindProperty(x => x.transmissionMode); |
|||
m_ThicknessRemap = o.FindProperty(x => x.thicknessRemap); |
|||
m_WorldScale = o.FindProperty(x => x.worldScale); |
|||
// Old SSS Model >>>
|
|||
m_ScatterDistance1 = o.FindProperty(x => x.scatterDistance1); |
|||
m_ScatterDistance2 = o.FindProperty(x => x.scatterDistance2); |
|||
m_LerpWeight = o.FindProperty(x => x.lerpWeight); |
|||
// <<< Old SSS Model
|
|||
} |
|||
|
|||
// These shaders don't need to be reference by RenderPipelineResource as they are not use at runtime
|
|||
m_ProfileMaterial = CoreUtils.CreateEngineMaterial("Hidden/HDRenderPipeline/DrawSssProfile"); |
|||
m_TransmittanceMaterial = CoreUtils.CreateEngineMaterial("Hidden/HDRenderPipeline/DrawTransmittanceGraph"); |
|||
|
|||
m_ProfileImage = new RenderTexture(256, 256, 0, RenderTextureFormat.DefaultHDR); |
|||
m_TransmittanceImage = new RenderTexture( 16, 256, 0, RenderTextureFormat.DefaultHDR); |
|||
} |
|||
|
|||
void OnDisable() |
|||
{ |
|||
CoreUtils.Destroy(m_ProfileMaterial); |
|||
CoreUtils.Destroy(m_TransmittanceMaterial); |
|||
CoreUtils.Destroy(m_ProfileImage); |
|||
CoreUtils.Destroy(m_TransmittanceImage); |
|||
} |
|||
|
|||
public override void OnInspectorGUI() |
|||
{ |
|||
var hdPipeline = RenderPipelineManager.currentPipeline as HDRenderPipeline; |
|||
|
|||
if (hdPipeline == null) |
|||
return; |
|||
|
|||
serializedObject.Update(); |
|||
CheckStyles(); |
|||
|
|||
// Old SSS Model >>>
|
|||
bool useDisneySSS = hdPipeline.sssSettings.useDisneySSS; |
|||
// <<< Old SSS Model
|
|||
|
|||
using (var scope = new EditorGUI.ChangeCheckScope()) |
|||
{ |
|||
if (useDisneySSS) |
|||
{ |
|||
EditorGUILayout.PropertyField(m_ScatteringDistance, s_Styles.profileScatteringDistance); |
|||
|
|||
using (new EditorGUI.DisabledScope(true)) |
|||
EditorGUILayout.PropertyField(m_MaxRadius, s_Styles.profileMaxRadius); |
|||
} |
|||
else |
|||
{ |
|||
EditorGUILayout.PropertyField(m_ScatterDistance1, s_Styles.profileScatterDistance1); |
|||
EditorGUILayout.PropertyField(m_ScatterDistance2, s_Styles.profileScatterDistance2); |
|||
EditorGUILayout.PropertyField(m_LerpWeight, s_Styles.profileLerpWeight); |
|||
} |
|||
|
|||
m_TexturingMode.intValue = EditorGUILayout.Popup(s_Styles.texturingMode, m_TexturingMode.intValue, s_Styles.texturingModeOptions); |
|||
m_TransmissionMode.intValue = EditorGUILayout.Popup(s_Styles.profileTransmissionMode, m_TransmissionMode.intValue, s_Styles.transmissionModeOptions); |
|||
|
|||
EditorGUILayout.PropertyField(m_TransmissionTint, s_Styles.profileTransmissionTint); |
|||
EditorGUILayout.PropertyField(m_ThicknessRemap, s_Styles.profileMinMaxThickness); |
|||
Vector2 thicknessRemap = m_ThicknessRemap.vector2Value; |
|||
EditorGUILayout.MinMaxSlider(s_Styles.profileThicknessRemap, ref thicknessRemap.x, ref thicknessRemap.y, 0.0f, 50.0f); |
|||
m_ThicknessRemap.vector2Value = thicknessRemap; |
|||
EditorGUILayout.PropertyField(m_WorldScale, s_Styles.profileWorldScale); |
|||
|
|||
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 each individual asset and update caches.
|
|||
((SubsurfaceScatteringProfile)target).Validate(); |
|||
hdPipeline.sssSettings.UpdateCache(); |
|||
} |
|||
} |
|||
|
|||
float r = m_MaxRadius.floatValue; |
|||
Vector3 S = m_ShapeParam.vector3Value; |
|||
Vector4 T = m_TransmissionTint.colorValue; |
|||
Vector2 R = m_ThicknessRemap.vector2Value; |
|||
bool transmissionEnabled = m_TransmissionMode.intValue != (int)SubsurfaceScatteringProfile.TransmissionMode.None; |
|||
|
|||
m_ProfileMaterial.SetFloat(HDShaderIDs._MaxRadius, r); |
|||
m_ProfileMaterial.SetVector(HDShaderIDs._ShapeParam, S); |
|||
|
|||
// Old SSS Model >>>
|
|||
CoreUtils.SelectKeyword(m_ProfileMaterial, "SSS_MODEL_DISNEY", "SSS_MODEL_BASIC", useDisneySSS); |
|||
|
|||
// Apply the three-sigma rule, and rescale.
|
|||
float s = (1.0f / 3.0f) * SssConstants.SSS_BASIC_DISTANCE_SCALE; |
|||
float rMax = Mathf.Max(m_ScatterDistance1.colorValue.r, m_ScatterDistance1.colorValue.g, m_ScatterDistance1.colorValue.b, |
|||
m_ScatterDistance2.colorValue.r, m_ScatterDistance2.colorValue.g, m_ScatterDistance2.colorValue.b); |
|||
Vector4 stdDev1 = s * m_ScatterDistance1.colorValue; |
|||
Vector4 stdDev2 = s * m_ScatterDistance2.colorValue; |
|||
m_ProfileMaterial.SetVector(HDShaderIDs._StdDev1, stdDev1); |
|||
m_ProfileMaterial.SetVector(HDShaderIDs._StdDev2, stdDev2); |
|||
m_ProfileMaterial.SetFloat(HDShaderIDs._LerpWeight, m_LerpWeight.floatValue); |
|||
m_ProfileMaterial.SetFloat(HDShaderIDs._MaxRadius, rMax); |
|||
// <<< Old SSS Model
|
|||
|
|||
// Draw the profile.
|
|||
EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(256, 256), m_ProfileImage, m_ProfileMaterial, ScaleMode.ScaleToFit, 1.0f); |
|||
|
|||
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(); |
|||
|
|||
// Old SSS Model >>>
|
|||
// Multiply by 0.1 to convert from millimeters to centimeters. Apply the distance scale.
|
|||
float a = 0.1f * SssConstants.SSS_BASIC_DISTANCE_SCALE; |
|||
Vector4 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), 4 * (1.0f - m_LerpWeight.floatValue)); |
|||
Vector4 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), 4 * m_LerpWeight.floatValue); |
|||
m_TransmittanceMaterial.SetVector(HDShaderIDs._HalfRcpVarianceAndWeight1, halfRcpVarianceAndWeight1); |
|||
m_TransmittanceMaterial.SetVector(HDShaderIDs._HalfRcpVarianceAndWeight2, halfRcpVarianceAndWeight2); |
|||
// <<< Old SSS Model
|
|||
|
|||
m_TransmittanceMaterial.SetVector(HDShaderIDs._ShapeParam, S); |
|||
m_TransmittanceMaterial.SetVector(HDShaderIDs._TransmissionTint, transmissionEnabled ? T : Vector4.zero); |
|||
m_TransmittanceMaterial.SetVector(HDShaderIDs._ThicknessRemap, R); |
|||
|
|||
// Draw the transmittance graph.
|
|||
EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(16, 16), m_TransmittanceImage, m_TransmittanceMaterial, ScaleMode.ScaleToFit, 16.0f); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d6ee4403015766f4093158d69216c0bf |
|||
timeCreated: 1493161911 |
|||
licenseType: Pro |
|||
NativeFormatImporter: |
|||
mainObjectFileID: 11400000 |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 906339bac2066fc4aa22a3652e1283ef |
|||
timeCreated: 1493291209 |
|||
licenseType: Pro |
|||
NativeFormatImporter: |
|||
mainObjectFileID: 11400000 |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
%YAML 1.1 |
|||
%TAG !u! tag:unity3d.com,2011: |
|||
--- !u!114 &11400000 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_PrefabParentObject: {fileID: 0} |
|||
m_PrefabInternal: {fileID: 0} |
|||
m_GameObject: {fileID: 0} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 11500000, guid: a6e7465350bf0d248b4799d98e18cd24, type: 3} |
|||
m_Name: FoliageSSSProfile |
|||
m_EditorClassIdentifier: |
|||
scatteringDistance: {r: 0.75735295, g: 0.7008203, b: 0.24502596, a: 1} |
|||
transmissionTint: {r: 1, g: 1, b: 1, a: 1} |
|||
texturingMode: 0 |
|||
transmissionMode: 1 |
|||
thicknessRemap: {x: 0, y: 0.2873168} |
|||
worldScale: 1 |
|||
settingsIndex: 1 |
|||
m_ShapeParam: {x: 1.3203883, y: 1.4268992, z: 4.0812} |
|||
m_MaxRadius: 7.839332 |
|||
m_FilterKernelNearField: |
|||
- {x: 0.013854082, y: 1.5332626} |
|||
- {x: 0.04207757, y: 1.5715919} |
|||
- {x: 0.07101321, y: 1.611625} |
|||
- {x: 0.10069309, y: 1.653469} |
|||
- {x: 0.13115102, y: 1.6972395} |
|||
- {x: 0.16242325, y: 1.743061} |
|||
- {x: 0.19454831, y: 1.791069} |
|||
- {x: 0.22756726, y: 1.8414105} |
|||
- {x: 0.2615238, y: 1.8942443} |
|||
- {x: 0.29646498, y: 1.9497447} |
|||
- {x: 0.33244094, y: 2.0081} |
|||
- {x: 0.36950532, y: 2.069517} |
|||
- {x: 0.40771604, y: 2.1342213} |
|||
- {x: 0.44713485, y: 2.2024598} |
|||
- {x: 0.48782852, y: 2.274504} |
|||
- {x: 0.5298689, y: 2.3506513} |
|||
- {x: 0.5733336, y: 2.4312308} |
|||
- {x: 0.6183063, y: 2.5166042} |
|||
- {x: 0.66487795, y: 2.6071734} |
|||
- {x: 0.71314675, y: 2.703383} |
|||
- {x: 0.76321995, y: 2.805728} |
|||
- {x: 0.81521386, y: 2.9147599} |
|||
- {x: 0.86925554, y: 3.031096} |
|||
- {x: 0.92548406, y: 3.1554284} |
|||
- {x: 0.9840515, y: 3.288537} |
|||
- {x: 1.0451256, y: 3.431302} |
|||
- {x: 1.1088905, y: 3.5847225} |
|||
- {x: 1.1755506, y: 3.749937} |
|||
- {x: 1.2453312, y: 3.9282467} |
|||
- {x: 1.3184844, y: 4.1211514} |
|||
- {x: 1.3952904, y: 4.3303804} |
|||
- {x: 1.4760638, y: 4.5579505} |
|||
- {x: 1.5611593, y: 4.8062224} |
|||
- {x: 1.650978, y: 5.0779796} |
|||
- {x: 1.7459753, y: 5.3765287} |
|||
- {x: 1.8466738, y: 5.7058387} |
|||
- {x: 1.9536757, y: 6.070716} |
|||
- {x: 2.0676782, y: 6.4770336} |
|||
- {x: 2.189499, y: 6.9320674} |
|||
- {x: 2.3201032, y: 7.4449396} |
|||
- {x: 2.4606442, y: 8.027264} |
|||
- {x: 2.612515, y: 8.694056} |
|||
- {x: 2.777422, y: 9.465107} |
|||
- {x: 2.9574924, y: 10.367077} |
|||
- {x: 3.1554203, y: 11.436736} |
|||
- {x: 3.3747015, y: 12.726331} |
|||
- {x: 3.6199868, y: 14.312694} |
|||
- {x: 3.897662, y: 16.313623} |
|||
- {x: 4.2168484, y: 18.919455} |
|||
- {x: 4.5912457, y: 22.459015} |
|||
- {x: 5.042839, y: 27.553566} |
|||
- {x: 5.610336, y: 35.534466} |
|||
- {x: 6.3721733, y: 49.863647} |
|||
- {x: 7.53103, y: 83.235695} |
|||
- {x: 10.026244, y: 249.90146} |
|||
m_FilterKernelFarField: |
|||
- {x: 0.036647703, y: 1.5641627} |
|||
- {x: 0.113649175, y: 1.671985} |
|||
- {x: 0.19610003, y: 1.7934121} |
|||
- {x: 0.28470552, y: 1.9309369} |
|||
- {x: 0.38030306, y: 2.0876582} |
|||
- {x: 0.48389605, y: 2.2674706} |
|||
- {x: 0.5966971, y: 2.4753277} |
|||
- {x: 0.72018707, y: 2.717616} |
|||
- {x: 0.85619545, y: 3.0026984} |
|||
- {x: 1.0070126, y: 3.3417335} |
|||
- {x: 1.1755506, y: 3.749937} |
|||
- {x: 1.3655809, y: 4.248627} |
|||
- {x: 1.5821002, y: 4.868682} |
|||
- {x: 1.8319175, y: 5.6567397} |
|||
- {x: 2.1246579, y: 6.6871305} |
|||
- {x: 2.4745958, y: 8.086858} |
|||
- {x: 2.9043517, y: 10.094101} |
|||
- {x: 3.4533012, y: 13.217383} |
|||
- {x: 4.20053, y: 18.777578} |
|||
- {x: 5.349281, y: 31.61934} |
|||
- {x: 7.839332, y: 95.36225} |
|||
scatterDistance1: {r: 0.3, g: 0.3, b: 0.3, a: 0} |
|||
scatterDistance2: {r: 0.6, g: 0.6, b: 0.6, a: 0} |
|||
lerpWeight: 0.5 |
|||
m_HalfRcpWeightedVariances: {x: 2.4691355, y: 2.4691355, z: 2.4691355, w: 2.4691355} |
|||
m_FilterKernelBasic: |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: -0.000000048879357} |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: -0.11381993} |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: -0.23580086} |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: -0.37865555} |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: -0.57677805} |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: -1.3907351} |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: 0.11382001} |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: 0.23580086} |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: 0.37865555} |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: 0.57677805} |
|||
- {x: 0.09090909, y: 0.09090909, z: 0.09090909, w: 1.3907368} |
|
|||
%YAML 1.1 |
|||
%TAG !u! tag:unity3d.com,2011: |
|||
--- !u!114 &11400000 |
|||
MonoBehaviour: |
|||
m_ObjectHideFlags: 0 |
|||
m_PrefabParentObject: {fileID: 0} |
|||
m_PrefabInternal: {fileID: 0} |
|||
m_GameObject: {fileID: 0} |
|||
m_Enabled: 1 |
|||
m_EditorHideFlags: 0 |
|||
m_Script: {fileID: 11500000, guid: a6e7465350bf0d248b4799d98e18cd24, type: 3} |
|||
m_Name: SkinSSSProfile |
|||
m_EditorClassIdentifier: |
|||
scatteringDistance: {r: 0.758, g: 0.321, b: 0.201, a: 1} |
|||
transmissionTint: {r: 0.7568628, g: 0.32156864, b: 0.20000002, a: 1} |
|||
texturingMode: 0 |
|||
transmissionMode: 2 |
|||
thicknessRemap: {x: 0, y: 8.152544} |
|||
worldScale: 1 |
|||
settingsIndex: 0 |
|||
m_ShapeParam: {x: 1.3192612, y: 3.1152647, z: 4.9751244} |
|||
m_MaxRadius: 7.84603 |
|||
m_FilterKernelNearField: |
|||
- {x: 0.013865918, y: 1.5345725} |
|||
- {x: 0.04211352, y: 1.5729346} |
|||
- {x: 0.07107388, y: 1.6130018} |
|||
- {x: 0.100779116, y: 1.6548817} |
|||
- {x: 0.13126306, y: 1.6986896} |
|||
- {x: 0.16256203, y: 1.7445502} |
|||
- {x: 0.1947145, y: 1.7925992} |
|||
- {x: 0.22776169, y: 1.8429838} |
|||
- {x: 0.26174724, y: 1.8958628} |
|||
- {x: 0.29671827, y: 1.9514104} |
|||
- {x: 0.33272493, y: 2.0098157} |
|||
- {x: 0.369821, y: 2.0712852} |
|||
- {x: 0.40806437, y: 2.1360447} |
|||
- {x: 0.44751683, y: 2.2043417} |
|||
- {x: 0.48824534, y: 2.276447} |
|||
- {x: 0.5303216, y: 2.3526597} |
|||
- {x: 0.5738235, y: 2.433308} |
|||
- {x: 0.61883456, y: 2.5187542} |
|||
- {x: 0.665446, y: 2.609401} |
|||
- {x: 0.713756, y: 2.7056925} |
|||
- {x: 0.763872, y: 2.808125} |
|||
- {x: 0.8159103, y: 2.91725} |
|||
- {x: 0.8699981, y: 3.0336854} |
|||
- {x: 0.9262747, y: 3.1581244} |
|||
- {x: 0.98489225, y: 3.2913465} |
|||
- {x: 1.0460185, y: 3.434234} |
|||
- {x: 1.1098381, y: 3.5877857} |
|||
- {x: 1.1765549, y: 3.7531407} |
|||
- {x: 1.2463952, y: 3.9316032} |
|||
- {x: 1.319611, y: 4.124672} |
|||
- {x: 1.3964823, y: 4.3340797} |
|||
- {x: 1.4773248, y: 4.561845} |
|||
- {x: 1.5624928, y: 4.810328} |
|||
- {x: 1.6523882, y: 5.082317} |
|||
- {x: 1.747467, y: 5.381122} |
|||
- {x: 1.8482518, y: 5.710715} |
|||
- {x: 1.9553448, y: 6.0759025} |
|||
- {x: 2.069445, y: 6.482568} |
|||
- {x: 2.1913698, y: 6.9379897} |
|||
- {x: 2.3220856, y: 7.4513016} |
|||
- {x: 2.4627466, y: 8.0341215} |
|||
- {x: 2.614747, y: 8.701484} |
|||
- {x: 2.7797952, y: 9.473195} |
|||
- {x: 2.9600194, y: 10.375936} |
|||
- {x: 3.1581159, y: 11.446505} |
|||
- {x: 3.3775842, y: 12.737201} |
|||
- {x: 3.6230793, y: 14.324919} |
|||
- {x: 3.9009922, y: 16.327562} |
|||
- {x: 4.220452, y: 18.935623} |
|||
- {x: 4.5951686, y: 22.478209} |
|||
- {x: 5.047148, y: 27.57712} |
|||
- {x: 5.615129, y: 35.564823} |
|||
- {x: 6.3776174, y: 49.90625} |
|||
- {x: 7.5374603, y: 83.30668} |
|||
- {x: 10.03481, y: 250.11497} |
|||
m_FilterKernelFarField: |
|||
- {x: 0.03667902, y: 1.5654991} |
|||
- {x: 0.11374626, y: 1.6734134} |
|||
- {x: 0.19626758, y: 1.7949444} |
|||
- {x: 0.28494877, y: 1.9325867} |
|||
- {x: 0.38062802, y: 2.0894418} |
|||
- {x: 0.48430943, y: 2.2694077} |
|||
- {x: 0.59720695, y: 2.4774427} |
|||
- {x: 0.72080237, y: 2.7199378} |
|||
- {x: 0.8569269, y: 3.005264} |
|||
- {x: 1.007873, y: 3.3445888} |
|||
- {x: 1.1765549, y: 3.7531407} |
|||
- {x: 1.3667475, y: 4.252257} |
|||
- {x: 1.5834517, y: 4.872841} |
|||
- {x: 1.8334827, y: 5.6615734} |
|||
- {x: 2.126473, y: 6.692844} |
|||
- {x: 2.4767098, y: 8.093767} |
|||
- {x: 2.9068332, y: 10.102725} |
|||
- {x: 3.4562516, y: 13.228675} |
|||
- {x: 4.2041187, y: 18.793621} |
|||
- {x: 5.3538504, y: 31.646347} |
|||
- {x: 7.84603, y: 95.44374} |
|||
scatterDistance1: {r: 0.3, g: 0.2, b: 0.2, a: 0} |
|||
scatterDistance2: {r: 0.6, g: 0.2, b: 0.2, a: 0} |
|||
lerpWeight: 0.5 |
|||
m_HalfRcpWeightedVariances: {x: 2.4691355, y: 12.5, z: 12.5, w: 2.4691355} |
|||
m_FilterKernelBasic: |
|||
- {x: 0.09090909, y: 0.21162307, z: 0.21162307, w: -0.000000048879357} |
|||
- {x: 0.09090909, y: 0.18990478, z: 0.18990478, w: -0.11381993} |
|||
- {x: 0.09090909, y: 0.13233659, z: 0.13233659, w: -0.23580086} |
|||
- {x: 0.09090909, y: 0.061445467, z: 0.061445467, w: -0.37865555} |
|||
- {x: 0.09090909, y: 0.010501689, z: 0.010501689, w: -0.57677805} |
|||
- {x: 0.09090909, y: 2.9458339e-10, z: 2.9458339e-10, w: -1.3907351} |
|||
- {x: 0.09090909, y: 0.18990476, z: 0.18990476, w: 0.11382001} |
|||
- {x: 0.09090909, y: 0.13233659, z: 0.13233659, w: 0.23580086} |
|||
- {x: 0.09090909, y: 0.061445467, z: 0.061445467, w: 0.37865555} |
|||
- {x: 0.09090909, y: 0.010501689, z: 0.010501689, w: 0.57677805} |
|||
- {x: 0.09090909, y: 2.9456845e-10, z: 2.9456845e-10, w: 1.3907368} |
|
|||
fileFormatVersion: 2 |
|||
guid: a6e7465350bf0d248b4799d98e18cd24 |
|||
timeCreated: 1490016484 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
|
|||
namespace UnityEngine.Experimental.Rendering.HDPipeline |
|||
{ |
|||
[GenerateHLSL] |
|||
public class SssConstants |
|||
{ |
|||
public const int SSS_N_PROFILES = 8; // Max. number of profiles, including the slot taken by the neutral profile
|
|||
public const int SSS_NEUTRAL_PROFILE_ID = SSS_N_PROFILES - 1; // Does not result in blurring
|
|||
public const int SSS_N_SAMPLES_NEAR_FIELD = 55; // Used for extreme close ups; must be a Fibonacci number
|
|||
public const int SSS_N_SAMPLES_FAR_FIELD = 21; // Used at a regular distance; must be a Fibonacci number
|
|||
public const int SSS_LOD_THRESHOLD = 4; // The LoD threshold of the near-field kernel (in pixels)
|
|||
public const int SSS_TRSM_MODE_NONE = 0; |
|||
public const int SSS_TRSM_MODE_THIN = 1; |
|||
// Old SSS Model >>>
|
|||
public const int SSS_BASIC_N_SAMPLES = 11; // Must be an odd number
|
|||
public const int SSS_BASIC_DISTANCE_SCALE = 3; // SSS distance units per centimeter
|
|||
// <<< Old SSS Model
|
|||
} |
|||
|
|||
[Serializable] |
|||
public class SubsurfaceScatteringProfile : ScriptableObject |
|||
{ |
|||
public enum TexturingMode : uint { PreAndPostScatter = 0, PostScatter = 1 }; |
|||
public enum TransmissionMode : uint { None = SssConstants.SSS_TRSM_MODE_NONE, ThinObject = SssConstants.SSS_TRSM_MODE_THIN, Regular }; |
|||
|
|||
[ColorUsage(false, true, 0f, 8f, 0.125f, 3f)] |
|||
public Color scatteringDistance; // Per color channel (no meaningful units)
|
|||
[ColorUsage(false)] |
|||
public Color transmissionTint; // Color, 0 to 1
|
|||
public TexturingMode texturingMode; |
|||
public TransmissionMode transmissionMode; |
|||
public Vector2 thicknessRemap; // X = min, Y = max (in millimeters)
|
|||
public float worldScale; // Size of the world unit in meters
|
|||
[HideInInspector] |
|||
public int settingsIndex; // SubsurfaceScatteringSettings.profiles[i]
|
|||
[SerializeField] |
|||
Vector3 m_ShapeParam; // RGB = shape parameter: S = 1 / D
|
|||
[SerializeField] |
|||
float m_MaxRadius; // In millimeters
|
|||
[SerializeField] |
|||
Vector2[] m_FilterKernelNearField; // X = radius, Y = reciprocal of the PDF
|
|||
[SerializeField] |
|||
Vector2[] m_FilterKernelFarField; // X = radius, Y = reciprocal of the PDF
|
|||
// Old SSS Model >>>
|
|||
[ColorUsage(false, true, 0f, 8f, 0.125f, 3f)] |
|||
public Color scatterDistance1; |
|||
[ColorUsage(false, true, 0f, 8f, 0.125f, 3f)] |
|||
public Color scatterDistance2; |
|||
[Range(0f, 1f)] |
|||
public float lerpWeight; |
|||
[SerializeField] |
|||
Vector4 m_HalfRcpWeightedVariances; |
|||
[SerializeField] |
|||
Vector4[] m_FilterKernelBasic; |
|||
// <<< Old SSS Model
|
|||
|
|||
// --- Public Methods ---
|
|||
|
|||
public SubsurfaceScatteringProfile() |
|||
{ |
|||
scatteringDistance = Color.grey; |
|||
transmissionTint = Color.white; |
|||
texturingMode = TexturingMode.PreAndPostScatter; |
|||
transmissionMode = TransmissionMode.None; |
|||
thicknessRemap = new Vector2(0.0f, 5.0f); |
|||
worldScale = 1.0f; |
|||
settingsIndex = SssConstants.SSS_NEUTRAL_PROFILE_ID; // Updated by SubsurfaceScatteringSettings.OnValidate() once assigned
|
|||
// Old SSS Model >>>
|
|||
scatterDistance1 = new Color(0.3f, 0.3f, 0.3f, 0.0f); |
|||
scatterDistance2 = new Color(0.5f, 0.5f, 0.5f, 0.0f); |
|||
lerpWeight = 1.0f; |
|||
// <<< Old SSS Model
|
|||
|
|||
BuildKernel(); |
|||
} |
|||
|
|||
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); |
|||
|
|||
// Old SSS Model >>>
|
|||
var c = new Color(); |
|||
c.r = Mathf.Max(0.05f, scatterDistance1.r); |
|||
c.g = Mathf.Max(0.05f, scatterDistance1.g); |
|||
c.b = Mathf.Max(0.05f, scatterDistance1.b); |
|||
c.a = 0.0f; |
|||
|
|||
scatterDistance1 = c; |
|||
|
|||
c.r = Mathf.Max(0.05f, scatterDistance2.r); |
|||
c.g = Mathf.Max(0.05f, scatterDistance2.g); |
|||
c.b = Mathf.Max(0.05f, scatterDistance2.b); |
|||
c.a = 0.0f; |
|||
|
|||
scatterDistance2 = c; |
|||
// <<< Old SSS Model
|
|||
|
|||
BuildKernel(); |
|||
} |
|||
|
|||
// Ref: Approximate Reflectance Profiles for Efficient Subsurface Scattering by Pixar.
|
|||
public void BuildKernel() |
|||
{ |
|||
if (m_FilterKernelNearField == null || m_FilterKernelNearField.Length != SssConstants.SSS_N_SAMPLES_NEAR_FIELD) |
|||
{ |
|||
m_FilterKernelNearField = new Vector2[SssConstants.SSS_N_SAMPLES_NEAR_FIELD]; |
|||
} |
|||
|
|||
if (m_FilterKernelFarField == null || m_FilterKernelFarField.Length != SssConstants.SSS_N_SAMPLES_FAR_FIELD) |
|||
{ |
|||
m_FilterKernelFarField = new Vector2[SssConstants.SSS_N_SAMPLES_FAR_FIELD]; |
|||
} |
|||
|
|||
// Clamp to avoid artifacts.
|
|||
m_ShapeParam.x = 1.0f / Mathf.Max(0.001f, scatteringDistance.r); |
|||
m_ShapeParam.y = 1.0f / Mathf.Max(0.001f, scatteringDistance.g); |
|||
m_ShapeParam.z = 1.0f / Mathf.Max(0.001f, scatteringDistance.b); |
|||
|
|||
// We importance sample the color channel with the widest scattering distance.
|
|||
float s = Mathf.Min(m_ShapeParam.x, m_ShapeParam.y, m_ShapeParam.z); |
|||
|
|||
// Importance sample the normalized diffusion profile for the computed value of 's'.
|
|||
// ------------------------------------------------------------------------------------
|
|||
// R(r, s) = s * (Exp[-r * s] + Exp[-r * s / 3]) / (8 * Pi * r)
|
|||
// PDF(r, s) = s * (Exp[-r * s] + Exp[-r * s / 3]) / 4
|
|||
// CDF(r, s) = 1 - 1/4 * Exp[-r * s] - 3/4 * Exp[-r * s / 3]
|
|||
// ------------------------------------------------------------------------------------
|
|||
|
|||
// Importance sample the near field kernel.
|
|||
for (int i = 0, n = SssConstants.SSS_N_SAMPLES_NEAR_FIELD; i < n; i++) |
|||
{ |
|||
float p = (i + 0.5f) * (1.0f / n); |
|||
float r = KernelCdfInverse(p, s); |
|||
|
|||
// N.b.: computation of normalized weights, and multiplication by the surface albedo
|
|||
// of the actual geometry is performed at runtime (in the shader).
|
|||
m_FilterKernelNearField[i].x = r; |
|||
m_FilterKernelNearField[i].y = 1.0f / KernelPdf(r, s); |
|||
} |
|||
|
|||
// Importance sample the far field kernel.
|
|||
for (int i = 0, n = SssConstants.SSS_N_SAMPLES_FAR_FIELD; i < n; i++) |
|||
{ |
|||
float p = (i + 0.5f) * (1.0f / n); |
|||
float r = KernelCdfInverse(p, s); |
|||
|
|||
// N.b.: computation of normalized weights, and multiplication by the surface albedo
|
|||
// of the actual geometry is performed at runtime (in the shader).
|
|||
m_FilterKernelFarField[i].x = r; |
|||
m_FilterKernelFarField[i].y = 1.0f / KernelPdf(r, s); |
|||
} |
|||
|
|||
m_MaxRadius = m_FilterKernelFarField[SssConstants.SSS_N_SAMPLES_FAR_FIELD - 1].x; |
|||
|
|||
// Old SSS Model >>>
|
|||
UpdateKernelAndVarianceData(); |
|||
// <<< Old SSS Model
|
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
public void UpdateKernelAndVarianceData() |
|||
{ |
|||
const int numSamples = SssConstants.SSS_BASIC_N_SAMPLES; |
|||
const int distanceScale = SssConstants.SSS_BASIC_DISTANCE_SCALE; |
|||
|
|||
if (m_FilterKernelBasic == null || m_FilterKernelBasic.Length != numSamples) |
|||
{ |
|||
m_FilterKernelBasic = new Vector4[numSamples]; |
|||
} |
|||
|
|||
// Apply the three-sigma rule, and rescale.
|
|||
Color stdDev1 = ((1.0f / 3.0f) * distanceScale) * scatterDistance1; |
|||
Color stdDev2 = ((1.0f / 3.0f) * distanceScale) * 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); |
|||
|
|||
Vector3 weightSum = new Vector3(0, 0, 0); |
|||
|
|||
float step = 1.0f / (numSamples - 1); |
|||
|
|||
// Importance sample the linear combination of two Gaussians.
|
|||
for (int i = 0; i < numSamples; i++) |
|||
{ |
|||
// Generate 'u' on (0, 0.5] and (0.5, 1).
|
|||
float u = (i <= numSamples / 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.
|
|||
m_FilterKernelBasic[i].x = val.x * (1 / pdf); |
|||
m_FilterKernelBasic[i].y = val.y * (1 / pdf); |
|||
m_FilterKernelBasic[i].z = val.z * (1 / pdf); |
|||
m_FilterKernelBasic[i].w = pos; |
|||
|
|||
weightSum.x += m_FilterKernelBasic[i].x; |
|||
weightSum.y += m_FilterKernelBasic[i].y; |
|||
weightSum.z += m_FilterKernelBasic[i].z; |
|||
} |
|||
|
|||
// Renormalize the weights to conserve energy.
|
|||
for (int i = 0; i < numSamples; i++) |
|||
{ |
|||
m_FilterKernelBasic[i].x *= 1 / weightSum.x; |
|||
m_FilterKernelBasic[i].y *= 1 / weightSum.y; |
|||
m_FilterKernelBasic[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.
|
|||
m_HalfRcpWeightedVariances.x = 0.5f / (weightedStdDev.x * weightedStdDev.x); |
|||
m_HalfRcpWeightedVariances.y = 0.5f / (weightedStdDev.y * weightedStdDev.y); |
|||
m_HalfRcpWeightedVariances.z = 0.5f / (weightedStdDev.z * weightedStdDev.z); |
|||
m_HalfRcpWeightedVariances.w = 0.5f / (weightedStdDev.w * weightedStdDev.w); |
|||
} |
|||
// <<< Old SSS Model
|
|||
|
|||
public Vector3 shapeParameter |
|||
{ |
|||
// Set in BuildKernel().
|
|||
get { return m_ShapeParam; } |
|||
} |
|||
|
|||
public float maxRadius |
|||
{ |
|||
// Set in BuildKernel().
|
|||
get { return m_MaxRadius; } |
|||
} |
|||
|
|||
public Vector2[] filterKernelNearField |
|||
{ |
|||
// Set in BuildKernel().
|
|||
get { return m_FilterKernelNearField; } |
|||
} |
|||
|
|||
public Vector2[] filterKernelFarField |
|||
{ |
|||
// Set in BuildKernel().
|
|||
get { return m_FilterKernelFarField; } |
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
public Vector4[] filterKernelBasic |
|||
{ |
|||
// Set via UpdateKernelAndVarianceData().
|
|||
get { return m_FilterKernelBasic; } |
|||
} |
|||
|
|||
public Vector4 halfRcpWeightedVariances |
|||
{ |
|||
// Set via UpdateKernelAndVarianceData().
|
|||
get { return m_HalfRcpWeightedVariances; } |
|||
} |
|||
// <<< Old SSS Model
|
|||
|
|||
// --- Private Methods ---
|
|||
|
|||
static float KernelVal(float r, float s) |
|||
{ |
|||
return s * (Mathf.Exp(-r * s) + Mathf.Exp(-r * s * (1.0f / 3.0f))) / (8.0f * Mathf.PI * r); |
|||
} |
|||
|
|||
// Computes the value of the integrand over a disk: (2 * PI * r) * KernelVal().
|
|||
static float KernelValCircle(float r, float s) |
|||
{ |
|||
return 0.25f * s * (Mathf.Exp(-r * s) + Mathf.Exp(-r * s * (1.0f / 3.0f))); |
|||
} |
|||
|
|||
static float KernelPdf(float r, float s) |
|||
{ |
|||
return KernelValCircle(r, s); |
|||
} |
|||
|
|||
static float KernelCdf(float r, float s) |
|||
{ |
|||
return 1.0f - 0.25f * Mathf.Exp(-r * s) - 0.75f * Mathf.Exp(-r * s * (1.0f / 3.0f)); |
|||
} |
|||
|
|||
static float KernelCdfDerivative1(float r, float s) |
|||
{ |
|||
return 0.25f * s * Mathf.Exp(-r * s) * (1.0f + Mathf.Exp(r * s * (2.0f / 3.0f))); |
|||
} |
|||
|
|||
static float KernelCdfDerivative2(float r, float s) |
|||
{ |
|||
return (-1.0f / 12.0f) * s * s * Mathf.Exp(-r * s) * (3.0f + Mathf.Exp(r * s * (2.0f / 3.0f))); |
|||
} |
|||
|
|||
// The CDF is not analytically invertible, so we use Halley's Method of root finding.
|
|||
// { f(r, s, p) = CDF(r, s) - p = 0 } with the initial guess { r = (10^p - 1) / s }.
|
|||
static float KernelCdfInverse(float p, float s) |
|||
{ |
|||
// Supply the initial guess.
|
|||
float r = (Mathf.Pow(10.0f, p) - 1.0f) / s; |
|||
float t = float.MaxValue; |
|||
|
|||
while (true) |
|||
{ |
|||
float f0 = KernelCdf(r, s) - p; |
|||
float f1 = KernelCdfDerivative1(r, s); |
|||
float f2 = KernelCdfDerivative2(r, s); |
|||
float dr = f0 / (f1 * (1.0f - f0 * f2 / (2.0f * f1 * f1))); |
|||
|
|||
if (Mathf.Abs(dr) < t) |
|||
{ |
|||
r = r - dr; |
|||
t = Mathf.Abs(dr); |
|||
} |
|||
else |
|||
{ |
|||
// Converged to the best result.
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
return r; |
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
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(-2.0f * Mathf.Log(p))); |
|||
} |
|||
else |
|||
{ |
|||
// F^-1(p) = G^-1(1-p)
|
|||
x = RationalApproximation(Mathf.Sqrt(-2.0f * Mathf.Log(1.0f - 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); |
|||
} |
|||
// <<< Old SSS Model
|
|||
} |
|||
|
|||
[Serializable] |
|||
public class SubsurfaceScatteringSettings : ISerializationCallbackReceiver |
|||
{ |
|||
public int numProfiles; // Excluding the neutral profile
|
|||
public SubsurfaceScatteringProfile[] profiles; |
|||
// Below are the cached values. TODO: uncomment when SSS profile asset serialization is fixed.
|
|||
/*[NonSerialized]*/ public int texturingModeFlags; // 1 bit/profile; 0 = PreAndPostScatter, 1 = PostScatter
|
|||
/*[NonSerialized]*/ public int transmissionFlags; // 2 bit/profile; 0 = inf. thick, 1 = thin, 2 = regular
|
|||
/*[NonSerialized]*/ public Vector4[] thicknessRemaps; // Remap: 0 = start, 1 = end - start
|
|||
/*[NonSerialized]*/ public Vector4[] worldScales; // Size of the world unit in meters (only the X component is used)
|
|||
/*[NonSerialized]*/ public Vector4[] shapeParams; // RGB = S = 1 / D, A = filter radius
|
|||
/*[NonSerialized]*/ public Vector4[] transmissionTints; // RGB = color, A = unused
|
|||
/*[NonSerialized]*/ public Vector4[] filterKernels; // XY = near field, ZW = far field; 0 = radius, 1 = reciprocal of the PDF
|
|||
// Old SSS Model >>>
|
|||
public bool useDisneySSS; |
|||
/*[NonSerialized]*/ public Vector4[] halfRcpWeightedVariances; |
|||
/*[NonSerialized]*/ public Vector4[] halfRcpVariancesAndWeights; |
|||
/*[NonSerialized]*/ public Vector4[] filterKernelsBasic; |
|||
// <<< Old SSS Model
|
|||
|
|||
// --- Public Methods ---
|
|||
|
|||
public SubsurfaceScatteringSettings() |
|||
{ |
|||
numProfiles = 1; |
|||
profiles = new SubsurfaceScatteringProfile[numProfiles]; |
|||
profiles[0] = null; |
|||
texturingModeFlags = 0; |
|||
transmissionFlags = 0; |
|||
thicknessRemaps = null; |
|||
worldScales = null; |
|||
shapeParams = null; |
|||
transmissionTints = null; |
|||
filterKernels = null; |
|||
// Old SSS Model >>>
|
|||
useDisneySSS = true; |
|||
halfRcpWeightedVariances = null; |
|||
halfRcpVariancesAndWeights = null; |
|||
filterKernelsBasic = null; |
|||
// <<< Old SSS Model
|
|||
|
|||
UpdateCache(); |
|||
} |
|||
|
|||
public void OnValidate() |
|||
{ |
|||
// Reserve one slot for the neutral profile.
|
|||
numProfiles = Math.Min(profiles.Length, SssConstants.SSS_N_PROFILES - 1); |
|||
|
|||
if (profiles.Length != numProfiles) |
|||
{ |
|||
Array.Resize(ref profiles, numProfiles); |
|||
} |
|||
|
|||
for (int i = 0; i < numProfiles; i++) |
|||
{ |
|||
if (profiles[i] != null) |
|||
{ |
|||
// Assign the profile IDs.
|
|||
profiles[i].settingsIndex = i; |
|||
} |
|||
} |
|||
|
|||
foreach (var profile in profiles) |
|||
{ |
|||
if (profile != null) |
|||
profile.Validate(); |
|||
} |
|||
|
|||
UpdateCache(); |
|||
} |
|||
|
|||
public void UpdateCache() |
|||
{ |
|||
texturingModeFlags = transmissionFlags = 0; |
|||
|
|||
if (thicknessRemaps == null || thicknessRemaps.Length != SssConstants.SSS_N_PROFILES) |
|||
{ |
|||
thicknessRemaps = new Vector4[SssConstants.SSS_N_PROFILES]; |
|||
} |
|||
|
|||
if (worldScales == null || worldScales.Length != SssConstants.SSS_N_PROFILES) |
|||
{ |
|||
worldScales = new Vector4[SssConstants.SSS_N_PROFILES]; |
|||
} |
|||
|
|||
if (shapeParams == null || shapeParams.Length != SssConstants.SSS_N_PROFILES) |
|||
{ |
|||
shapeParams = new Vector4[SssConstants.SSS_N_PROFILES]; |
|||
} |
|||
|
|||
if (transmissionTints == null || transmissionTints.Length != SssConstants.SSS_N_PROFILES) |
|||
{ |
|||
transmissionTints = new Vector4[SssConstants.SSS_N_PROFILES]; |
|||
} |
|||
|
|||
const int filterKernelsNearFieldLen = SssConstants.SSS_N_PROFILES * SssConstants.SSS_N_SAMPLES_NEAR_FIELD; |
|||
if (filterKernels == null || filterKernels.Length != filterKernelsNearFieldLen) |
|||
{ |
|||
filterKernels = new Vector4[filterKernelsNearFieldLen]; |
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
if (halfRcpWeightedVariances == null || halfRcpWeightedVariances.Length != SssConstants.SSS_N_PROFILES) |
|||
{ |
|||
halfRcpWeightedVariances = new Vector4[SssConstants.SSS_N_PROFILES]; |
|||
} |
|||
|
|||
if (halfRcpVariancesAndWeights == null || halfRcpVariancesAndWeights.Length != 2 * SssConstants.SSS_N_PROFILES) |
|||
{ |
|||
halfRcpVariancesAndWeights = new Vector4[2 * SssConstants.SSS_N_PROFILES]; |
|||
} |
|||
|
|||
const int filterKernelsLen = SssConstants.SSS_N_PROFILES * SssConstants.SSS_BASIC_N_SAMPLES; |
|||
if (filterKernelsBasic == null || filterKernelsBasic.Length != filterKernelsLen) |
|||
{ |
|||
filterKernelsBasic = new Vector4[filterKernelsLen]; |
|||
} |
|||
// <<< Old SSS Model
|
|||
|
|||
for (int i = 0; i < SssConstants.SSS_N_PROFILES - 1; i++) |
|||
{ |
|||
// If a profile is null, it means that it was never set in the HDRenderPipeline Asset or that the profile asset has been deleted.
|
|||
// In this case we want the users to be warned if a material uses one of those. This is why we fill the profile with pink transmission values.
|
|||
if (i >= numProfiles || profiles[i] == null) |
|||
{ |
|||
// Pink transmission
|
|||
transmissionFlags |= 1 << i * 2; |
|||
transmissionTints[i] = new Vector4(100.0f, 0.0f, 100.0f, 1.0f); |
|||
|
|||
// Default neutral values for the rest
|
|||
worldScales[i] = Vector4.one; |
|||
shapeParams[i] = Vector4.zero; |
|||
|
|||
for (int j = 0, n = SssConstants.SSS_N_SAMPLES_NEAR_FIELD; j < n; j++) |
|||
{ |
|||
filterKernels[n * i + j].x = 0.0f; |
|||
filterKernels[n * i + j].y = 1.0f; |
|||
filterKernels[n * i + j].z = 0.0f; |
|||
filterKernels[n * i + j].w = 1.0f; |
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
halfRcpWeightedVariances[i] = Vector4.one; |
|||
halfRcpVariancesAndWeights[2 * i + 0] = Vector4.one; |
|||
halfRcpVariancesAndWeights[2 * i + 1] = Vector4.one; |
|||
|
|||
for (int j = 0, n = SssConstants.SSS_BASIC_N_SAMPLES; j < n; j++) |
|||
{ |
|||
filterKernelsBasic[n * i + j] = Vector4.one; |
|||
filterKernelsBasic[n * i + j].w = 0.0f; |
|||
} |
|||
|
|||
continue; |
|||
} |
|||
|
|||
Debug.Assert(numProfiles < 16, "Transmission flags (32-bit integer) cannot support more than 16 profiles."); |
|||
|
|||
texturingModeFlags |= (int)profiles[i].texturingMode << i; |
|||
transmissionFlags |= (int)profiles[i].transmissionMode << i * 2; |
|||
|
|||
thicknessRemaps[i] = new Vector4(profiles[i].thicknessRemap.x, profiles[i].thicknessRemap.y - profiles[i].thicknessRemap.x, 0.0f, 0.0f); |
|||
worldScales[i] = new Vector4(profiles[i].worldScale, 0, 0, 0); |
|||
shapeParams[i] = profiles[i].shapeParameter; |
|||
shapeParams[i].w = profiles[i].maxRadius; |
|||
transmissionTints[i] = profiles[i].transmissionTint * 0.25f; // Premultiplied
|
|||
|
|||
for (int j = 0, n = SssConstants.SSS_N_SAMPLES_NEAR_FIELD; j < n; j++) |
|||
{ |
|||
filterKernels[n * i + j].x = profiles[i].filterKernelNearField[j].x; |
|||
filterKernels[n * i + j].y = profiles[i].filterKernelNearField[j].y; |
|||
|
|||
if (j < SssConstants.SSS_N_SAMPLES_FAR_FIELD) |
|||
{ |
|||
filterKernels[n * i + j].z = profiles[i].filterKernelFarField[j].x; |
|||
filterKernels[n * i + j].w = profiles[i].filterKernelFarField[j].y; |
|||
} |
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
halfRcpWeightedVariances[i] = profiles[i].halfRcpWeightedVariances; |
|||
|
|||
Vector4 stdDev1 = ((1.0f / 3.0f) * SssConstants.SSS_BASIC_DISTANCE_SCALE) * profiles[i].scatterDistance1; |
|||
Vector4 stdDev2 = ((1.0f / 3.0f) * SssConstants.SSS_BASIC_DISTANCE_SCALE) * profiles[i].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 * SssConstants.SSS_BASIC_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), 4 * (1.0f - profiles[i].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), 4 * profiles[i].lerpWeight); |
|||
|
|||
for (int j = 0, n = SssConstants.SSS_BASIC_N_SAMPLES; j < n; j++) |
|||
{ |
|||
filterKernelsBasic[n * i + j] = profiles[i].filterKernelBasic[j]; |
|||
} |
|||
// <<< Old SSS Model
|
|||
} |
|||
|
|||
// Fill the neutral profile.
|
|||
{ |
|||
int i = SssConstants.SSS_NEUTRAL_PROFILE_ID; |
|||
|
|||
worldScales[i] = Vector4.one; |
|||
shapeParams[i] = Vector4.zero; |
|||
|
|||
for (int j = 0, n = SssConstants.SSS_N_SAMPLES_NEAR_FIELD; j < n; j++) |
|||
{ |
|||
filterKernels[n * i + j].x = 0.0f; |
|||
filterKernels[n * i + j].y = 1.0f; |
|||
filterKernels[n * i + j].z = 0.0f; |
|||
filterKernels[n * i + j].w = 1.0f; |
|||
} |
|||
|
|||
// Old SSS Model >>>
|
|||
halfRcpWeightedVariances[i] = Vector4.one; |
|||
|
|||
for (int j = 0, n = SssConstants.SSS_BASIC_N_SAMPLES; j < n; j++) |
|||
{ |
|||
filterKernelsBasic[n * i + j] = Vector4.one; |
|||
filterKernelsBasic[n * i + j].w = 0.0f; |
|||
} |
|||
// <<< Old SSS Model
|
|||
} |
|||
} |
|||
|
|||
public void OnBeforeSerialize() |
|||
{ |
|||
// No special action required.
|
|||
} |
|||
|
|||
public void OnAfterDeserialize() |
|||
{ |
|||
// TODO: uncomment when SSS profile asset serialization is fixed.
|
|||
// UpdateCache();
|
|||
} |
|||
} |
|||
} |
撰写
预览
正在加载...
取消
保存
Reference in new issue