Boat Attack使用了Universal RP的许多新图形功能,可以用于探索 Universal RP 的使用方式和技巧。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

1021 行
47 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEditor.Graphing;
namespace UnityEditor.ShaderGraph
{
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 = false)
{
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 AddGenerator(ShaderGenerator generator)
{
foreach (ShaderChunk chunk in generator.m_ShaderChunks)
{
m_ShaderChunks.Add(new ShaderChunk(m_IndentLevel + chunk.chunkIndentLevel, chunk.chunkString));
}
}
public void Indent()
{
m_IndentLevel++;
}
public void Deindent()
{
m_IndentLevel = Math.Max(0, m_IndentLevel - 1);
}
public string GetShaderString(int baseIndentLevel, bool finalNewline = true)
{
bool appendedNewline = false;
var sb = new StringBuilder();
foreach (var shaderChunk in m_ShaderChunks)
{
// TODO: regex split is a slow way to handle this .. should build an iterator instead
var lines = Regex.Split(shaderChunk.chunkString, Environment.NewLine);
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.Append(" "); // unity convention use space instead of tab...
}
sb.AppendLine(line);
appendedNewline = true;
}
}
if (appendedNewline && !finalNewline)
{
// remove last newline if we didn't want it
sb.Length -= Environment.NewLine.Length;
}
return sb.ToString();
}
public static string GetTemplatePath(string templateName)
{
var path = new List<string>
{
DefaultShaderIncludes.GetAssetsPackagePath() ?? Path.GetFullPath("Packages/com.unity.shadergraph"),
"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);
if (File.Exists(result))
return result;
return string.Empty;
}
private const string kErrorString = @"ERROR!";
public static string AdaptNodeOutput(AbstractMaterialNode node, int outputSlotId, ConcreteSlotValueType convertToType)
{
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}.xx)", rawOutput);
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}.xxx)", rawOutput);
case ConcreteSlotValueType.Vector2:
return string.Format("($precision3({0}, 0.0))", rawOutput);
case ConcreteSlotValueType.Vector4:
return string.Format("({0}.xyz)", rawOutput);
default:
return kErrorString;
}
case ConcreteSlotValueType.Vector4:
switch (convertFromType)
{
case ConcreteSlotValueType.Vector1:
return string.Format("({0}.xxxx)", rawOutput);
case ConcreteSlotValueType.Vector2:
return string.Format("($precision4({0}, 0.0, 1.0))", rawOutput);
case ConcreteSlotValueType.Vector3:
return string.Format("($precision4({0}, 1.0))", rawOutput);
default:
return kErrorString;
}
case ConcreteSlotValueType.Matrix3:
return rawOutput;
case ConcreteSlotValueType.Matrix2:
return rawOutput;
default:
return kErrorString;
}
}
public static string AdaptNodeOutputForPreview(AbstractMaterialNode node, int outputSlotId)
{
var rawOutput = node.GetVariableNameForSlot(outputSlotId);
return AdaptNodeOutputForPreview(node, outputSlotId, rawOutput);
}
public static string AdaptNodeOutputForPreview(AbstractMaterialNode node, int slotId, string variableName)
{
var slot = node.FindSlot<MaterialSlot>(slotId);
if (slot == null)
return kErrorString;
var convertFromType = slot.concreteValueType;
// preview is always dimension 4
switch (convertFromType)
{
case ConcreteSlotValueType.Vector1:
return string.Format("half4({0}, {0}, {0}, 1.0)", variableName);
case ConcreteSlotValueType.Vector2:
return string.Format("half4({0}.x, {0}.y, 0.0, 1.0)", variableName);
case ConcreteSlotValueType.Vector3:
return string.Format("half4({0}.x, {0}.y, {0}.z, 1.0)", variableName);
case ConcreteSlotValueType.Vector4:
return string.Format("half4({0}.x, {0}.y, {0}.z, 1.0)", variableName);
case ConcreteSlotValueType.Boolean:
return string.Format("half4({0}, {0}, {0}, 1.0)", variableName);
default:
return "half4(0, 0, 0, 0)";
}
}
public int numberOfChunks
{
get { return m_ShaderChunks.Count; }
}
public enum InputType
{
Position,
Vector,
Normal
}
public struct TransformDesc
{
public TransformDesc(string name)
{
this.name = name;
transpose = false;
}
public TransformDesc(string name, bool transpose)
{
this.name = name;
this.transpose = transpose;
}
public string name;
public bool transpose;
}
static TransformDesc[, ][] m_transforms = null;
static TransformDesc[] GetTransformPath(CoordinateSpace from, CoordinateSpace to)
{
if (m_transforms[(int)from, (int)to] != null)
{
return m_transforms[(int)from, (int)to];
}
var distance = new int[5];
var prev = new CoordinateSpace ? [5];
var queue = new List<CoordinateSpace>();
foreach (var space in Enum.GetValues(typeof(CoordinateSpace)))
{
distance[(int)space] = int.MaxValue;
prev[(int)space] = null;
queue.Add((CoordinateSpace)space);
}
distance[(int)from] = 0;
List<CoordinateSpace> path = null;
while (queue.Count != 0)
{
queue.Sort((x, y) => distance[(int)x] - distance[(int)y]);
var min = queue[0];
queue.Remove(min);
if (min == to)
{
path = new List<CoordinateSpace>();
while (prev[(int)min] != null)
{
path.Add(min);
min = prev[(int)min].Value;
}
break;
}
if (distance[(int)min] == int.MaxValue)
{
break;
}
foreach (var space in Enum.GetValues(typeof(CoordinateSpace)))
{
int index = (int)space;
if (m_transforms[(int)min, index] != null)
{
var alt = distance[(int)min] + m_transforms[(int)min, index].Length;
if (alt < distance[index])
{
distance[index] = alt;
prev[index] = min;
}
}
}
}
path.Reverse();
var matrixList = new List<TransformDesc>();
foreach (var node in path)
{
matrixList.AddRange(m_transforms[(int)from, (int)node]);
from = node;
}
return matrixList.ToArray();
}
static void InitTransforms()
{
if (m_transforms == null)
{
m_transforms = new TransformDesc[5, 5][];
m_transforms[(int)CoordinateSpace.Object, (int)CoordinateSpace.Object] = new TransformDesc[] {};
m_transforms[(int)CoordinateSpace.View, (int)CoordinateSpace.View] = new TransformDesc[] {};
m_transforms[(int)CoordinateSpace.World, (int)CoordinateSpace.World] = new TransformDesc[] {};
m_transforms[(int)CoordinateSpace.Tangent, (int)CoordinateSpace.Tangent] = new TransformDesc[] {};
m_transforms[(int)CoordinateSpace.AbsoluteWorld, (int)CoordinateSpace.AbsoluteWorld] = new TransformDesc[] {};
m_transforms[(int)CoordinateSpace.Object, (int)CoordinateSpace.World]
= new TransformDesc[] {new TransformDesc(MatrixNames.Model)};
m_transforms[(int)CoordinateSpace.View, (int)CoordinateSpace.World]
= new TransformDesc[] {new TransformDesc(MatrixNames.ViewInverse) };
m_transforms[(int)CoordinateSpace.World, (int)CoordinateSpace.Object]
= new TransformDesc[] {new TransformDesc(MatrixNames.ModelInverse)};
m_transforms[(int)CoordinateSpace.World, (int)CoordinateSpace.View]
= new TransformDesc[] {new TransformDesc(MatrixNames.View)};
for (var from = CoordinateSpace.Object; from != CoordinateSpace.Tangent; from++)
{
for (var to = CoordinateSpace.Object; to != CoordinateSpace.Tangent; to++)
{
if (m_transforms[(int)from, (int)to] == null)
{
m_transforms[(int)from, (int)to] = GetTransformPath(from, to);
}
}
}
}
for (var k = CoordinateSpace.Object; k != CoordinateSpace.Tangent; k++)
{
m_transforms[(int)CoordinateSpace.Tangent, (int)k] = null;
m_transforms[(int)k, (int)CoordinateSpace.Tangent] = null;
}
}
public static string EmitTransform(TransformDesc[] matrices, TransformDesc[] invMatrices, string variable, bool isAffine, bool noMatrixCast, bool inverseTranspose)
{
// Use inverse transpose for situations where
// scale needs to be considered (normals)
if (inverseTranspose)
matrices = invMatrices;
if (isAffine)
{
variable = string.Format("float4({0},1.0)", variable);
}
foreach (var m in matrices)
{
var matrix = m.name;
if (!isAffine && !noMatrixCast)
{
matrix = "(float3x3)" + matrix;
}
// if the matrix is NOT a transpose type
// invert the order of multiplication
// it is implicit transpose.
if (m.transpose)
inverseTranspose = !inverseTranspose;
variable = inverseTranspose
? string.Format("mul({1},{0})", matrix, variable)
: string.Format("mul({0},{1})", matrix, variable);
}
return variable;
}
public static string ConvertBetweenSpace(string variable, CoordinateSpace from, CoordinateSpace to,
InputType inputType, CoordinateSpace tangentMatrixSpace = CoordinateSpace.Object)
{
if (from == to)
{
// nothing to do
return variable;
}
// Ensure that the transform graph is initialized
InitTransforms();
bool isNormal = false;
bool affine = (inputType == InputType.Position && to != CoordinateSpace.World);
bool noMatrixCast = (inputType == InputType.Position && to == CoordinateSpace.World);
if (inputType == InputType.Normal)
{
inputType = InputType.Vector;
isNormal = true;
}
m_transforms[(int)CoordinateSpace.Tangent, (int)tangentMatrixSpace] =
new[] {new TransformDesc("tangentSpaceTransform")};
m_transforms[(int)tangentMatrixSpace, (int)CoordinateSpace.Tangent] = new[]
{new TransformDesc("tangentSpaceTransform", true)};
if (from == CoordinateSpace.Tangent)
{
// if converting from tangent space, reuse the underlying space
from = tangentMatrixSpace;
variable = EmitTransform(
GetTransformPath(CoordinateSpace.Tangent, tangentMatrixSpace),
GetTransformPath(tangentMatrixSpace, CoordinateSpace.Tangent),
variable, affine, noMatrixCast, !isNormal);
if (to == tangentMatrixSpace)
{
return variable;
}
}
return EmitTransform(GetTransformPath(from, to), GetTransformPath(to, from), variable, affine, noMatrixCast, isNormal);
}
public static void GenerateSpaceTranslationSurfaceInputs(
NeededCoordinateSpace neededSpaces,
InterpolatorType interpolatorType,
ShaderStringBuilder builder,
string format = "float3 {0};")
{
if ((neededSpaces & NeededCoordinateSpace.Object) > 0)
builder.AppendLine(format, CoordinateSpace.Object.ToVariableName(interpolatorType));
if ((neededSpaces & NeededCoordinateSpace.World) > 0)
builder.AppendLine(format, CoordinateSpace.World.ToVariableName(interpolatorType));
if ((neededSpaces & NeededCoordinateSpace.View) > 0)
builder.AppendLine(format, CoordinateSpace.View.ToVariableName(interpolatorType));
if ((neededSpaces & NeededCoordinateSpace.Tangent) > 0)
builder.AppendLine(format, CoordinateSpace.Tangent.ToVariableName(interpolatorType));
if ((neededSpaces & NeededCoordinateSpace.AbsoluteWorld) > 0)
builder.AppendLine(format, CoordinateSpace.AbsoluteWorld.ToVariableName(interpolatorType));
}
public static void GenerateStandardTransforms(
int interpolatorStartIndex,
int maxInterpolators,
ShaderStringBuilder vertexOutputStruct,
ShaderStringBuilder vertexShader,
ShaderStringBuilder vertexShaderDescriptionInputs,
ShaderStringBuilder vertexShaderOutputs,
ShaderStringBuilder pixelShader,
ShaderStringBuilder pixelShaderSurfaceInputs,
ShaderGraphRequirements pixelRequirements,
ShaderGraphRequirements surfaceRequirements,
ShaderGraphRequirements modelRequiements,
ShaderGraphRequirements vertexRequirements,
CoordinateSpace preferredCoordinateSpace)
{
// ----------------------------------------------------- //
// SETUP //
// ----------------------------------------------------- //
// -------------------------------------
// Tangent space requires going via World space
if (preferredCoordinateSpace == CoordinateSpace.Tangent)
preferredCoordinateSpace = CoordinateSpace.World;
// -------------------------------------
// Generate combined graph requirements
var graphModelRequirements = pixelRequirements.Union(modelRequiements);
var combinedRequirements = vertexRequirements.Union(graphModelRequirements);
int interpolatorIndex = interpolatorStartIndex;
// ----------------------------------------------------- //
// GENERATE VERTEX > PIXEL PIPELINE //
// ----------------------------------------------------- //
// -------------------------------------
// If any stage requires component write into vertex stage
// If model or pixel requires component write into interpolators and pixel stage
// -------------------------------------
// Position
if (combinedRequirements.requiresPosition > 0)
{
var name = preferredCoordinateSpace.ToVariableName(InterpolatorType.Position);
var preferredSpacePosition = ConvertBetweenSpace("v.vertex", CoordinateSpace.Object, preferredCoordinateSpace, InputType.Position);
vertexShader.AppendLine("float3 {0} = {1}.xyz;", name, preferredSpacePosition);
if (graphModelRequirements.requiresPosition > 0)
{
vertexOutputStruct.AppendLine("float3 {0} : TEXCOORD{1};", name, interpolatorIndex);
vertexShaderOutputs.AppendLine("o.{0} = {0};", name);
pixelShader.AppendLine("float3 {0} = IN.{0};", name);
interpolatorIndex++;
}
}
// -------------------------------------
// Normal
// Bitangent needs Normal for x product
if (combinedRequirements.requiresNormal > 0 || combinedRequirements.requiresBitangent > 0)
{
var name = preferredCoordinateSpace.ToVariableName(InterpolatorType.Normal);
vertexShader.AppendLine("float3 {0} = normalize({1});", name, ConvertBetweenSpace("v.normal", CoordinateSpace.Object, preferredCoordinateSpace, InputType.Normal));
if (graphModelRequirements.requiresNormal > 0 || graphModelRequirements.requiresBitangent > 0)
{
vertexOutputStruct.AppendLine("float3 {0} : TEXCOORD{1};", name, interpolatorIndex);
vertexShaderOutputs.AppendLine("o.{0} = {0};", name);
pixelShader.AppendLine("float3 {0} = IN.{0};", name);
interpolatorIndex++;
}
}
// -------------------------------------
// Tangent
if (combinedRequirements.requiresTangent > 0 || combinedRequirements.requiresBitangent > 0)
{
var name = preferredCoordinateSpace.ToVariableName(InterpolatorType.Tangent);
vertexShader.AppendLine("float3 {0} = normalize({1});", name, ConvertBetweenSpace("v.tangent.xyz", CoordinateSpace.Object, preferredCoordinateSpace, InputType.Vector));
if (graphModelRequirements.requiresTangent > 0 || graphModelRequirements.requiresBitangent > 0)
{
vertexOutputStruct.AppendLine("float3 {0} : TEXCOORD{1};", name, interpolatorIndex);
vertexShaderOutputs.AppendLine("o.{0} = {0};", name);
pixelShader.AppendLine("float3 {0} = IN.{0};", name);
interpolatorIndex++;
}
}
// -------------------------------------
// Bitangent
if (combinedRequirements.requiresBitangent > 0)
{
var name = preferredCoordinateSpace.ToVariableName(InterpolatorType.BiTangent);
vertexShader.AppendLine("float3 {0} = cross({1}, {2}.xyz) * {3};",
name,
preferredCoordinateSpace.ToVariableName(InterpolatorType.Normal),
preferredCoordinateSpace.ToVariableName(InterpolatorType.Tangent),
"v.tangent.w");
if (graphModelRequirements.requiresBitangent > 0)
{
vertexOutputStruct.AppendLine("float3 {0} : TEXCOORD{1};", name, interpolatorIndex);
vertexShaderOutputs.AppendLine("o.{0} = {0};", name);
pixelShader.AppendLine("float3 {0} = IN.{0};", name);
interpolatorIndex++;
}
}
// -------------------------------------
// View Direction
if (combinedRequirements.requiresViewDir > 0)
{
var name = preferredCoordinateSpace.ToVariableName(InterpolatorType.ViewDirection);
const string worldSpaceViewDir = "_WorldSpaceCameraPos.xyz - mul(GetObjectToWorldMatrix(), float4(v.vertex.xyz, 1.0)).xyz";
var preferredSpaceViewDir = ConvertBetweenSpace(worldSpaceViewDir, CoordinateSpace.World, preferredCoordinateSpace, InputType.Vector);
vertexShader.AppendLine("float3 {0} = {1};", name, preferredSpaceViewDir);
if (graphModelRequirements.requiresViewDir > 0)
{
vertexOutputStruct.AppendLine("float3 {0} : TEXCOORD{1};", name, interpolatorIndex);
vertexShaderOutputs.AppendLine("o.{0} = {0};", name);
pixelShader.AppendLine("float3 {0} = IN.{0};", name);
interpolatorIndex++;
}
}
// -------------------------------------
// Tangent space transform matrix
if (combinedRequirements.NeedsTangentSpace())
{
var tangent = preferredCoordinateSpace.ToVariableName(InterpolatorType.Tangent);
var bitangent = preferredCoordinateSpace.ToVariableName(InterpolatorType.BiTangent);
var normal = preferredCoordinateSpace.ToVariableName(InterpolatorType.Normal);
if (vertexRequirements.NeedsTangentSpace())
vertexShader.AppendLine("float3x3 tangentSpaceTransform = float3x3({0},{1},{2});",
tangent, bitangent, normal);
if (graphModelRequirements.NeedsTangentSpace())
pixelShader.AppendLine("float3x3 tangentSpaceTransform = float3x3({0},{1},{2});",
tangent, bitangent, normal);
}
// -------------------------------------
// Vertex Color
if (combinedRequirements.requiresVertexColor)
{
var vertexColor = "v.color";
vertexShader.AppendLine("float4 {0} = {1};", ShaderGeneratorNames.VertexColor, vertexColor);
if (graphModelRequirements.requiresVertexColor)
{
vertexOutputStruct.AppendLine("float4 {0} : COLOR;", ShaderGeneratorNames.VertexColor);
vertexShaderOutputs.AppendLine("o.{0} = {0};", ShaderGeneratorNames.VertexColor);
pixelShader.AppendLine("float4 {0} = IN.{0};", ShaderGeneratorNames.VertexColor);
}
}
// -------------------------------------
// Screen Position
if (combinedRequirements.requiresScreenPosition)
{
var screenPosition = "ComputeScreenPos(mul(GetWorldToHClipMatrix(), mul(GetObjectToWorldMatrix(), v.vertex)), _ProjectionParams.x)";
vertexShader.AppendLine("float4 {0} = {1};", ShaderGeneratorNames.ScreenPosition, screenPosition);
if (graphModelRequirements.requiresScreenPosition)
{
vertexOutputStruct.AppendLine("float4 {0} : TEXCOORD{1};", ShaderGeneratorNames.ScreenPosition, interpolatorIndex);
vertexShaderOutputs.AppendLine("o.{0} = {0};", ShaderGeneratorNames.ScreenPosition);
pixelShader.AppendLine("float4 {0} = IN.{0};", ShaderGeneratorNames.ScreenPosition);
interpolatorIndex++;
}
}
// -------------------------------------
// UV
foreach (var channel in combinedRequirements.requiresMeshUVs.Distinct())
vertexShader.AppendLine("float4 {0} = v.texcoord{1};", channel.GetUVName(), (int)channel);
foreach (var channel in graphModelRequirements.requiresMeshUVs.Distinct())
{
vertexOutputStruct.AppendLine("half4 {0} : TEXCOORD{1};", channel.GetUVName(), interpolatorIndex == 0 ? "" : interpolatorIndex.ToString());
vertexShaderOutputs.AppendLine("o.{0} = {0};", channel.GetUVName());
pixelShader.AppendLine("float4 {0} = IN.{0};", channel.GetUVName());
interpolatorIndex++;
}
// ----------------------------------------------------- //
// TRANSLATE NEEDED SPACES //
// ----------------------------------------------------- //
// -------------------------------------
// Generate the space translations needed by the vertex description
GenerateSpaceTranslations(vertexRequirements.requiresNormal, InterpolatorType.Normal, preferredCoordinateSpace,
InputType.Normal, vertexShader, Dimension.Three);
GenerateSpaceTranslations(vertexRequirements.requiresTangent, InterpolatorType.Tangent, preferredCoordinateSpace,
InputType.Vector, vertexShader, Dimension.Three);
GenerateSpaceTranslations(vertexRequirements.requiresBitangent, InterpolatorType.BiTangent, preferredCoordinateSpace,
InputType.Vector, vertexShader, Dimension.Three);
GenerateSpaceTranslations(vertexRequirements.requiresViewDir, InterpolatorType.ViewDirection, preferredCoordinateSpace,
InputType.Vector, vertexShader, Dimension.Three);
GenerateSpaceTranslations(vertexRequirements.requiresPosition, InterpolatorType.Position, preferredCoordinateSpace,
InputType.Position, vertexShader, Dimension.Three);
// -------------------------------------
// Generate the space translations needed by the surface description and model
GenerateSpaceTranslations(graphModelRequirements.requiresNormal, InterpolatorType.Normal, preferredCoordinateSpace,
InputType.Normal, pixelShader, Dimension.Three);
GenerateSpaceTranslations(graphModelRequirements.requiresTangent, InterpolatorType.Tangent, preferredCoordinateSpace,
InputType.Vector, pixelShader, Dimension.Three);
GenerateSpaceTranslations(graphModelRequirements.requiresBitangent, InterpolatorType.BiTangent, preferredCoordinateSpace,
InputType.Vector, pixelShader, Dimension.Three);
GenerateSpaceTranslations(graphModelRequirements.requiresViewDir, InterpolatorType.ViewDirection, preferredCoordinateSpace,
InputType.Vector, pixelShader, Dimension.Three);
GenerateSpaceTranslations(graphModelRequirements.requiresPosition, InterpolatorType.Position, preferredCoordinateSpace,
InputType.Position, pixelShader, Dimension.Three);
// ----------------------------------------------------- //
// START SURFACE DESCRIPTION //
// ----------------------------------------------------- //
// -------------------------------------
// Copy the locally defined values into the surface description
// structure using the requirements for ONLY the shader graph pixel stage
// additional requirements have come from the lighting model
// and are not needed in the shader graph
{
var replaceString = "surfaceInput.{0} = {0};";
GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresNormal, InterpolatorType.Normal, pixelShaderSurfaceInputs, replaceString);
GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresTangent, InterpolatorType.Tangent, pixelShaderSurfaceInputs, replaceString);
GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresBitangent, InterpolatorType.BiTangent, pixelShaderSurfaceInputs, replaceString);
GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresViewDir, InterpolatorType.ViewDirection, pixelShaderSurfaceInputs, replaceString);
GenerateSpaceTranslationSurfaceInputs(surfaceRequirements.requiresPosition, InterpolatorType.Position, pixelShaderSurfaceInputs, replaceString);
}
if (pixelRequirements.requiresVertexColor)
pixelShaderSurfaceInputs.AppendLine("surfaceInput.{0} = {0};", ShaderGeneratorNames.VertexColor);
if (pixelRequirements.requiresScreenPosition)
pixelShaderSurfaceInputs.AppendLine("surfaceInput.{0} = {0};", ShaderGeneratorNames.ScreenPosition);
if (pixelRequirements.requiresFaceSign)
pixelShaderSurfaceInputs.AppendLine("surfaceInput.{0} = {0};", ShaderGeneratorNames.FaceSign);
foreach (var channel in pixelRequirements.requiresMeshUVs.Distinct())
pixelShaderSurfaceInputs.AppendLine("surfaceInput.{0} = {0};", ShaderGeneratorNames.GetUVName(channel));
if (pixelRequirements.requiresTime)
{
pixelShaderSurfaceInputs.AppendLine("surfaceInput.{0} = _TimeParameters.xyz;", ShaderGeneratorNames.TimeParameters);
}
// ----------------------------------------------------- //
// START VERTEX DESCRIPTION //
// ----------------------------------------------------- //
// -------------------------------------
// Copy the locally defined values into the vertex description
// structure using the requirements for ONLY the shader graph vertex stage
// additional requirements have come from the lighting model
// and are not needed in the shader graph
{
var replaceString = "vdi.{0} = {0};";
GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresNormal, InterpolatorType.Normal, vertexShaderDescriptionInputs, replaceString);
GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresTangent, InterpolatorType.Tangent, vertexShaderDescriptionInputs, replaceString);
GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresBitangent, InterpolatorType.BiTangent, vertexShaderDescriptionInputs, replaceString);
GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresViewDir, InterpolatorType.ViewDirection, vertexShaderDescriptionInputs, replaceString);
GenerateSpaceTranslationSurfaceInputs(vertexRequirements.requiresPosition, InterpolatorType.Position, vertexShaderDescriptionInputs, replaceString);
}
if (vertexRequirements.requiresVertexColor)
vertexShaderDescriptionInputs.AppendLine("vdi.{0} = {0};", ShaderGeneratorNames.VertexColor);
if (vertexRequirements.requiresScreenPosition)
vertexShaderDescriptionInputs.AppendLine("vdi.{0} = {0};", ShaderGeneratorNames.ScreenPosition);
foreach (var channel in vertexRequirements.requiresMeshUVs.Distinct())
vertexShaderDescriptionInputs.AppendLine("vdi.{0} = {0};", channel.GetUVName(), (int)channel);
if (vertexRequirements.requiresTime)
{
vertexShaderDescriptionInputs.AppendLine("vdi.{0} = _TimeParameters.xyz;", ShaderGeneratorNames.TimeParameters);
}
}
public enum Dimension
{
One,
Two,
Three,
Four
}
private static string DimensionToString(Dimension d)
{
switch (d)
{
case Dimension.One:
return string.Empty;
case Dimension.Two:
return "2";
case Dimension.Three:
return "3";
case Dimension.Four:
return "4";
}
return "error";
}
private static string DimensionToSwizzle(Dimension d)
{
switch (d)
{
case Dimension.One:
return "x";
case Dimension.Two:
return "xy";
case Dimension.Three:
return "xyz";
case Dimension.Four:
return "xyzw";
}
return "error";
}
public static void GenerateSpaceTranslations(
NeededCoordinateSpace neededSpaces,
InterpolatorType type,
CoordinateSpace from,
InputType inputType,
ShaderStringBuilder pixelShader,
Dimension dimension)
{
if ((neededSpaces & NeededCoordinateSpace.Object) > 0 && from != CoordinateSpace.Object)
pixelShader.AppendLine("float{0} {1} = {2}.{3};", DimensionToString(dimension),
CoordinateSpace.Object.ToVariableName(type), ConvertBetweenSpace(from.ToVariableName(type), from, CoordinateSpace.Object, inputType, from),
DimensionToSwizzle(dimension));
if ((neededSpaces & NeededCoordinateSpace.World) > 0 && from != CoordinateSpace.World)
pixelShader.AppendLine("float{0} {1} = {2}.{3};", DimensionToString(dimension),
CoordinateSpace.World.ToVariableName(type), ConvertBetweenSpace(from.ToVariableName(type), from, CoordinateSpace.World, inputType, from),
DimensionToSwizzle(dimension));
if ((neededSpaces & NeededCoordinateSpace.View) > 0 && from != CoordinateSpace.View)
pixelShader.AppendLine("float{0} {1} = {2}.{3};", DimensionToString(dimension),
CoordinateSpace.View.ToVariableName(type),
ConvertBetweenSpace(from.ToVariableName(type), from, CoordinateSpace.View, inputType, from),
DimensionToSwizzle(dimension));
if ((neededSpaces & NeededCoordinateSpace.Tangent) > 0 && from != CoordinateSpace.Tangent)
pixelShader.AppendLine("float{0} {1} = {2}.{3};", DimensionToString(dimension),
CoordinateSpace.Tangent.ToVariableName(type),
ConvertBetweenSpace(from.ToVariableName(type), from, CoordinateSpace.Tangent, inputType, from),
DimensionToSwizzle(dimension));
if ((neededSpaces & NeededCoordinateSpace.AbsoluteWorld) > 0 && from != CoordinateSpace.AbsoluteWorld)
pixelShader.AppendLine("float{0} {1} = GetAbsolutePositionWS({2}).{3};", DimensionToString(dimension),
CoordinateSpace.AbsoluteWorld.ToVariableName(type),
CoordinateSpace.World.ToVariableName(type),
DimensionToSwizzle(dimension));
}
public static string GetPreviewSubShader(AbstractMaterialNode node, ShaderGraphRequirements shaderGraphRequirements)
{
// Should never be called without a node
Debug.Assert(node != null);
var vertexOutputStruct = new ShaderStringBuilder(2);
var vertexShader = new ShaderStringBuilder(2);
var vertexShaderDescriptionInputs = new ShaderStringBuilder(2);
var vertexShaderOutputs = new ShaderStringBuilder(1);
var pixelShader = new ShaderStringBuilder(2);
var pixelShaderSurfaceInputs = new ShaderStringBuilder(2);
var pixelShaderSurfaceRemap = new ShaderStringBuilder(2);
ShaderGenerator.GenerateStandardTransforms(
0,
16,
vertexOutputStruct,
vertexShader,
vertexShaderDescriptionInputs,
vertexShaderOutputs,
pixelShader,
pixelShaderSurfaceInputs,
shaderGraphRequirements,
shaderGraphRequirements,
ShaderGraphRequirements.none,
ShaderGraphRequirements.none,
CoordinateSpace.World);
vertexShader.AppendLines(vertexShaderDescriptionInputs.ToString());
vertexShader.AppendLines(vertexShaderOutputs.ToString());
var outputSlot = node.GetOutputSlots<MaterialSlot>().FirstOrDefault();
// Sub Graph Output uses first input slot
if (node is SubGraphOutputNode)
{
outputSlot = node.GetInputSlots<MaterialSlot>().FirstOrDefault();
}
if (outputSlot != null)
{
var result = $"surf.{NodeUtils.GetHLSLSafeName(outputSlot.shaderOutputName)}_{outputSlot.id}";
pixelShaderSurfaceRemap.AppendLine("return all(isfinite({0})) ? {1} : {2};",
result, AdaptNodeOutputForPreview(node, outputSlot.id, result), nanOutput);
}
else
{
// No valid slots to display, so just show black.
// It's up to each node to error or warn as appropriate.
pixelShaderSurfaceRemap.AppendLine("return 0;");
}
// -------------------------------------
// Extra pixel shader work
var faceSign = new ShaderStringBuilder();
if (shaderGraphRequirements.requiresFaceSign)
faceSign.AppendLine(", half FaceSign : VFACE");
var res = subShaderTemplate.Replace("${Interpolators}", vertexOutputStruct.ToString());
res = res.Replace("${VertexShader}", vertexShader.ToString());
res = res.Replace("${FaceSign}", faceSign.ToString());
res = res.Replace("${LocalPixelShader}", pixelShader.ToString());
res = res.Replace("${SurfaceInputs}", pixelShaderSurfaceInputs.ToString());
res = res.Replace("${SurfaceOutputRemap}", pixelShaderSurfaceRemap.ToString());
return res;
}
public static SurfaceMaterialTags BuildMaterialTags(SurfaceType surfaceType)
{
SurfaceMaterialTags materialTags = new SurfaceMaterialTags();
if (surfaceType == SurfaceType.Opaque)
{
materialTags.renderQueue = SurfaceMaterialTags.RenderQueue.Geometry;
materialTags.renderType = SurfaceMaterialTags.RenderType.Opaque;
}
else
{
materialTags.renderQueue = SurfaceMaterialTags.RenderQueue.Transparent;
materialTags.renderType = SurfaceMaterialTags.RenderType.Transparent;
}
return materialTags;
}
public static SurfaceMaterialOptions GetMaterialOptions(SurfaceType surfaceType, AlphaMode alphaMode, bool twoSided, bool offscreenTransparent = false)
{
var materialOptions = new SurfaceMaterialOptions();
switch (surfaceType)
{
case SurfaceType.Opaque:
materialOptions.srcBlend = SurfaceMaterialOptions.BlendMode.One;
materialOptions.dstBlend = SurfaceMaterialOptions.BlendMode.Zero;
materialOptions.cullMode = twoSided ? SurfaceMaterialOptions.CullMode.Off : SurfaceMaterialOptions.CullMode.Back;
materialOptions.zTest = SurfaceMaterialOptions.ZTest.LEqual;
materialOptions.zWrite = SurfaceMaterialOptions.ZWrite.On;
break;
case SurfaceType.Transparent:
switch (alphaMode)
{
case AlphaMode.Alpha:
materialOptions.srcBlend = SurfaceMaterialOptions.BlendMode.SrcAlpha;
materialOptions.dstBlend = SurfaceMaterialOptions.BlendMode.OneMinusSrcAlpha;
if(offscreenTransparent)
{
materialOptions.alphaSrcBlend = SurfaceMaterialOptions.BlendMode.Zero;
}
else
{
materialOptions.alphaSrcBlend = SurfaceMaterialOptions.BlendMode.One;
}
materialOptions.alphaDstBlend = SurfaceMaterialOptions.BlendMode.OneMinusSrcAlpha;
materialOptions.cullMode = twoSided ? SurfaceMaterialOptions.CullMode.Off : SurfaceMaterialOptions.CullMode.Back;
materialOptions.zTest = SurfaceMaterialOptions.ZTest.LEqual;
materialOptions.zWrite = SurfaceMaterialOptions.ZWrite.Off;
break;
case AlphaMode.Premultiply:
materialOptions.srcBlend = SurfaceMaterialOptions.BlendMode.One;
materialOptions.dstBlend = SurfaceMaterialOptions.BlendMode.OneMinusSrcAlpha;
if (offscreenTransparent)
{
materialOptions.alphaSrcBlend = SurfaceMaterialOptions.BlendMode.Zero;
}
else
{
materialOptions.alphaSrcBlend = SurfaceMaterialOptions.BlendMode.One;
}
materialOptions.alphaDstBlend = SurfaceMaterialOptions.BlendMode.OneMinusSrcAlpha;
materialOptions.cullMode = twoSided ? SurfaceMaterialOptions.CullMode.Off : SurfaceMaterialOptions.CullMode.Back;
materialOptions.zTest = SurfaceMaterialOptions.ZTest.LEqual;
materialOptions.zWrite = SurfaceMaterialOptions.ZWrite.Off;
break;
case AlphaMode.Additive:
materialOptions.srcBlend = SurfaceMaterialOptions.BlendMode.One;
materialOptions.dstBlend = SurfaceMaterialOptions.BlendMode.One;
if (offscreenTransparent)
{
materialOptions.alphaSrcBlend = SurfaceMaterialOptions.BlendMode.Zero;
}
else
{
materialOptions.alphaSrcBlend = SurfaceMaterialOptions.BlendMode.One;
}
materialOptions.alphaDstBlend = SurfaceMaterialOptions.BlendMode.One;
materialOptions.cullMode = twoSided ? SurfaceMaterialOptions.CullMode.Off : SurfaceMaterialOptions.CullMode.Back;
materialOptions.zTest = SurfaceMaterialOptions.ZTest.LEqual;
materialOptions.zWrite = SurfaceMaterialOptions.ZWrite.Off;
break;
case AlphaMode.Multiply:
materialOptions.srcBlend = SurfaceMaterialOptions.BlendMode.DstColor;
materialOptions.dstBlend = SurfaceMaterialOptions.BlendMode.Zero;
materialOptions.cullMode = twoSided ? SurfaceMaterialOptions.CullMode.Off : SurfaceMaterialOptions.CullMode.Back;
materialOptions.zTest = SurfaceMaterialOptions.ZTest.LEqual;
materialOptions.zWrite = SurfaceMaterialOptions.ZWrite.Off;
break;
}
break;
}
return materialOptions;
}
const string nanOutput = "float4(1.0f, 0.0f, 1.0f, 1.0f)";
const string subShaderTemplate = @"
SubShader
{
Tags { ""RenderType""=""Opaque"" }
LOD 100
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
struct GraphVertexOutput
{
float4 position : POSITION;
${Interpolators}
};
GraphVertexOutput vert (GraphVertexInput v)
{
v = PopulateVertexData(v);
GraphVertexOutput o;
float3 positionWS = TransformObjectToWorld(v.vertex);
o.position = TransformWorldToHClip(positionWS);
${VertexShader}
return o;
}
float4 frag (GraphVertexOutput IN ${FaceSign}) : SV_Target
{
${LocalPixelShader}
SurfaceDescriptionInputs surfaceInput = (SurfaceDescriptionInputs)0;
${SurfaceInputs}
SurfaceDescription surf = PopulateSurfaceData(surfaceInput);
${SurfaceOutputRemap}
}
ENDHLSL
}
}";
}
}