浏览代码

- Move HDRenderLoop to HDRenderPipeline

- Work in progress : only fix compilation, code generation should be rework
/scriptablerenderloop-materialgraph
Paul Demeulenaere 8 年前
当前提交
795ccf44
共有 22 个文件被更改,包括 1024 次插入38 次删除
  1. 3
      .gitignore
  2. 4
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Editor/BaseLitUI.cs
  3. 1
      Assets/ScriptableRenderLoop/common/TextureCache.cs
  4. 2
      Assets/ShaderGenerator/ShaderGeneratorHelper.cs
  5. 1
      _ExternalSymLink.bat
  6. 33
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.template
  7. 802
      Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderloopMaterialGraph.cs
  8. 47
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Editor/LitGraphUI.cs
  9. 107
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitSharePass.template
  10. 44
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Editor/UnlitGraphUI.cs
  11. 9
      Assets/TestScenes/HDTest/GraphicTest/Common/Textures.meta
  12. 9
      Assets/TestScenes/HDTest/HDRenderLoopTest.meta
  13. 0
      /Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderloopMaterialGraph.cs.meta
  14. 0
      /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.template.meta
  15. 0
      /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitSharePass.template.meta
  16. 0
      /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.template
  17. 0
      /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Editor/LitGraphUI.cs.meta
  18. 0
      /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Unlit.template.meta
  19. 0
      /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Unlit.template
  20. 0
      /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Editor/UnlitGraphUI.cs.meta

3
.gitignore


Assets/GraphFramework/*
Assets/NewUI/*
Assets/UnityShaderEditor/*
Assets/MaterialGraphInterface/*
Assets/MaterialGraphInterface/*
Assets/Resources/*

4
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Editor/BaseLitUI.cs


public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props)
{
FindCommonOptionProperties(props); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly
FindMaterialProperties(props);
FindMaterialProperties(props);
m_MaterialEditor = materialEditor;
Material material = materialEditor.target as Material;

protected static string[] reservedProperties = new string[] { kSurfaceType, kBlendMode, kAlphaCutoff, kAlphaCutoffEnabled, kDoubleSidedMode };
protected abstract void FindMaterialProperties(MaterialProperty[] props);
protected abstract void ShaderInputGUI();
protected abstract void ShaderInputGUI();
protected abstract void ShaderInputOptionsGUI();
protected abstract void SetupMaterialKeywords(Material material);
protected abstract bool ShouldEmissionBeEnabled(Material material);

1
Assets/ScriptableRenderLoop/common/TextureCache.cs


public int FetchSlice(Texture texture)
{
if (texture == null) return 0;
var texId = (uint)texture.GetInstanceID();
//assert(TexID!=g_InvalidTexID);

2
Assets/ShaderGenerator/ShaderGeneratorHelper.cs


namespace UnityEngine.Experimental.ScriptableRenderLoop
namespace UnityEditor.Experimental.Rendering
{
public class ShaderGeneratorHelper
{

1
_ExternalSymLink.bat


mklink /J "%cd%\Assets\UnityShaderEditor" "%cd%\External\MaterialGraphProject\Assets\UnityShaderEditor"
mklink /J "%cd%\Assets\NewUI" "%cd%\External\MaterialGraphProject\Assets\NewUI"
mklink /J "%cd%\Assets\GraphFramework" "%cd%\External\MaterialGraphProject\Assets\GraphFramework"
mklink /J "%cd%\Assets\Resources" "%cd%\External\MaterialGraphProject\Assets\Resources"
pause

33
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.template


//-------------------------------------------------------------------------------------
#include "common.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderLoop/ShaderConfig.cs.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderLoop/ShaderVariables.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderLoop/Material/Attributes.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderLoop/ShaderPass/ShaderPass.cs.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderConfig.cs.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderVariables.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderPass/FragInputs.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderPass/ShaderPass.cs.hlsl"
//-------------------------------------------------------------------------------------
// variable declaration

HLSLPROGRAM
#pragma vertex VertDefault
#pragma vertex Vert
#include "Assets/ScriptableRenderLoop/HDRenderLoop/Material/Material.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Material.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderLoop/ShaderPass/ShaderPassGBuffer.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderPass/ShaderPassGBuffer.hlsl"
/*
// ------------------------------------------------------------------
// Debug pass
// ------------------------------------------------------------------

#pragma fragment Frag
#define SHADERPASS SHADERPASS_DEBUG_VIEW_MATERIAL
#include "Assets/ScriptableRenderLoop/HDRenderLoop/Material/Material.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Material.hlsl"
//Generated code : Begin

#include "Assets/ScriptableRenderLoop/HDRenderLoop/ShaderPass/ShaderPassDebugViewMaterial.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderPass/ShaderPassDebugViewMaterial.hlsl"
ENDHLSL
}

#pragma fragment Frag
#define SHADERPASS SHADERPASS_LIGHT_TRANSPORT
#include "Assets/ScriptableRenderLoop/HDRenderLoop/Material/Material.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Material.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderLoop/ShaderPass/ShaderPassLightTransport.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderPass/ShaderPassLightTransport.hlsl"
ENDHLSL
}

#pragma fragment Frag
#define SHADERPASS SHADERPASS_DEPTH_ONLY
#include "Assets/ScriptableRenderLoop/HDRenderLoop/Material/Material.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Material.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderLoop/ShaderPass/ShaderPassDepthOnly.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderPass/ShaderPassDepthOnly.hlsl"
ENDHLSL
}

#pragma multi_compile LIGHTLOOP_SINGLE_PASS
//#pragma multi_compile SHADOWFILTERING_FIXED_SIZE_PCF
#include "Assets/ScriptableRenderLoop/HDRenderLoop/Lighting/Lighting.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/Lighting/Lighting.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderLoop/ShaderPass/ShaderPassForward.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderPass/ShaderPassForward.hlsl"
*/
}
CustomEditor "Experimental.ScriptableRenderLoop.LitGraphUI"
}

