You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

337 lines
17 KiB

using System.Collections.Generic;
using System;
using UnityEngine;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph;
using UnityEditor.ShaderGraph.Drawing.Controls;
using UnityEditor.ShaderGraph.Internal;
using UnityEngine.Rendering.HighDefinition;
using System.Reflection;
using System.Linq;
namespace UnityEditor.Rendering.HighDefinition
{
[SRPFilter(typeof(HDRenderPipeline))]
[Title("Input", "High Definition Render Pipeline", "HD Sample Buffer")]
sealed class HDSampleBufferNode : AbstractMaterialNode, IGeneratesBodyCode, IGeneratesFunction, IMayRequireScreenPosition, IMayRequireDepthTexture, IMayRequireNDCPosition
{
const string k_ScreenPositionSlotName = "UV";
const string k_ThicknessLayerIDSlotName = "Layer Mask";
const string k_OutputSlotName = "Output";
const string k_OutputThicknessSlotName = "Thickness";
const string k_OutputOverlapCountSlotName = "Overlap Count";
const string k_OutputDistanceSlotName = "Distance";
const int k_ScreenPositionSlotId = 0;
const int k_ThicknessLayerIDSlotId = 1;
const int k_OutputSlotId = 2;
const int k_OutputThicknessSlotId = 3;
const int k_OutputOverlapSlotId = 4;
const int k_OutputDistanceSlotId = 5;
public enum BufferType
{
NormalWorldSpace,
Smoothness,
MotionVectors,
IsSky,
PostProcessInput,
RenderingLayerMask,
Thickness,
IsUnderWater,
}
[SerializeField]
private BufferType m_BufferType = BufferType.NormalWorldSpace;
[EnumControl("Source Buffer")]
public BufferType bufferType
{
get { return m_BufferType; }
set
{
if (m_BufferType == value)
return;
m_BufferType = value;
UpdateNodeAfterDeserialization();
Dirty(ModificationScope.Graph);
}
}
public override string documentationURL => NodeUtils.GetDocumentationString("HD-Sample-Buffer");
public static List<HDSampleBufferNode> nodeList = new();
public HDSampleBufferNode()
{
name = "HD Sample Buffer";
synonyms = new string[] { "normal", "motion vector", "smoothness", "postprocessinput", "issky", "thickness", "underwater" };
UpdateNodeAfterDeserialization();
nodeList.Add(this);
}
~HDSampleBufferNode()
{
nodeList.Remove(this);
}
public override bool hasPreview { get { return true; } }
public override PreviewMode previewMode => PreviewMode.Preview2D;
int channelCount;
public sealed override void UpdateNodeAfterDeserialization()
{
var addedSlots = new List<int>();
var last0 = AddSlot(new ScreenPositionMaterialSlot(k_ScreenPositionSlotId, k_ScreenPositionSlotName, k_ScreenPositionSlotName, ScreenSpaceType.Default));
addedSlots.Add(last0.id);
switch (bufferType)
{
case BufferType.NormalWorldSpace:
{
var last = AddSlot(new Vector3MaterialSlot(k_OutputSlotId, k_OutputSlotName, k_OutputSlotName, SlotType.Output, Vector3.zero, ShaderStageCapability.Fragment));
addedSlots.Add(last.id);
channelCount = 3;
}
break;
case BufferType.Smoothness:
{
var last = AddSlot(new Vector1MaterialSlot(k_OutputSlotId, k_OutputSlotName, k_OutputSlotName, SlotType.Output, 0, ShaderStageCapability.Fragment));
addedSlots.Add(last.id);
channelCount = 1;
}
break;
case BufferType.MotionVectors:
{
var last = AddSlot(new Vector2MaterialSlot(k_OutputSlotId, k_OutputSlotName, k_OutputSlotName, SlotType.Output, Vector2.zero, ShaderStageCapability.Fragment));
addedSlots.Add(last.id);
channelCount = 2;
}
break;
case BufferType.IsSky:
{
var last = AddSlot(new Vector1MaterialSlot(k_OutputSlotId, k_OutputSlotName, k_OutputSlotName, SlotType.Output, 0, ShaderStageCapability.Fragment));
addedSlots.Add(last.id);
channelCount = 1;
}
break;
case BufferType.PostProcessInput:
{
var last = AddSlot(new ColorRGBAMaterialSlot(k_OutputSlotId, k_OutputSlotName, k_OutputSlotName, SlotType.Output, Color.black, ShaderStageCapability.Fragment));
addedSlots.Add(last.id);
channelCount = 4;
}
break;
case BufferType.RenderingLayerMask:
{
var last = AddSlot(new Vector1MaterialSlot(k_OutputSlotId, k_OutputSlotName, k_OutputSlotName, SlotType.Output, 0, ShaderStageCapability.Fragment));
addedSlots.Add(last.id);
channelCount = 1;
}
break;
case BufferType.Thickness:
{
var lastMat = AddSlot(new Vector1MaterialSlot(k_ThicknessLayerIDSlotId, k_ThicknessLayerIDSlotName, k_ThicknessLayerIDSlotName, SlotType.Input, 0.0f, ShaderStageCapability.Fragment));
addedSlots.Add(lastMat.id);
var last = AddSlot(new Vector1MaterialSlot(k_OutputThicknessSlotId, k_OutputThicknessSlotName, k_OutputThicknessSlotName, SlotType.Output, 0.0f, ShaderStageCapability.Fragment));
addedSlots.Add(last.id);
last = AddSlot(new Vector1MaterialSlot(k_OutputOverlapSlotId, k_OutputOverlapCountSlotName, k_OutputOverlapCountSlotName, SlotType.Output, 0.0f, ShaderStageCapability.Fragment));
addedSlots.Add(last.id);
channelCount = 2;
}
break;
case BufferType.IsUnderWater:
{
var last = AddSlot(new BooleanMaterialSlot(k_OutputSlotId, k_OutputSlotName, k_OutputSlotName, SlotType.Output, false, ShaderStageCapability.Fragment));
addedSlots.Add(last.id);
var distance = AddSlot(new Vector1MaterialSlot(k_OutputDistanceSlotId, k_OutputDistanceSlotName, k_OutputDistanceSlotName, SlotType.Output, 0, ShaderStageCapability.Fragment));
addedSlots.Add(distance.id);
channelCount = 1;
}
break;
}
RemoveSlotsNameNotMatching(addedSlots, supressWarnings: true);
}
string GetFunctionName() => $"Unity_HDRP_SampleBuffer_{bufferType}_$precision";
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
{
// Preview SG doesn't have access to HDRP depth buffer
if (!generationMode.IsPreview())
{
registry.RequiresIncludePath("Packages/com.unity.render-pipelines.high-definition/Runtime/Material/NormalBuffer.hlsl");
registry.RequiresIncludePath("Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl");
if (bufferType == BufferType.IsUnderWater)
registry.RequiresIncludePath("Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/UnderWaterUtilities.hlsl");
registry.ProvideFunction(GetFunctionName(), s =>
{
if (bufferType == BufferType.PostProcessInput)
{
// Declare post process input here because the property collector don't support TEXTURE_X type
s.AppendLine($"TEXTURE2D_X({nameof(HDShaderIDs._CustomPostProcessInput)});");
}
s.AppendLine("$precision{1} {0}($precision2 uv, int layerID)", GetFunctionName(), channelCount);
using (s.BlockScope())
{
switch (bufferType)
{
case BufferType.NormalWorldSpace:
s.AppendLine("uint2 pixelCoords = uint2(uv * _ScreenSize.xy);");
s.AppendLine("NormalData normalData;");
s.AppendLine("DecodeFromNormalBuffer(pixelCoords, normalData);");
s.AppendLine("return normalData.normalWS;");
break;
case BufferType.Smoothness:
s.AppendLine("uint2 pixelCoords = uint2(uv * _ScreenSize.xy);");
s.AppendLine("NormalData normalData;");
s.AppendLine("DecodeFromNormalBuffer(pixelCoords, normalData);");
s.AppendLine("return IsSky(pixelCoords) ? 1 : RoughnessToPerceptualSmoothness(PerceptualRoughnessToRoughness(normalData.perceptualRoughness));");
break;
case BufferType.MotionVectors:
s.AppendLine("uint2 pixelCoords = uint2(uv * _ScreenSize.xy);");
s.AppendLine($"float4 motionVecBufferSample = LOAD_TEXTURE2D_X_LOD(_CameraMotionVectorsTexture, pixelCoords, 0);");
s.AppendLine("float2 motionVec;");
s.AppendLine("DecodeMotionVector(motionVecBufferSample, motionVec);");
s.AppendLine("return motionVec;");
break;
case BufferType.IsSky:
s.AppendLine("return IsSky(uv) ? 1 : 0;");
break;
case BufferType.PostProcessInput:
s.AppendLine("uint2 pixelCoords = uint2(uv * _ScreenSize.xy);");
s.AppendLine("return LOAD_TEXTURE2D_X_LOD(_CustomPostProcessInput, pixelCoords, 0);");
break;
case BufferType.RenderingLayerMask:
s.AppendLine("uint2 pixelCoords = uint2(uv * _ScreenSize.xy);");
s.AppendLine("return _EnableRenderingLayers ? UnpackMeshRenderingLayerMask(LOAD_TEXTURE2D_X_LOD(_RenderingLayerMaskTexture, pixelCoords, 0)) : 0;");
break;
case BufferType.Thickness:
s.AppendLine(GetRayTracingError());
s.AppendLine("return SampleThickness(uv.xy, layerID);");
break;
case BufferType.IsUnderWater:
s.AppendLine("uint2 pixelCoords = uint2(uv * _ScreenSize.xy);");
s.AppendLine("return _UnderWaterSurfaceIndex != -1 ? GetUnderWaterDistance(pixelCoords) : 1.0f;");
break;
default:
s.AppendLine("return 0.0;");
break;
}
}
});
}
else
{
registry.ProvideFunction(GetFunctionName(), s =>
{
s.AppendLine("$precision{1} {0}($precision2 uv, int layerID)", GetFunctionName(), channelCount);
using (s.BlockScope())
{
switch (bufferType)
{
case BufferType.NormalWorldSpace:
s.AppendLine("return LatlongToDirectionCoordinate(uv);");
break;
case BufferType.MotionVectors:
s.AppendLine("return uv * 2 - 1;");
break;
case BufferType.Smoothness:
s.AppendLine("return uv.x;");
break;
case BufferType.Thickness:
// Thickness of a centered sphere seen from an infinite point of view
s.AppendLine("return pow(abs(1.0f - saturate(dot(uv * 2 - 1, uv * 2 - 1))), 2.2f);");
break;
case BufferType.IsUnderWater:
s.AppendLine("return uv.y * 2 - 1;");
break;
default:
s.AppendLine("return 0.0f;");
break;
}
}
});
}
}
public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
{
string uv = GetSlotValue(k_ScreenPositionSlotId, generationMode);
if (bufferType == BufferType.Thickness)
{
string layerID = GetSlotValue(k_ThicknessLayerIDSlotId, generationMode);
sb.AppendLine($"$precision2 {GetVariableNameForSlot(k_OutputThicknessSlotId)}_Value = {GetFunctionName()}({uv}.xy, (int){layerID});");
sb.AppendLine($"$precision {GetVariableNameForSlot(k_OutputThicknessSlotId)} = {GetVariableNameForSlot(k_OutputThicknessSlotId)}_Value.x;");
sb.AppendLine($"$precision {GetVariableNameForSlot(k_OutputOverlapSlotId)} = {GetVariableNameForSlot(k_OutputThicknessSlotId)}_Value.y;");
}
else if (bufferType == BufferType.IsUnderWater)
{
sb.AppendLine($"$precision {GetVariableNameForSlot(k_OutputSlotId)}_Value = {GetFunctionName()}({uv}.xy, 0);");
sb.AppendLine($"$precision {GetVariableNameForSlot(k_OutputSlotId)} = {GetVariableNameForSlot(k_OutputSlotId)}_Value <= 0.0f;");
sb.AppendLine($"$precision {GetVariableNameForSlot(k_OutputDistanceSlotId)} = {GetVariableNameForSlot(k_OutputSlotId)}_Value;");
}
else
{
sb.AppendLine($"$precision{channelCount} {GetVariableNameForSlot(k_OutputSlotId)} = {GetFunctionName()}({uv}.xy, 0);");
}
}
public bool RequiresDepthTexture(ShaderStageCapability stageCapability) => true;
public bool RequiresNDCPosition(ShaderStageCapability stageCapability = ShaderStageCapability.All) => true;
public bool RequiresScreenPosition(ShaderStageCapability stageCapability = ShaderStageCapability.All) => true;
#region Warning Badge
static readonly Dictionary<BufferType, ShaderMessage> s_TypeToMessage = new()
{
{ BufferType.RenderingLayerMask, new ShaderMessage("Rendering Layer Mask Buffer is not enabled in the HDRP Asset. This will not work.", ShaderCompilerMessageSeverity.Warning) },
{ BufferType.Thickness, new ShaderMessage("Compute Thickness is not enabled in the HDRP Asset. This will not work.", ShaderCompilerMessageSeverity.Warning) },
{ BufferType.IsUnderWater, new ShaderMessage("Water is not enabled in the HDRP Asset. This will not work.", ShaderCompilerMessageSeverity.Warning) },
};
public override void ValidateNode()
{
if ((bufferType == BufferType.RenderingLayerMask && HDRenderPipeline.currentAsset?.currentPlatformRenderPipelineSettings.renderingLayerMaskBuffer == false) ||
(bufferType == BufferType.Thickness && HDRenderPipeline.currentAsset?.currentPlatformRenderPipelineSettings.supportComputeThickness == false) ||
(bufferType == BufferType.IsUnderWater && HDRenderPipeline.currentAsset?.currentPlatformRenderPipelineSettings.supportWater == false))
owner.messageManager?.AddOrAppendError(owner, objectId, s_TypeToMessage[bufferType]);
}
private void UpdateWarningBadge(BufferType bufferType, bool supported)
{
if (owner == null) return;
if (!supported && this.bufferType == bufferType)
owner.messageManager?.AddOrAppendError(owner, objectId, s_TypeToMessage[bufferType]);
else
owner.ClearErrorsForNode(this);
}
internal static void UpdateWarningBadges(BufferType bufferType, bool supported)
{
foreach (var node in nodeList)
{
if (node != null)
node.UpdateWarningBadge(bufferType, supported);
}
EditorApplication.delayCall += () => {
foreach (var node in nodeList)
{
if (node != null && node.owner?.owner != null)
node.owner.owner.Validate();
}
};
}
#endregion
}
}