您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
390 行
17 KiB
390 行
17 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using UnityEngine.Graphing;
|
|
|
|
namespace UnityEngine.MaterialGraph
|
|
{
|
|
public static class ShaderGeneratorNames
|
|
{
|
|
private static string[] UV = {"uv0", "uv1", "uv2", "uv3"};
|
|
public static int UVCount = 4;
|
|
|
|
public const string WorldSpaceNormal = "worldSpaceNormal";
|
|
public const string WorldSpaceBitangent = "worldSpaceBitangent";
|
|
public const string WorldSpaceTangent = "worldSpaceTangent";
|
|
public const string WorldSpacePosition = "worldPosition";
|
|
public const string WorldSpaceViewDirection = "worldSpaceViewDirection";
|
|
public const string TangentSpaceViewDirection = "tangentSpaceViewDirection";
|
|
public const string ScreenPosition = "screenPosition";
|
|
public const string VertexColor = "vertexColor";
|
|
|
|
|
|
public static string GetUVName(this UVChannel channel)
|
|
{
|
|
return UV[(int) channel];
|
|
}
|
|
}
|
|
|
|
public enum UVChannel
|
|
{
|
|
uv0 = 0,
|
|
uv1 = 1,
|
|
uv2 = 2,
|
|
uv3 = 3,
|
|
}
|
|
|
|
public class ShaderGenerator
|
|
{
|
|
private struct ShaderChunk
|
|
{
|
|
public ShaderChunk(int indentLevel, string shaderChunkString)
|
|
{
|
|
m_IndentLevel = indentLevel;
|
|
m_ShaderChunkString = shaderChunkString;
|
|
}
|
|
|
|
private readonly int m_IndentLevel;
|
|
private readonly string m_ShaderChunkString;
|
|
|
|
public int chunkIndentLevel
|
|
{
|
|
get { return m_IndentLevel; }
|
|
}
|
|
|
|
public string chunkString
|
|
{
|
|
get { return m_ShaderChunkString; }
|
|
}
|
|
}
|
|
|
|
private readonly List<ShaderChunk> m_ShaderChunks = new List<ShaderChunk>();
|
|
private int m_IndentLevel;
|
|
private string m_Pragma = string.Empty;
|
|
|
|
public void AddPragmaChunk(string s)
|
|
{
|
|
m_Pragma += s;
|
|
}
|
|
|
|
public string GetPragmaString()
|
|
{
|
|
return m_Pragma;
|
|
}
|
|
|
|
public void AddShaderChunk(string s, bool unique)
|
|
{
|
|
if (string.IsNullOrEmpty(s))
|
|
return;
|
|
|
|
if (unique && m_ShaderChunks.Any(x => x.chunkString == s))
|
|
return;
|
|
|
|
m_ShaderChunks.Add(new ShaderChunk(m_IndentLevel, s));
|
|
}
|
|
|
|
public void Indent()
|
|
{
|
|
m_IndentLevel++;
|
|
}
|
|
|
|
public void Deindent()
|
|
{
|
|
m_IndentLevel = Math.Max(0, m_IndentLevel - 1);
|
|
}
|
|
|
|
public string GetShaderString(int baseIndentLevel)
|
|
{
|
|
var sb = new StringBuilder();
|
|
foreach (var shaderChunk in m_ShaderChunks)
|
|
{
|
|
var lines = shaderChunk.chunkString.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
|
for (int index = 0; index < lines.Length; index++)
|
|
{
|
|
var line = lines[index];
|
|
for (var i = 0; i < shaderChunk.chunkIndentLevel + baseIndentLevel; i++)
|
|
sb.Append("\t");
|
|
|
|
sb.AppendLine(line);
|
|
}
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
|
|
internal static string GetTemplatePath(string templateName)
|
|
{
|
|
var path = new List<string>
|
|
{
|
|
Application.dataPath,
|
|
"UnityShaderEditor",
|
|
"Editor",
|
|
"Templates"
|
|
};
|
|
|
|
string result = path[0];
|
|
for (int i = 1; i < path.Count; i++)
|
|
result = Path.Combine(result, path[i]);
|
|
|
|
result = Path.Combine(result, templateName);
|
|
return result;
|
|
}
|
|
|
|
private const string kErrorString = @"ERROR!";
|
|
|
|
public static string AdaptNodeOutput(AbstractMaterialNode node, int outputSlotId, ConcreteSlotValueType convertToType, bool textureSampleUVHack = false)
|
|
{
|
|
var outputSlot = node.FindOutputSlot<MaterialSlot>(outputSlotId);
|
|
|
|
if (outputSlot == null)
|
|
return kErrorString;
|
|
|
|
var convertFromType = outputSlot.concreteValueType;
|
|
var rawOutput = node.GetVariableNameForSlot(outputSlotId);
|
|
if (convertFromType == convertToType)
|
|
return rawOutput;
|
|
|
|
switch (convertToType)
|
|
{
|
|
case ConcreteSlotValueType.Vector1:
|
|
return string.Format("({0}).x", rawOutput);
|
|
case ConcreteSlotValueType.Vector2:
|
|
switch (convertFromType)
|
|
{
|
|
case ConcreteSlotValueType.Vector1:
|
|
return string.Format("({0}{1})", rawOutput, ".xx");
|
|
case ConcreteSlotValueType.Vector3:
|
|
case ConcreteSlotValueType.Vector4:
|
|
return string.Format("({0}.xy)", rawOutput);
|
|
default:
|
|
return kErrorString;
|
|
}
|
|
case ConcreteSlotValueType.Vector3:
|
|
switch (convertFromType)
|
|
{
|
|
case ConcreteSlotValueType.Vector1:
|
|
return string.Format("({0}{1})", rawOutput, ".xxx");
|
|
case ConcreteSlotValueType.Vector4:
|
|
return string.Format("({0}.xyz)", rawOutput);
|
|
default:
|
|
return kErrorString;
|
|
}
|
|
case ConcreteSlotValueType.Vector4:
|
|
switch (convertFromType)
|
|
{
|
|
case ConcreteSlotValueType.Vector1:
|
|
return string.Format("({0}{1})", rawOutput, ".xxxx");
|
|
default:
|
|
return kErrorString;
|
|
}
|
|
default:
|
|
return kErrorString;
|
|
}
|
|
}
|
|
|
|
public static string AdaptNodeOutputForPreview(AbstractMaterialNode node, int outputSlotId)
|
|
{
|
|
var outputSlot = node.FindOutputSlot<MaterialSlot>(outputSlotId);
|
|
|
|
if (outputSlot == null)
|
|
return kErrorString;
|
|
|
|
var convertFromType = outputSlot.concreteValueType;
|
|
|
|
var rawOutput = node.GetVariableNameForSlot(outputSlotId);
|
|
|
|
// preview is always dimension 4, and we always ignore alpha
|
|
switch (convertFromType)
|
|
{
|
|
case ConcreteSlotValueType.Vector1:
|
|
return string.Format("half4({0}, {0}, {0}, 1.0)", rawOutput);
|
|
case ConcreteSlotValueType.Vector2:
|
|
return string.Format("half4({0}.x, {0}.y, 0.0, 1.0)", rawOutput);
|
|
case ConcreteSlotValueType.Vector3:
|
|
return string.Format("half4({0}.x, {0}.y, {0}.z, 1.0)", rawOutput);
|
|
case ConcreteSlotValueType.Vector4:
|
|
return string.Format("half4({0}.x, {0}.y, {0}.z, 1.0)", rawOutput);
|
|
default:
|
|
return kErrorString;
|
|
}
|
|
}
|
|
|
|
public int numberOfChunks
|
|
{
|
|
get { return m_ShaderChunks.Count; }
|
|
}
|
|
|
|
public static string GeneratePreviewShader(AbstractMaterialNode node, out PreviewMode generatedShaderMode)
|
|
{
|
|
if (!node.GetOutputSlots<MaterialSlot>().Any())
|
|
{
|
|
generatedShaderMode = PreviewMode.Preview2D;
|
|
return string.Empty;
|
|
}
|
|
|
|
// figure out what kind of preview we want!
|
|
var activeNodeList = ListPool<INode>.Get();
|
|
NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, node);
|
|
generatedShaderMode = PreviewMode.Preview2D;
|
|
|
|
if (activeNodeList.OfType<AbstractMaterialNode>().Any(x => x.previewMode == PreviewMode.Preview3D))
|
|
generatedShaderMode = PreviewMode.Preview3D;
|
|
|
|
string templateLocation = GetTemplatePath("2DPreview.template");
|
|
if (!File.Exists(templateLocation))
|
|
return null;
|
|
|
|
string template = File.ReadAllText(templateLocation);
|
|
|
|
var shaderBodyVisitor = new ShaderGenerator();
|
|
var shaderFunctionVisitor = new ShaderGenerator();
|
|
var shaderPropertiesVisitor = new PropertyGenerator();
|
|
var shaderPropertyUsagesVisitor = new ShaderGenerator();
|
|
|
|
var shaderName = "Hidden/PreviewShader/" + node.GetVariableNameForSlot(node.GetOutputSlots<MaterialSlot>().First().id);
|
|
|
|
|
|
var shaderInputVisitor = new ShaderGenerator();
|
|
var vertexShaderBlock = new ShaderGenerator();
|
|
|
|
// always add color because why not.
|
|
shaderInputVisitor.AddShaderChunk("float4 color : COLOR;", true);
|
|
|
|
vertexShaderBlock.AddShaderChunk("float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;", true);
|
|
vertexShaderBlock.AddShaderChunk("float3 viewDir = UnityWorldSpaceViewDir(worldPos);", true);
|
|
vertexShaderBlock.AddShaderChunk("float4 screenPos = ComputeScreenPos(UnityObjectToClipPos(v.vertex));", true);
|
|
vertexShaderBlock.AddShaderChunk("float3 worldNormal = UnityObjectToWorldNormal(v.normal);", true);
|
|
|
|
bool requiresBitangent = activeNodeList.OfType<IMayRequireBitangent>().Any(x => x.RequiresBitangent());
|
|
bool requiresTangent = activeNodeList.OfType<IMayRequireTangent>().Any(x => x.RequiresTangent());
|
|
bool requiresViewDirTangentSpace = activeNodeList.OfType<IMayRequireViewDirectionTangentSpace>().Any(x => x.RequiresViewDirectionTangentSpace());
|
|
bool requiresViewDir = activeNodeList.OfType<IMayRequireViewDirection>().Any(x => x.RequiresViewDirection());
|
|
bool requiresWorldPos = activeNodeList.OfType<IMayRequireWorldPosition>().Any(x => x.RequiresWorldPosition());
|
|
bool requiresNormal = activeNodeList.OfType<IMayRequireNormal>().Any(x => x.RequiresNormal());
|
|
bool requiresScreenPosition = activeNodeList.OfType<IMayRequireScreenPosition>().Any(x => x.RequiresScreenPosition());
|
|
bool requiresVertexColor = activeNodeList.OfType<IMayRequireVertexColor>().Any(x => x.RequiresVertexColor());
|
|
|
|
// view directions calculated from world position
|
|
if (requiresWorldPos || requiresViewDir || requiresViewDirTangentSpace)
|
|
{
|
|
shaderInputVisitor.AddShaderChunk("float3 worldPos : TEXCOORD0;", true);
|
|
vertexShaderBlock.AddShaderChunk("o.worldPos = worldPos;", true);
|
|
shaderBodyVisitor.AddShaderChunk("float3 " + ShaderGeneratorNames.WorldSpacePosition + " = IN.worldPos;", true);
|
|
}
|
|
|
|
if (requiresBitangent || requiresNormal || requiresViewDirTangentSpace)
|
|
{
|
|
shaderInputVisitor.AddShaderChunk("float3 worldNormal : TEXCOORD1;", true);
|
|
vertexShaderBlock.AddShaderChunk("o.worldNormal = worldNormal;", true);
|
|
shaderBodyVisitor.AddShaderChunk("float3 " + ShaderGeneratorNames.WorldSpaceNormal + " = normalize(IN.worldNormal);", true);
|
|
}
|
|
|
|
for (int uvIndex = 0; uvIndex < ShaderGeneratorNames.UVCount; ++uvIndex)
|
|
{
|
|
var channel = (UVChannel) uvIndex;
|
|
if (activeNodeList.OfType<IMayRequireMeshUV>().Any(x => x.RequiresMeshUV(channel)))
|
|
{
|
|
shaderInputVisitor.AddShaderChunk(string.Format("half4 meshUV{0} : TEXCOORD{1};", uvIndex, (uvIndex + 5)), true);
|
|
vertexShaderBlock.AddShaderChunk(string.Format("o.meshUV{0} = v.texcoord{1};", uvIndex, uvIndex == 0 ? "" : uvIndex.ToString()), true);
|
|
shaderBodyVisitor.AddShaderChunk(string.Format("half4 {0} = IN.meshUV{1};", channel.GetUVName(), uvIndex), true);
|
|
}
|
|
}
|
|
|
|
if (requiresViewDir || requiresViewDirTangentSpace)
|
|
{
|
|
shaderBodyVisitor.AddShaderChunk(
|
|
"float3 "
|
|
+ ShaderGeneratorNames.WorldSpaceViewDirection
|
|
+ " = normalize(UnityWorldSpaceViewDir("
|
|
+ ShaderGeneratorNames.WorldSpacePosition
|
|
+ "));", true);
|
|
}
|
|
|
|
if (requiresScreenPosition)
|
|
{
|
|
shaderInputVisitor.AddShaderChunk("float4 screenPos : TEXCOORD3;", true);
|
|
vertexShaderBlock.AddShaderChunk("o.screenPos = screenPos;", true);
|
|
shaderBodyVisitor.AddShaderChunk("half4 " + ShaderGeneratorNames.ScreenPosition + " = IN.screenPos;", true);
|
|
}
|
|
|
|
if (requiresBitangent || requiresViewDirTangentSpace || requiresTangent)
|
|
{
|
|
shaderInputVisitor.AddShaderChunk("float4 worldTangent : TEXCOORD4;", true);
|
|
vertexShaderBlock.AddShaderChunk("o.worldTangent = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);", true);
|
|
shaderBodyVisitor.AddShaderChunk("float3 " + ShaderGeneratorNames.WorldSpaceTangent + " = normalize(IN.worldTangent.xyz);", true);
|
|
}
|
|
|
|
if (requiresBitangent || requiresViewDirTangentSpace)
|
|
{
|
|
shaderBodyVisitor.AddShaderChunk(string.Format("float3 {0} = cross({1}, {2}) * IN.worldTangent.w;", ShaderGeneratorNames.WorldSpaceBitangent, ShaderGeneratorNames.WorldSpaceNormal, ShaderGeneratorNames.WorldSpaceTangent), true);
|
|
}
|
|
|
|
if (requiresViewDirTangentSpace)
|
|
{
|
|
shaderBodyVisitor.AddShaderChunk(
|
|
"float3 " + ShaderGeneratorNames.TangentSpaceViewDirection + ";", true);
|
|
|
|
shaderBodyVisitor.AddShaderChunk(
|
|
ShaderGeneratorNames.TangentSpaceViewDirection + ".x = dot(" +
|
|
ShaderGeneratorNames.WorldSpaceViewDirection + "," +
|
|
ShaderGeneratorNames.WorldSpaceTangent + ");", true);
|
|
|
|
shaderBodyVisitor.AddShaderChunk(
|
|
ShaderGeneratorNames.TangentSpaceViewDirection + ".y = dot(" +
|
|
ShaderGeneratorNames.WorldSpaceViewDirection + "," +
|
|
ShaderGeneratorNames.WorldSpaceBitangent + ");", true);
|
|
|
|
shaderBodyVisitor.AddShaderChunk(
|
|
ShaderGeneratorNames.TangentSpaceViewDirection + ".z = dot(" +
|
|
ShaderGeneratorNames.WorldSpaceViewDirection + "," +
|
|
ShaderGeneratorNames.WorldSpaceNormal + ");", true);
|
|
}
|
|
|
|
if (requiresVertexColor)
|
|
{
|
|
vertexShaderBlock.AddShaderChunk("o.color = v.color;", true);
|
|
shaderBodyVisitor.AddShaderChunk("float4 " + ShaderGeneratorNames.VertexColor + " = IN.color;", true);
|
|
}
|
|
|
|
var generationMode = GenerationMode.Preview;
|
|
foreach (var activeNode in activeNodeList.OfType<AbstractMaterialNode>())
|
|
{
|
|
if (activeNode is IGeneratesFunction)
|
|
(activeNode as IGeneratesFunction).GenerateNodeFunction(shaderFunctionVisitor, generationMode);
|
|
if (activeNode is IGeneratesBodyCode)
|
|
(activeNode as IGeneratesBodyCode).GenerateNodeCode(shaderBodyVisitor, generationMode);
|
|
|
|
activeNode.GeneratePropertyBlock(shaderPropertiesVisitor, generationMode);
|
|
activeNode.GeneratePropertyUsages(shaderPropertyUsagesVisitor, generationMode);
|
|
}
|
|
|
|
shaderBodyVisitor.AddShaderChunk("return " + AdaptNodeOutputForPreview(node, node.GetOutputSlots<MaterialSlot>().First().id) + ";", true);
|
|
|
|
ListPool<INode>.Release(activeNodeList);
|
|
|
|
template = template.Replace("${ShaderName}", shaderName);
|
|
template = template.Replace("${ShaderPropertiesHeader}", shaderPropertiesVisitor.GetShaderString(2));
|
|
template = template.Replace("${ShaderPropertyUsages}", shaderPropertyUsagesVisitor.GetShaderString(3));
|
|
template = template.Replace("${ShaderInputs}", shaderInputVisitor.GetShaderString(4));
|
|
template = template.Replace("${ShaderFunctions}", shaderFunctionVisitor.GetShaderString(3));
|
|
template = template.Replace("${VertexShaderBody}", vertexShaderBlock.GetShaderString(4));
|
|
template = template.Replace("${PixelShaderBody}", shaderBodyVisitor.GetShaderString(4));
|
|
|
|
string vertexShaderBody = vertexShaderBlock.GetShaderString(4);
|
|
if (vertexShaderBody.Length > 0)
|
|
{
|
|
template = template.Replace("${VertexShaderDecl}", "vertex:vert");
|
|
template = template.Replace("${VertexShaderBody}", vertexShaderBody);
|
|
}
|
|
else
|
|
{
|
|
template = template.Replace("${VertexShaderDecl}", "");
|
|
template = template.Replace("${VertexShaderBody}", vertexShaderBody);
|
|
}
|
|
|
|
return Regex.Replace(template, @"\r\n|\n\r|\n|\r", Environment.NewLine);
|
|
}
|
|
}
|
|
}
|