802
Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderloopMaterialGraph.cs


using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine.MaterialGraph;
using UnityEngine.Graphing;
namespace UnityEngine.Experimental.Rendering.HDPipeline
{
public abstract class AbstractMaterialNodeHDRenderPipeline : AbstractMaterialNode
{
protected string GetVariableName(IEdge edge, ConcreteSlotValueType type)
{
var fromNode = owner.GetNodeFromGuid<AbstractMaterialNode>(edge.outputSlot.nodeGuid);
return ShaderGenerator.AdaptNodeOutput(fromNode, edge.outputSlot.slotId, type);
}
protected IEdge GetEdge(int idSlot)
{
var slot = FindInputSlot<MaterialSlot>(idSlot);
if (slot != null)
{
var edges = owner.GetEdges(slot.slotReference).ToArray();
if (edges.Length > 0)
{
return edges[0];
}
}
return null;
}
}
[Serializable]
[Title("HDRenderPipeline/BakeDiffuseLighting")]
public class BakeDiffuseLightingNode : AbstractMaterialNodeHDRenderPipeline, IGeneratesBodyCode, IMayRequireNormal, IMayRequireWorldPosition, IMayRequireMeshUV
{
private const int PositionWSInput = 0;
private const int NormalWSInput = 1;
private const int UVStaticInput = 2;
private const int UVDynamicInput = 3;
private const int ColorOuput = 4;
public BakeDiffuseLightingNode()
{
name = GetType().Name;
UpdateNodeAfterDeserialization();
}
public bool RequiresWorldPosition()
{
return GetEdge(PositionWSInput) == null;
}
public bool RequiresMeshUV(UVChannel uv)
{
if (uv == UVChannel.uv0)
{
return GetEdge(UVStaticInput) == null;
}
if (uv == UVChannel.uv1)
{
return GetEdge(UVDynamicInput) == null;
}
return false;
}
public bool RequiresNormal()
{
return GetEdge(NormalWSInput) == null;
}
public sealed override void UpdateNodeAfterDeserialization()
{
AddSlot(new MaterialSlot(PositionWSInput, "PositionWS", "PositionWSInput", Graphing.SlotType.Input, SlotValueType.Vector3, Vector4.zero));
AddSlot(new MaterialSlot(NormalWSInput, "Normal", "NormalWSInput", Graphing.SlotType.Input, SlotValueType.Vector3, Vector4.zero));
AddSlot(new MaterialSlot(UVStaticInput, "UVStatic", "UVStaticInput", Graphing.SlotType.Input, SlotValueType.Vector2, Vector4.zero));
AddSlot(new MaterialSlot(UVDynamicInput, "UVDynamic", "UVDynamicInput", Graphing.SlotType.Input, SlotValueType.Vector2, Vector4.zero));
AddSlot(new MaterialSlot(ColorOuput, "Color", "ColorOuput", Graphing.SlotType.Output, SlotValueType.Vector3, Vector4.zero));
}
public void GenerateNodeCode(ShaderGenerator visitor, GenerationMode generationMode)
{
var positionWSInputEdge = GetEdge(PositionWSInput);
var normalWSInputEdge = GetEdge(NormalWSInput);
var UVStaticInputEdge = GetEdge(UVStaticInput);
var UVDynamicInputEdge = GetEdge(UVDynamicInput);
var positionWSInputValue = ShaderGeneratorNames.WorldSpacePosition;
var normalWSInputValue = ShaderGeneratorNames.WorldSpaceNormal;
var UVStaticInputValue = ShaderGeneratorNames.GetUVName(UVChannel.uv1);
var UVDynamicInputValue = ShaderGeneratorNames.GetUVName(UVChannel.uv2);
if (positionWSInputEdge != null)
{
positionWSInputValue = GetVariableName(positionWSInputEdge, ConcreteSlotValueType.Vector3);
}
if (normalWSInputEdge != null)
{
normalWSInputValue = GetVariableName(normalWSInputEdge, ConcreteSlotValueType.Vector3);
}
if (UVStaticInputEdge != null)
{
UVStaticInputValue = GetVariableName(UVStaticInputEdge, ConcreteSlotValueType.Vector2);
}
if (UVDynamicInputEdge != null)
{
UVDynamicInputValue = GetVariableName(UVDynamicInputEdge, ConcreteSlotValueType.Vector2);
}
var body = string.Format("{0}3 {1} = SampleBakedGI({2}, {3}, {4}.xy, {5}.xy);", precision, GetVariableNameForSlot(ColorOuput), positionWSInputValue, normalWSInputValue, UVStaticInputValue, UVDynamicInputValue);
visitor.AddShaderChunk(body, false);
}
}
[Serializable]
[Title("HDRenderPipeline/TangentToWorldDirectionNode")]
public class TangentToWorldDirectionNode : AbstractMaterialNodeHDRenderPipeline, IGeneratesBodyCode, IMayRequireBitangent, IMayRequireTangent, IMayRequireNormal
{
private const int TextureNormal = 0;
private const int NormalInput = 1;
private const int TangentInput = 2;
private const int BitangentInput = 3;
private const int NormalOutput = 4;
public TangentToWorldDirectionNode()
{
name = GetType().Name;
UpdateNodeAfterDeserialization();
}
public override bool hasPreview
{
get { return true; }
}
public override PreviewMode previewMode
{
get { return PreviewMode.Preview3D; }
}
public void GenerateNodeCode(ShaderGenerator visitor, GenerationMode generationMode)
{
var textureNormalSlotEdge = GetEdge(TextureNormal);
var normalInputSlotEdge = GetEdge(NormalInput);
var tangentInputSlotEdge = GetEdge(TangentInput);
var bitangentInputSlotEdge = GetEdge(BitangentInput);
var textureNormalValue = string.Format("{0}4(UnpackNormal(float3(0.5f), 0.0f)", precision);
var normalInputSlotValue = ShaderGeneratorNames.WorldSpaceNormal;
var tangentInputSlotValue = ShaderGeneratorNames.WorldSpaceTangent;
var bitangentInputSlotValue = ShaderGeneratorNames.WorldSpaceBitangent;
if (textureNormalSlotEdge != null)
{
textureNormalValue = GetVariableName(textureNormalSlotEdge, ConcreteSlotValueType.Vector4);
}
if (normalInputSlotEdge != null)
{
normalInputSlotValue = GetVariableName(normalInputSlotEdge, ConcreteSlotValueType.Vector3);
}
if (tangentInputSlotEdge != null)
{
tangentInputSlotValue = GetVariableName(tangentInputSlotEdge, ConcreteSlotValueType.Vector3);
}
if (bitangentInputSlotEdge != null)
{
bitangentInputSlotValue = GetVariableName(bitangentInputSlotEdge, ConcreteSlotValueType.Vector3);
}
var tangentToWorldName = string.Format("tangentToWorld_{0}", GetVariableNameForNode());
var normalTSName = string.Format("normalTS_{0}", GetVariableNameForNode());
var body = "";
body += string.Format("float3 {0} = {1}.xyz;\n", normalTSName, textureNormalValue);
body += string.Format("float3 {0}[3] = {{ {1}, {2}, {3} }};\n", tangentToWorldName, tangentInputSlotValue, bitangentInputSlotValue, normalInputSlotValue);
if (generationMode == GenerationMode.Preview)
{
body += string.Format("{0}3 {1} = mul({2}, float3x3({3}[0], {3}[1], {3}[2]));\n", precision, GetVariableNameForSlot(NormalOutput), normalTSName, tangentToWorldName);
}
else
{
body += string.Format("{0}3 {1} = TransformTangentToWorld({2}, {3});\n", precision, GetVariableNameForSlot(NormalOutput), normalTSName, tangentToWorldName);
}
visitor.AddShaderChunk(body, false);
}
public sealed override void UpdateNodeAfterDeserialization()
{
AddSlot(new MaterialSlot(TextureNormal, "TextureNormal", "TextureNormalInput", Graphing.SlotType.Input, SlotValueType.Vector4, Vector4.zero));
AddSlot(new MaterialSlot(NormalInput, "Normal", "NormalInput", Graphing.SlotType.Input, SlotValueType.Vector3, Vector4.zero));
AddSlot(new MaterialSlot(TangentInput, "Tangent", "TangentInput", Graphing.SlotType.Input, SlotValueType.Vector3, Vector4.zero));
AddSlot(new MaterialSlot(BitangentInput, "Bitangent", "BitangentInput", Graphing.SlotType.Input, SlotValueType.Vector3, Vector4.zero));
AddSlot(new MaterialSlot(NormalOutput, "Normal", "NormalOutput", Graphing.SlotType.Output, SlotValueType.Vector3, Vector4.zero));
}
public bool RequiresBitangent()
{
return GetEdge(BitangentInput) == null;
}
public bool RequiresTangent()
{
return GetEdge(TangentInput) == null;
}
public bool RequiresNormal()
{
return GetEdge(NormalInput) == null;
}
}
[Serializable]
public abstract class AbstractHDRenderPipelineMasterNode : AbstractMasterNode
{
public AbstractHDRenderPipelineMasterNode()
{
name = GetType().Name;
UpdateNodeAfterDeserialization();
}
private static void DepthFirstCollectNodesFromNodeAndSlotIDList(List<INode> nodeList, INode node, List<int> slotId)
{
foreach (var slot in node.GetInputSlots<ISlot>().Select(x => x.id).Where(x => slotId.Contains(x)))
{
foreach (var edge in node.owner.GetEdges(node.GetSlotReference(slot)))
{
var outputNode = node.owner.GetNodeFromGuid(edge.outputSlot.nodeGuid);
NodeUtils.DepthFirstCollectNodesFromNode(nodeList, outputNode);
}
}
}
public sealed override void UpdateNodeAfterDeserialization()
{
var surfaceType = GetSurfaceType();
if (surfaceType != null)
{
var fieldsBuiltIn = typeof(Builtin.BuiltinData).GetFields();
var fieldsSurface = surfaceType.GetFields();
var slots = fieldsSurface.Concat(fieldsBuiltIn).Select((field, index) =>
{
var attributes = (SurfaceDataAttributes[])field.GetCustomAttributes(typeof(SurfaceDataAttributes), false);
var attribute = attributes.Length > 0 ? attributes[0] : new SurfaceDataAttributes();
var valueType = SlotValueType.Dynamic;
var fieldType = field.FieldType;
if (fieldType == typeof(float))
{
valueType = SlotValueType.Vector1;
}
else if (fieldType == typeof(Vector2))
{
valueType = SlotValueType.Vector2;
}
else if (fieldType == typeof(Vector3))
{
valueType = SlotValueType.Vector3;
}
else if (fieldType == typeof(Vector2))
{
valueType = SlotValueType.Vector4;
}
return new
{
index = index,
priority = attribute.priority,
displayName = attribute.displayName,
materialID = attribute.filter,
semantic = attribute.semantic,
shaderOutputName = field.Name,
valueType = valueType
};
})
.Where(o => (o.materialID == null || o.materialID.Contains(GetMatchingMaterialID())) && o.valueType != SlotValueType.Dynamic)
.OrderBy(o => o.priority)
.ThenBy(o => o.displayName)
.ToArray();
foreach (var slot in slots)
{
if (slot.semantic == SurfaceDataAttributes.Semantic.BakeDiffuseLighting && !IncludeBuiltInLitData())
{
continue;
}
AddSlot(new MaterialSlot(slot.index, slot.displayName, slot.shaderOutputName, Graphing.SlotType.Input, slot.valueType, Vector4.zero));
}
}
}
private bool Requires(SurfaceDataAttributes.Semantic targetSemantic, Regex filter)
{
var fields = GetSurfaceType().GetFields().Concat(typeof(Builtin.BuiltinData).GetFields());
return fields.Where(f => filter.IsMatch(f.Name)).Any(f =>
{
var attributes = (SurfaceDataAttributes[])f.GetCustomAttributes(typeof(SurfaceDataAttributes), false);
var semantic = attributes.Length > 0 ? attributes[0].semantic : SurfaceDataAttributes.Semantic.None;
if (semantic == targetSemantic)
{
var slot = GetInputSlots<MaterialSlot>().FirstOrDefault(o => o.shaderOutputName == f.Name);
if (slot != null)
{
return owner.GetEdges(slot.slotReference).Count() == 0;
}
}
return false;
});
}
public bool RequiresNormal(Regex filter)
{
return Requires(SurfaceDataAttributes.Semantic.Normal, filter);
}
public bool RequiresTangent(Regex filter)
{
return Requires(SurfaceDataAttributes.Semantic.Tangent, filter);
}
public bool RequiresMeshUV(int index, Regex filter)
{
if (index == 1 || index == 2)
{
return Requires(SurfaceDataAttributes.Semantic.BakeDiffuseLighting, filter);
}
return false;
}
public bool RequiresWorldPosition(Regex filter)
{
return Requires(SurfaceDataAttributes.Semantic.BakeDiffuseLighting, filter);
}
private class Vayring
{
public string attributeName;
public string semantic;
public SlotValueType semanticType;
public string vayringName;
public SlotValueType type;
public string vertexCode;
public string pixelCode;
public string fragInputTarget;
};
private string GenerateLitDataTemplate(GenerationMode mode, string useSurfaceDataInput, string useSurfaceFragInput, PropertyGenerator propertyGenerator, ShaderGenerator propertyUsagesVisitor, ShaderGenerator shaderFunctionVisitor, string litShareTemplate)
{
var activeNodeList = new List<INode>();
var useDataInputRegex = new Regex(useSurfaceDataInput);
var needFragInputRegex = new Regex(useSurfaceFragInput);
var slotIDList = GetInputSlots<MaterialSlot>().Where(s => useDataInputRegex.IsMatch(s.shaderOutputName)).Select(s => s.id).ToList();
DepthFirstCollectNodesFromNodeAndSlotIDList(activeNodeList, this, slotIDList);
var vayrings = new List<Vayring>();
for (int iTexCoord = 0; iTexCoord < 4; ++iTexCoord)
{
if (needFragInputRegex.IsMatch("texCoord" + iTexCoord) || RequiresMeshUV(iTexCoord, useDataInputRegex) || activeNodeList.OfType<IMayRequireMeshUV>().Any(x => x.RequiresMeshUV((UVChannel)iTexCoord)))
{
vayrings.Add(new Vayring()
{
attributeName = "texCoord" + iTexCoord,
semantic = "TEXCOORD" + iTexCoord,
vayringName = "texCoord" + iTexCoord,
type = SlotValueType.Vector2,
vertexCode = string.Format("output.texCoord{0} = input.texCoord{0};", iTexCoord),
pixelCode = string.Format("float4 {0} = float4(fragInput.texCoord{1}, 0, 0);", ShaderGeneratorNames.GetUVName((UVChannel)iTexCoord), iTexCoord)
});
}
}
bool needBitangent = needFragInputRegex.IsMatch("bitangentWS") || activeNodeList.OfType<IMayRequireBitangent>().Any(x => x.RequiresBitangent());
if (needBitangent || needFragInputRegex.IsMatch("tangentWS") || RequiresTangent(useDataInputRegex) || activeNodeList.OfType<IMayRequireTangent>().Any(x => x.RequiresTangent()))
{
vayrings.Add(new Vayring()
{
attributeName = "tangentOS",
semantic = "TANGENT",
semanticType = SlotValueType.Vector4,
vayringName = "tangentWS",
type = SlotValueType.Vector3,
vertexCode = "output.tangentWS = TransformObjectToWorldDir(input.tangentOS.xyz);",
fragInputTarget = "tangentToWorld[0]",
pixelCode = string.Format("float3 {0} = normalize(fragInput.tangentToWorld[0]);", ShaderGeneratorNames.WorldSpaceTangent)
});
}
if (needBitangent || needFragInputRegex.IsMatch("normalWS") || RequiresNormal(useDataInputRegex) || activeNodeList.OfType<IMayRequireNormal>().Any(x => x.RequiresNormal()))
{
vayrings.Add(new Vayring()
{
attributeName = "normalOS",
semantic = "NORMAL",
vayringName = "normalWS",
type = SlotValueType.Vector3,
vertexCode = "output.normalWS = TransformObjectToWorldNormal(input.normalOS);",
fragInputTarget = "tangentToWorld[2]",
pixelCode = string.Format("float3 {0} = normalize(fragInput.tangentToWorld[2]);", ShaderGeneratorNames.WorldSpaceNormal)
});
}
if (needBitangent)
{
vayrings.Add(new Vayring()
{
vayringName = "bitangentWS",
type = SlotValueType.Vector3,
vertexCode = "output.bitangentWS = CreateBitangent(output.normalWS, output.tangentWS, input.tangentOS.w);",
fragInputTarget = "tangentToWorld[1]",
pixelCode = string.Format("float3 {0} = normalize(fragInput.tangentToWorld[1]);", ShaderGeneratorNames.WorldSpaceBitangent)
});
}
bool requireViewDirection = needFragInputRegex.IsMatch("viewDirectionWS") || activeNodeList.OfType<IMayRequireViewDirection>().Any(x => x.RequiresViewDirection());
if (requireViewDirection || needFragInputRegex.IsMatch("positionWS") || RequiresWorldPosition(useDataInputRegex) || activeNodeList.OfType<IMayRequireWorldPosition>().Any(x => x.RequiresWorldPosition()))
{
vayrings.Add(new Vayring()
{
vayringName = "positionWS",
type = SlotValueType.Vector3,
vertexCode = "output.positionWS = TransformObjectToWorld(input.positionOS);",
pixelCode = string.Format("float3 {0} = fragInput.positionWS;", ShaderGeneratorNames.WorldSpacePosition)
});
}
if (requireViewDirection)
{
vayrings.Add(new Vayring()
{
pixelCode = string.Format("float3 {0} = GetWorldSpaceNormalizeViewDir(fragInput.positionWS);", ShaderGeneratorNames.WorldSpaceViewDirection)
});
}
if (needFragInputRegex.IsMatch("vertexColor") || activeNodeList.OfType<IMayRequireVertexColor>().Any(x => x.RequiresVertexColor()))
{
vayrings.Add(new Vayring()
{
attributeName = "vertexColor",
semantic = "COLOR",
vayringName = "vertexColor",
type = SlotValueType.Vector4,
vertexCode = "output.vertexColor = input.vertexColor;",
pixelCode = string.Format("float4 {0} = fragInput.vertexColor;", "vertexColor")
});
}
Func<SlotValueType, int> _fnTypeToSize = o =>
{
switch (o)
{
case SlotValueType.Vector1: return 1;
case SlotValueType.Vector2: return 2;
case SlotValueType.Vector3: return 3;
case SlotValueType.Vector4: return 4;
}
return 0;
};
var packedVarying = new ShaderGenerator();
int totalSize = vayrings.Sum(x => _fnTypeToSize(x.type));
if (totalSize > 0)
{
var interpolatorCount = Mathf.Ceil((float)totalSize / 4.0f);
packedVarying.AddShaderChunk(string.Format("float4 interpolators[{0}] : TEXCOORD0;", (int)interpolatorCount), false);
}
var vayringVisitor = new ShaderGenerator();
var pixelShaderInitVisitor = new ShaderGenerator();
var vertexAttributeVisitor = new ShaderGenerator();
var vertexShaderBodyVisitor = new ShaderGenerator();
var packInterpolatorVisitor = new ShaderGenerator();
var unpackInterpolatorVisitor = new ShaderGenerator();
int currentIndex = 0;
int currentChannel = 0;
foreach (var vayring in vayrings)
{
vertexShaderBodyVisitor.AddShaderChunk(vayring.vertexCode, false);
pixelShaderInitVisitor.AddShaderChunk(vayring.pixelCode, false);
if (vayring.type != SlotValueType.Dynamic)
{
var typeSize = _fnTypeToSize(vayring.type);
if (!string.IsNullOrEmpty(vayring.attributeName))
{
var semanticType = vayring.semanticType != SlotValueType.Dynamic ? vayring.semanticType : vayring.type;
var semanticSize = _fnTypeToSize(semanticType);
vertexAttributeVisitor.AddShaderChunk(string.Format("float{0} {1} : {2};", semanticSize, vayring.attributeName, vayring.semantic), true);
}
vayringVisitor.AddShaderChunk(string.Format("float{0} {1};", typeSize, vayring.vayringName), false);
for (int channel = 0; channel < typeSize; ++channel)
{
var packed = string.Format("interpolators[{0}][{1}]", currentIndex, currentChannel);
var source = string.Format("{0}[{1}]", vayring.vayringName, channel);
var target = string.Format("{0}[{1}]", string.IsNullOrEmpty(vayring.fragInputTarget) ? vayring.vayringName : vayring.fragInputTarget, channel);
packInterpolatorVisitor.AddShaderChunk(string.Format("output.{0} = input.{1};", packed, source), false);
unpackInterpolatorVisitor.AddShaderChunk(string.Format("output.{0} = input.{1};", target, packed), false);
if (currentChannel == 3)
{
currentChannel = 0;
currentIndex++;
}
else
{
currentChannel++;
}
}
}
}
foreach (var node in activeNodeList.OfType<AbstractMaterialNode>())
{
if (node is IGeneratesFunction) (node as IGeneratesFunction).GenerateNodeFunction(shaderFunctionVisitor, mode);
if (node is IGenerateProperties)
{
(node as IGenerateProperties).GeneratePropertyBlock(propertyGenerator, mode);
(node as IGenerateProperties).GeneratePropertyUsages(propertyUsagesVisitor, mode);
}
}
var pixelShaderBodyVisitor = new ShaderGenerator[] { new ShaderGenerator(), new ShaderGenerator() };
foreach (var node in activeNodeList)
{
if (node is IGeneratesBodyCode)
(node as IGeneratesBodyCode).GenerateNodeCode(pixelShaderBodyVisitor[0], mode);
}
foreach (var slot in GetInputSlots<MaterialSlot>())
{
if (!slotIDList.Contains(slot.id))
continue;
var slotOutputName = slot.shaderOutputName;
var surfaceField = GetSurfaceType().GetFields().FirstOrDefault(o => o.Name == slotOutputName);
var builtinField = typeof(Builtin.BuiltinData).GetFields().FirstOrDefault(o => o.Name == slotOutputName);
var currentField = surfaceField != null ? surfaceField : builtinField;
string variableName = null;
int visitorIndex = 0;
var egdes = owner.GetEdges(slot.slotReference).ToArray();
if (egdes.Length == 1)
{
var outputRef = egdes[0].outputSlot;
var fromNode = owner.GetNodeFromGuid<AbstractMaterialNode>(outputRef.nodeGuid);
if (fromNode != null)
{
variableName = fromNode.GetVariableNameForSlot(outputRef.slotId);
}
}
else if (egdes.Length == 0)
{
var attributes = (SurfaceDataAttributes[])currentField.GetCustomAttributes(typeof(SurfaceDataAttributes), false);
var semantic = attributes.Length > 0 ? attributes[0].semantic : SurfaceDataAttributes.Semantic.None;
switch(semantic)
{
case SurfaceDataAttributes.Semantic.AmbientOcclusion:
case SurfaceDataAttributes.Semantic.Opacity:
variableName = "1.0f";
break;
case SurfaceDataAttributes.Semantic.Normal:
variableName = ShaderGeneratorNames.WorldSpaceNormal;
break;
case SurfaceDataAttributes.Semantic.Tangent:
variableName = ShaderGeneratorNames.WorldSpaceTangent;
break;
case SurfaceDataAttributes.Semantic.BakeDiffuseLighting:
variableName = string.Format("SampleBakedGI({0}, surfaceData.normalWS, {1}, {2})", ShaderGeneratorNames.WorldSpacePosition, ShaderGeneratorNames.GetUVName(UVChannel.uv1), ShaderGeneratorNames.GetUVName(UVChannel.uv2));
visitorIndex = 1; //it depends of surfaceData.normalWS, do it last
break;
default: break;
}
}
else
{
Debug.LogError("Unexpected graph : multiples edges connected to the same slot");
}
if (!string.IsNullOrEmpty(variableName))
{
pixelShaderBodyVisitor[visitorIndex].AddShaderChunk(string.Format("{0}.{1} = {2};", surfaceField != null ? "surfaceData" : "builtinData", slotOutputName, variableName), false);
}
}
var type = GetMatchingMaterialID();
var typeString = type.ToString();
var fieldsSurface = GetSurfaceType().GetFields();
var materialIdField = fieldsSurface.FirstOrDefault(o => o.FieldType.IsEnum);
if (materialIdField != null)
{
var enumValue = Enum.ToObject(materialIdField.FieldType, GetMatchingMaterialID()).ToString();
var define = string.Format("{0}_{1}", materialIdField.Name, UnityEditor.Experimental.Rendering.ShaderGeneratorHelper.InsertUnderscore(enumValue));
define = define.ToUpper();
pixelShaderBodyVisitor[1].AddShaderChunk(string.Format("surfaceData.{0} = {1};", materialIdField.Name, define), false);
}
var resultShader = litShareTemplate.Replace("${VaryingAttributes}", vayringVisitor.GetShaderString(1));
resultShader = resultShader.Replace("${PixelShaderInitialize}", pixelShaderInitVisitor.GetShaderString(1));
resultShader = resultShader.Replace("${PixelShaderBody}", pixelShaderBodyVisitor.Select(o => o.GetShaderString(1)).Aggregate((a, b) => a + b));
resultShader = resultShader.Replace("${VertexAttributes}", vertexAttributeVisitor.GetShaderString(1));
resultShader = resultShader.Replace("${PackedVaryingAttributes}", packedVarying.GetShaderString(1));
resultShader = resultShader.Replace("${PackingVaryingCode}", packInterpolatorVisitor.GetShaderString(1));
resultShader = resultShader.Replace("${UnpackVaryingCode}", unpackInterpolatorVisitor.GetShaderString(1));
resultShader = resultShader.Replace("${VertexShaderBody}", vertexShaderBodyVisitor.GetShaderString(1));
return resultShader;
}
public override string GetSubShader(GenerationMode mode, PropertyGenerator shaderPropertiesVisitor)
{
//TODOPAUL
throw new NotImplementedException();
}
public override string GetFullShader(GenerationMode mode, out List<PropertyGenerator.TextureInfo> configuredTextures)
{
configuredTextures = new List<PropertyGenerator.TextureInfo>();
var templateText = GetTemplateText();
var templatePassText = GetTemplatePassText();
var shaderPropertiesVisitor = new PropertyGenerator();
var propertyUsagesVisitor = new ShaderGenerator();
var shaderFunctionVisitor = new ShaderGenerator();
var templateToShader = new Dictionary<string, string>();
var findLitShareTemplate = new System.Text.RegularExpressions.Regex("#{TemplatePass.*}");
var findUseDataInput = new System.Text.RegularExpressions.Regex("useSurfaceData:{(.*?)}");
var findNeedFragInput = new System.Text.RegularExpressions.Regex("useFragInput:{(.*?)}");
foreach (System.Text.RegularExpressions.Match match in findLitShareTemplate.Matches(templateText))
{
if (match.Captures.Count > 0)
{
var capture = match.Captures[0].Value;
if (!templateToShader.ContainsKey(capture))
{
var useUseDataInputRegex = "";
if (findUseDataInput.IsMatch(capture))
{
var useInputMatch = findUseDataInput.Match(capture);
useUseDataInputRegex = useInputMatch.Groups.Count > 1 ? useInputMatch.Groups[1].Value : "";
}
var needFragInputRegex = "";
if (findNeedFragInput.IsMatch(capture))
{
var useInputMatch = findNeedFragInput.Match(capture);
needFragInputRegex = useInputMatch.Groups.Count > 1 ? useInputMatch.Groups[1].Value : "";
}
var generatedShader = GenerateLitDataTemplate(mode, useUseDataInputRegex, needFragInputRegex, shaderPropertiesVisitor, propertyUsagesVisitor, shaderFunctionVisitor, templatePassText);
templateToShader.Add(capture, generatedShader);
}
}
}
var resultShader = templateText.Replace("${ShaderName}", GetType() + guid.ToString());
resultShader = resultShader.Replace("${ShaderPropertiesHeader}", shaderPropertiesVisitor.GetShaderString(2));
resultShader = resultShader.Replace("${ShaderPropertyUsages}", propertyUsagesVisitor.GetShaderString(1));
resultShader = resultShader.Replace("${ShaderFunctions}", shaderFunctionVisitor.GetShaderString(1));
foreach (var entry in templateToShader)
{
resultShader = resultShader.Replace(entry.Key, entry.Value);
}
configuredTextures = shaderPropertiesVisitor.GetConfiguredTexutres();
resultShader = Regex.Replace(resultShader, @"\t", " ");
resultShader = Regex.Replace(resultShader, @"\r\n|\n\r|\n|\r", Environment.NewLine);
//Test
System.IO.File.WriteAllText("C:/Unity/Git_ScriptableRenderLoop/ScriptableRenderLoop/Assets/UnityShaderEditor/Editor/Testing/IntegrationTests/Graphs/Test.shader", resultShader);
return resultShader;
}
protected abstract Type GetSurfaceType();
protected abstract int GetMatchingMaterialID();
protected abstract string GetTemplateText();
protected abstract string GetTemplatePassText();
protected abstract bool IncludeBuiltInLitData();
}
[Serializable]
public abstract class LitNode : AbstractHDRenderPipelineMasterNode
{
protected override sealed Type GetSurfaceType()
{
return typeof(Lit.SurfaceData);
}
protected sealed override bool IncludeBuiltInLitData()
{
return true;
}
protected sealed override string GetTemplateText()
{
var templatePath = "Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.template";
if (!System.IO.File.Exists(templatePath))
return "";
return System.IO.File.ReadAllText(templatePath);
}
protected sealed override string GetTemplatePassText()
{
var templatePathPass = "Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitSharePass.template";
if (!System.IO.File.Exists(templatePathPass))
return "";
return System.IO.File.ReadAllText(templatePathPass);
}
}
[Serializable]
[Title("HDRenderPipeline/Lit/Standard")]
public class StandardtLitNode : LitNode
{
protected override int GetMatchingMaterialID()
{
return (int)Lit.MaterialId.LitStandard;
}
}
[Serializable]
[Title("HDRenderPipeline/Lit/SubsurfaceScattering")]
public class SubsurfaceScatteringLitNode : LitNode
{
protected override int GetMatchingMaterialID()
{
return (int)Lit.MaterialId.LitSSS;
}
}
[Serializable]
[Title("HDRenderPipeline/Lit/SubsurfaceClearCoat")]
public class SubsurfaceClearCoatLitNode : LitNode
{
protected override int GetMatchingMaterialID()
{
return (int)Lit.MaterialId.LitClearCoat;
}
}
[Serializable]
[Title("HDRenderPipeline/Lit/SpecularColor")]
public class SpecularColorLitNode : LitNode
{
protected override int GetMatchingMaterialID()
{
return (int)Lit.MaterialId.LitSpecular;
}
}
[Serializable]
[Title("HDRenderPipeline/Unlit")]
public class UnlitNode : AbstractHDRenderPipelineMasterNode
{
protected override Type GetSurfaceType()
{
return typeof(Unlit.SurfaceData);
}
protected override int GetMatchingMaterialID()
{
return -1;
}
protected sealed override bool IncludeBuiltInLitData()
{
return false;
}
protected sealed override string GetTemplateText()
{
var templatePath = "Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Unlit.template";
if (!System.IO.File.Exists(templatePath))
return "";
return System.IO.File.ReadAllText(templatePath);
}
protected sealed override string GetTemplatePassText()
{
var templatePathPass = "Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitSharePass.template";
if (!System.IO.File.Exists(templatePathPass))
return "";
return System.IO.File.ReadAllText(templatePathPass);
}
}
}

