您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
388 行
17 KiB
388 行
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 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 (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();
|
|
}
|
|
|
|
private 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, textureSampleUVHack ? ".xx" : string.Empty);
|
|
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, textureSampleUVHack ? ".xxx" : string.Empty);
|
|
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, textureSampleUVHack ? ".xxxx" : string.Empty);
|
|
default:
|
|
return kErrorString;
|
|
}
|
|
default:
|
|
return kErrorString;
|
|
}
|
|
}
|
|
|
|
public static string AdaptNodeOutputForPreview(AbstractMaterialNode node, int outputSlotId, ConcreteSlotValueType convertToType)
|
|
{
|
|
var outputSlot = node.FindOutputSlot<MaterialSlot>(outputSlotId);
|
|
|
|
if (outputSlot == null)
|
|
return kErrorString;
|
|
|
|
var convertFromType = outputSlot.concreteValueType;
|
|
|
|
// if we are in a normal situation, just convert!
|
|
if (convertFromType >= convertToType || convertFromType == ConcreteSlotValueType.Vector1)
|
|
return AdaptNodeOutput(node, outputSlotId, convertToType);
|
|
|
|
var rawOutput = node.GetVariableNameForSlot(outputSlotId);
|
|
|
|
// otherwise we need to pad output for the preview!
|
|
switch (convertToType)
|
|
{
|
|
case ConcreteSlotValueType.Vector3:
|
|
switch (convertFromType)
|
|
{
|
|
case ConcreteSlotValueType.Vector2:
|
|
return string.Format("half3({0}.x, {0}.y, 0.0)", rawOutput);
|
|
default:
|
|
return kErrorString;
|
|
}
|
|
case ConcreteSlotValueType.Vector4:
|
|
switch (convertFromType)
|
|
{
|
|
case ConcreteSlotValueType.Vector2:
|
|
return string.Format("half4({0}.x, {0}.y, 0.0, 0.0)", rawOutput);
|
|
case ConcreteSlotValueType.Vector3:
|
|
return string.Format("half4({0}.x, {0}.y, {0}.z, 0.0)", rawOutput);
|
|
default:
|
|
return kErrorString;
|
|
}
|
|
default:
|
|
return kErrorString;
|
|
}
|
|
}
|
|
|
|
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);
|
|
var generationMode = GenerationMode.Preview2D;
|
|
generatedShaderMode = PreviewMode.Preview2D;
|
|
|
|
if (activeNodeList.OfType<AbstractMaterialNode>().Any(x => x.previewMode == PreviewMode.Preview3D))
|
|
{
|
|
generationMode = GenerationMode.Preview3D;
|
|
generatedShaderMode = PreviewMode.Preview3D;
|
|
}
|
|
|
|
string templateLocation = GetTemplatePath(generationMode == GenerationMode.Preview2D ? "2DPreview.template" : "3DPreview.template");
|
|
if (!File.Exists(templateLocation))
|
|
return null;
|
|
|
|
string template = File.ReadAllText(templateLocation);
|
|
|
|
var shaderBodyVisitor = new ShaderGenerator();
|
|
var shaderInputVisitor = new ShaderGenerator();
|
|
var shaderFunctionVisitor = new ShaderGenerator();
|
|
var shaderPropertiesVisitor = new PropertyGenerator();
|
|
var shaderPropertyUsagesVisitor = new ShaderGenerator();
|
|
var vertexShaderBlock = new ShaderGenerator();
|
|
|
|
var shaderName = "Hidden/PreviewShader/" + node.GetVariableNameForSlot(node.GetOutputSlots<MaterialSlot>().First().id);
|
|
|
|
foreach (var activeNode in activeNodeList.OfType<AbstractMaterialNode>())
|
|
{
|
|
if (activeNode is IGeneratesFunction)
|
|
(activeNode as IGeneratesFunction).GenerateNodeFunction(shaderFunctionVisitor, generationMode);
|
|
if (activeNode is IGeneratesVertexToFragmentBlock)
|
|
(activeNode as IGeneratesVertexToFragmentBlock).GenerateVertexToFragmentBlock(shaderInputVisitor, generationMode);
|
|
if (activeNode is IGeneratesBodyCode)
|
|
(activeNode as IGeneratesBodyCode).GenerateNodeCode(shaderBodyVisitor, generationMode);
|
|
if (activeNode is IGeneratesVertexShaderBlock)
|
|
(activeNode as IGeneratesVertexShaderBlock).GenerateVertexShaderBlock(vertexShaderBlock, generationMode);
|
|
|
|
activeNode.GeneratePropertyBlock(shaderPropertiesVisitor, generationMode);
|
|
activeNode.GeneratePropertyUsages(shaderPropertyUsagesVisitor, generationMode);
|
|
}
|
|
|
|
if (shaderInputVisitor.numberOfChunks == 0)
|
|
{
|
|
shaderInputVisitor.AddShaderChunk("float4 color : COLOR;", true);
|
|
}
|
|
|
|
if (generationMode == GenerationMode.Preview2D)
|
|
shaderBodyVisitor.AddShaderChunk("return " + AdaptNodeOutputForPreview(node, node.GetOutputSlots<MaterialSlot>().First().id, ConcreteSlotValueType.Vector4) + ";", true);
|
|
else
|
|
shaderBodyVisitor.AddShaderChunk("o.Emission = " + AdaptNodeOutputForPreview(node, node.GetOutputSlots<MaterialSlot>().First().id, ConcreteSlotValueType.Vector3) + ";", true);
|
|
|
|
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);
|
|
}
|
|
|
|
private static void GenerateSurfaceShaderInternal(
|
|
PixelShaderNode pixelNode,
|
|
ShaderGenerator shaderBody,
|
|
ShaderGenerator inputStruct,
|
|
ShaderGenerator lightFunction,
|
|
ShaderGenerator surfaceOutput,
|
|
ShaderGenerator nodeFunction,
|
|
PropertyGenerator shaderProperties,
|
|
ShaderGenerator propertyUsages,
|
|
ShaderGenerator vertexShader,
|
|
bool isPreview)
|
|
{
|
|
pixelNode.GenerateLightFunction(lightFunction);
|
|
pixelNode.GenerateSurfaceOutput(surfaceOutput);
|
|
|
|
var genMode = isPreview ? GenerationMode.Preview3D : GenerationMode.SurfaceShader;
|
|
|
|
var activeNodes = new List<INode>();
|
|
NodeUtils.DepthFirstCollectNodesFromNode(activeNodes, pixelNode);
|
|
var activeMaterialNodes = activeNodes.OfType<AbstractMaterialNode>();
|
|
|
|
foreach (var node in activeMaterialNodes)
|
|
{
|
|
if (node is IGeneratesFunction) (node as IGeneratesFunction).GenerateNodeFunction(nodeFunction, genMode);
|
|
if (node is IGeneratesVertexToFragmentBlock) (node as IGeneratesVertexToFragmentBlock).GenerateVertexToFragmentBlock(inputStruct, genMode);
|
|
if (node is IGeneratesVertexShaderBlock) (node as IGeneratesVertexShaderBlock).GenerateVertexShaderBlock(vertexShader, genMode);
|
|
|
|
if (node is IGenerateProperties)
|
|
{
|
|
(node as IGenerateProperties).GeneratePropertyBlock(shaderProperties, genMode);
|
|
(node as IGenerateProperties).GeneratePropertyUsages(propertyUsages, genMode);
|
|
}
|
|
}
|
|
|
|
pixelNode.GenerateNodeCode(shaderBody, genMode);
|
|
}
|
|
|
|
public static string GenerateSurfaceShader(PixelShaderNode node, MaterialOptions options, string shaderName, bool isPreview, out List<PropertyGenerator.TextureInfo> configuredTextures)
|
|
{
|
|
var templateLocation = GetTemplatePath("shader.template");
|
|
|
|
if (!File.Exists(templateLocation))
|
|
{
|
|
configuredTextures = new List<PropertyGenerator.TextureInfo>();
|
|
return string.Empty;
|
|
}
|
|
|
|
var templateText = File.ReadAllText(templateLocation);
|
|
|
|
var shaderBodyVisitor = new ShaderGenerator();
|
|
var shaderInputVisitor = new ShaderGenerator();
|
|
var shaderLightFunctionVisitor = new ShaderGenerator();
|
|
var shaderOutputSurfaceVisitor = new ShaderGenerator();
|
|
var shaderFunctionVisitor = new ShaderGenerator();
|
|
var shaderPropertiesVisitor = new PropertyGenerator();
|
|
var shaderPropertyUsagesVisitor = new ShaderGenerator();
|
|
var vertexShaderBlock = new ShaderGenerator();
|
|
|
|
GenerateSurfaceShaderInternal(
|
|
node,
|
|
shaderBodyVisitor,
|
|
shaderInputVisitor,
|
|
shaderLightFunctionVisitor,
|
|
shaderOutputSurfaceVisitor,
|
|
shaderFunctionVisitor,
|
|
shaderPropertiesVisitor,
|
|
shaderPropertyUsagesVisitor,
|
|
vertexShaderBlock,
|
|
isPreview);
|
|
|
|
if (shaderInputVisitor.numberOfChunks == 0)
|
|
{
|
|
shaderInputVisitor.AddShaderChunk("float4 color : COLOR;", true);
|
|
}
|
|
|
|
var tagsVisitor = new ShaderGenerator();
|
|
var blendingVisitor = new ShaderGenerator();
|
|
var cullingVisitor = new ShaderGenerator();
|
|
var zTestVisitor = new ShaderGenerator();
|
|
var zWriteVisitor = new ShaderGenerator();
|
|
|
|
options.GetTags(tagsVisitor);
|
|
options.GetBlend(blendingVisitor);
|
|
options.GetCull(cullingVisitor);
|
|
options.GetDepthTest(zTestVisitor);
|
|
options.GetDepthWrite(zWriteVisitor);
|
|
|
|
var resultShader = templateText.Replace("${ShaderName}", shaderName);
|
|
resultShader = resultShader.Replace("${ShaderPropertiesHeader}", shaderPropertiesVisitor.GetShaderString(2));
|
|
resultShader = resultShader.Replace("${ShaderPropertyUsages}", shaderPropertyUsagesVisitor.GetShaderString(2));
|
|
resultShader = resultShader.Replace("${LightingFunctionName}", shaderLightFunctionVisitor.GetPragmaString());
|
|
resultShader = resultShader.Replace("${LightingFunction}", shaderLightFunctionVisitor.GetShaderString(2));
|
|
resultShader = resultShader.Replace("${SurfaceOutputStructureName}", shaderOutputSurfaceVisitor.GetPragmaString());
|
|
resultShader = resultShader.Replace("${ShaderFunctions}", shaderFunctionVisitor.GetShaderString(2));
|
|
resultShader = resultShader.Replace("${ShaderInputs}", shaderInputVisitor.GetShaderString(3));
|
|
resultShader = resultShader.Replace("${PixelShaderBody}", shaderBodyVisitor.GetShaderString(3));
|
|
resultShader = resultShader.Replace("${Tags}", tagsVisitor.GetShaderString(2));
|
|
resultShader = resultShader.Replace("${Blending}", blendingVisitor.GetShaderString(2));
|
|
resultShader = resultShader.Replace("${Culling}", cullingVisitor.GetShaderString(2));
|
|
resultShader = resultShader.Replace("${ZTest}", zTestVisitor.GetShaderString(2));
|
|
resultShader = resultShader.Replace("${ZWrite}", zWriteVisitor.GetShaderString(2));
|
|
|
|
string vertexShaderBody = vertexShaderBlock.GetShaderString(3);
|
|
if (vertexShaderBody.Length > 0)
|
|
{
|
|
resultShader = resultShader.Replace("${VertexShaderDecl}", "vertex:vert");
|
|
resultShader = resultShader.Replace("${VertexShaderBody}", vertexShaderBody);
|
|
}
|
|
else
|
|
{
|
|
resultShader = resultShader.Replace("${VertexShaderDecl}", "");
|
|
resultShader = resultShader.Replace("${VertexShaderBody}", "");
|
|
}
|
|
|
|
configuredTextures = shaderPropertiesVisitor.GetConfiguredTexutres();
|
|
|
|
return Regex.Replace(resultShader, @"\r\n|\n\r|\n|\r", Environment.NewLine);
|
|
}
|
|
|
|
public int numberOfChunks
|
|
{
|
|
get { return m_ShaderChunks.Count; }
|
|
}
|
|
}
|
|
}
|