47
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Editor/LitGraphUI.cs


using UnityEngine;
using System;
using System.Linq;
using UnityEditor;
namespace UnityEngine.Experimental.Rendering.HDPipeline
{
class LitGraphUI : UnityEditor.Experimental.Rendering.HDPipeline.BaseLitGUI
{
private MaterialProperty[] genericProperties = new MaterialProperty[] { };
protected override void FindMaterialProperties(MaterialProperty[] props)
{
genericProperties = props.Where(p => (p.flags & MaterialProperty.PropFlags.HideInInspector) == 0 & !reservedProperties.Contains(p.name)).ToArray();
}
protected override void SetupMaterialKeywords(Material material)
{
}
protected override void ShaderInputGUI()
{
EditorGUI.indentLevel++;
foreach (var prop in genericProperties)
{
if ((prop.type & MaterialProperty.PropType.Texture) != 0)
{
m_MaterialEditor.TexturePropertySingleLine(new GUIContent(prop.name), prop);
}
else
{
m_MaterialEditor.ShaderProperty(prop, prop.name);
}
}
EditorGUI.indentLevel--;
}
protected override void ShaderInputOptionsGUI()
{
}
protected override bool ShouldEmissionBeEnabled(Material material)
{
return true;
}
}
}

107
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitSharePass.template


#ifdef UNITY_MATERIAL_LIT
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/Material/MaterialUtilities.hlsl"
#endif
#define UnpackNormal(x) UnpackNormalAG(x, 1.0)
#if SHADERPASS == SHADERPASS_LIGHT_TRANSPORT
CBUFFER_START(UnityMetaPass)
// x = use uv1 as raster position
// y = use uv2 as raster position
bool4 unity_MetaVertexControl;
// x = return albedo
// y = return normal
bool4 unity_MetaFragmentControl;
CBUFFER_END
// This was not in constant buffer in original unity, so keep outiside. But should be in as ShaderRenderPass frequency
float unity_OneOverOutputBoost;
float unity_MaxOutputValue;
#endif
void GetSurfaceAndBuiltinData(FragInputs fragInput, float3 V, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
{
ZERO_INITIALIZE(SurfaceData, surfaceData);
ZERO_INITIALIZE(BuiltinData, builtinData);
${PixelShaderInitialize}
${PixelShaderBody}
/* this clip could be integrated earlier for optimisation */
#ifdef _ALPHATEST_ON
clip(builtinData.opacity - _AlphaCutoff);
#endif
#ifdef UNITY_MATERIAL_LIT
/* HotFix to keep the PreIntegratedFGD shared texture sampler (TODO : workaround until we support independant sampler declaration)*/
surfaceData.specularOcclusion = max(surfaceData.specularOcclusion, 10e-5f);
#endif
}
#if 1 //lazy implementation, use define approach
//Sth with some define generated=
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderPass/VaryingMesh.hlsl"
#else
//WIP : old implementation (needs refactor to use this approach, should be better packing)
struct Attributes
{
float3 positionOS : POSITION;
${VertexAttributes}
};
struct Varyings
{
float4 positionCS;
${VaryingAttributes}
};
struct PackedVaryingsToPS
{
float4 positionCS : SV_Position;
${PackedVaryingAttributes}
};
PackedVaryingsToPS PackVaryings(Varyings input)
{
PackedVaryingsToPS output;
output.positionCS = input.positionCS;
${PackingVaryingCode}
return output;
}
FragInputs UnpackVaryingsMeshToFragInputs(PackedVaryingsToPS input)
{
FragInputs output;
ZERO_INITIALIZE(FragInputs, output);
output.unPositionSS = input.positionCS;
${UnpackVaryingCode}
return output;
}
PackedVaryingsToPS VertDefault(Attributes input)
{
Varyings output;
#if SHADERPASS == SHADERPASS_LIGHT_TRANSPORT
// Output UV coordinate in vertex shader
if (unity_MetaVertexControl.x)
{
input.positionOS.xy = input.texCoord1 * unity_LightmapST.xy + unity_LightmapST.zw;
// OpenGL right now needs to actually use incoming vertex position,
// so use it in a very dummy way
//v.positionOS.z = vertex.z > 0 ? 1.0e-4f : 0.0f;
}
if (unity_MetaVertexControl.y)
{
input.positionOS.xy = input.texCoord2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
// OpenGL right now needs to actually use incoming vertex position,
// so use it in a very dummy way
//v.positionOS.z = vertex.z > 0 ? 1.0e-4f : 0.0f;
}
#endif
output.positionCS = TransformWorldToHClip(TransformObjectToWorld(input.positionOS));
${VertexShaderBody}
return PackVaryings(output);
}
#endif

44
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Editor/UnlitGraphUI.cs


using UnityEngine;
using System;
using System.Linq;
namespace UnityEditor.Experimental.Rendering.HDPipeline
{
class UnlitGraphUI : BaseUnlitGUI
{
private MaterialProperty[] genericProperties = new MaterialProperty[] { };
protected override void FindMaterialProperties(MaterialProperty[] props)
{
genericProperties = props.Where(p => (p.flags & MaterialProperty.PropFlags.HideInInspector) == 0 & !reservedProperties.Contains(p.name)).ToArray();
}
protected override void SetupMaterialKeywords(Material material)
{
}
protected override void ShaderInputGUI()
{
EditorGUI.indentLevel++;
foreach (var prop in genericProperties)
{
if ((prop.type & MaterialProperty.PropType.Texture) != 0)
{
m_MaterialEditor.TexturePropertySingleLine(new GUIContent(prop.name), prop);
}
else
{
m_MaterialEditor.ShaderProperty(prop, prop.name);
}
}
EditorGUI.indentLevel--;
}
protected override void ShaderInputOptionsGUI()
{
}
protected override bool ShouldEmissionBeEnabled(Material material)
{
return true;
}
}
}

9
Assets/TestScenes/HDTest/GraphicTest/Common/Textures.meta


fileFormatVersion: 2
guid: 5d7f6a5ce24cb9a4c8af11d5f2b72af4
folderAsset: yes
timeCreated: 1485165228
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

9
Assets/TestScenes/HDTest/HDRenderLoopTest.meta


fileFormatVersion: 2
guid: bb067c1f82e9d8648b8909e905f6607b
folderAsset: yes
timeCreated: 1484331444
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

/Assets/ScriptableRenderLoop/HDRenderLoop/HDRenderloopMaterialGraph.cs.meta → /Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderloopMaterialGraph.cs.meta

/Assets/ScriptableRenderLoop/HDRenderLoop/Material/Lit/Lit.template.meta → /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.template.meta

/Assets/ScriptableRenderLoop/HDRenderLoop/Material/Lit/LitSharePass.template.meta → /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitSharePass.template.meta

/Assets/ScriptableRenderLoop/HDRenderLoop/Material/Lit/Lit.template → /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.template

/Assets/ScriptableRenderLoop/HDRenderLoop/Material/Lit/Editor/LitGraphUI.cs.meta → /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Editor/LitGraphUI.cs.meta

/Assets/ScriptableRenderLoop/HDRenderLoop/Material/Unlit/Unlit.template.meta → /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Unlit.template.meta

/Assets/ScriptableRenderLoop/HDRenderLoop/Material/Unlit/Unlit.template → /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Unlit.template

/Assets/ScriptableRenderLoop/HDRenderLoop/Material/Unlit/Editor/UnlitGraphUI.cs.meta → /Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Editor/UnlitGraphUI.cs.meta

正在加载...
取消
保存