using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor.Experimental.Rendering.LightweightPipeline; using UnityEditor.Graphing; using UnityEditor.ShaderGraph; namespace UnityEngine.Experimental.Rendering.LightweightPipeline { [Serializable] [FormerName("UnityEditor.ShaderGraph.LightWeightUnlitSubShader")] public class LightWeightUnlitSubShader : IUnlitSubShader { Pass m_UnlitPass = new Pass { Name = "Unlit", PixelShaderSlots = new List { UnlitMasterNode.ColorSlotId, UnlitMasterNode.AlphaSlotId, UnlitMasterNode.AlphaThresholdSlotId } }; struct Pass { public string Name; public List VertexShaderSlots; public List PixelShaderSlots; } private static string GetShaderPassFromTemplate( string template, UnlitMasterNode masterNode, Pass pass, GenerationMode mode, SurfaceMaterialOptions materialOptions) { var builder = new ShaderStringBuilder(); builder.IncreaseIndent(); builder.IncreaseIndent(); var vertexInputs = new ShaderGenerator(); var surfaceVertexShader = new ShaderGenerator(); var surfaceDescriptionFunction = new ShaderGenerator(); var surfaceDescriptionStruct = new ShaderGenerator(); var functionRegistry = new FunctionRegistry(builder); var surfaceInputs = new ShaderGenerator(); var shaderProperties = new PropertyCollector(); surfaceInputs.AddShaderChunk("struct SurfaceInputs{", false); surfaceInputs.Indent(); var activeNodeList = ListPool.Get(); NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, masterNode, NodeUtils.IncludeSelf.Include, pass.PixelShaderSlots); var requirements = ShaderGraphRequirements.FromNodes(activeNodeList); GraphUtil.GenerateApplicationVertexInputs(requirements, vertexInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresNormal, InterpolatorType.Normal, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresTangent, InterpolatorType.Tangent, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresBitangent, InterpolatorType.BiTangent, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresViewDir, InterpolatorType.ViewDirection, surfaceInputs); ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresPosition, InterpolatorType.Position, surfaceInputs); if (requirements.requiresVertexColor) surfaceInputs.AddShaderChunk(string.Format("float4 {0};", ShaderGeneratorNames.VertexColor), false); if (requirements.requiresScreenPosition) surfaceInputs.AddShaderChunk(string.Format("float4 {0};", ShaderGeneratorNames.ScreenPosition), false); foreach (var channel in requirements.requiresMeshUVs.Distinct()) surfaceInputs.AddShaderChunk(string.Format("half4 {0};", channel.GetUVName()), false); surfaceInputs.Deindent(); surfaceInputs.AddShaderChunk("};", false); surfaceVertexShader.AddShaderChunk("GraphVertexInput PopulateVertexData(GraphVertexInput v){", false); surfaceVertexShader.Indent(); surfaceVertexShader.AddShaderChunk("return v;", false); surfaceVertexShader.Deindent(); surfaceVertexShader.AddShaderChunk("}", false); var slots = new List(); foreach (var id in pass.PixelShaderSlots) { var slot = masterNode.FindSlot(id); if (slot != null) slots.Add(slot); } GraphUtil.GenerateSurfaceDescriptionStruct(surfaceDescriptionStruct, slots, true); var usedSlots = new List(); foreach (var id in pass.PixelShaderSlots) usedSlots.Add(masterNode.FindSlot(id)); GraphUtil.GenerateSurfaceDescription( activeNodeList, masterNode, masterNode.owner as AbstractMaterialGraph, surfaceDescriptionFunction, functionRegistry, shaderProperties, requirements, mode, "PopulateSurfaceData", "SurfaceDescription", null, usedSlots); var graph = new ShaderGenerator(); graph.AddShaderChunk(shaderProperties.GetPropertiesDeclaration(2), false); graph.AddShaderChunk(surfaceInputs.GetShaderString(2), false); graph.AddShaderChunk(builder.ToString(), false); graph.AddShaderChunk(vertexInputs.GetShaderString(2), false); graph.AddShaderChunk(surfaceDescriptionStruct.GetShaderString(2), false); graph.AddShaderChunk(surfaceVertexShader.GetShaderString(2), false); graph.AddShaderChunk(surfaceDescriptionFunction.GetShaderString(2), false); var blendingVisitor = new ShaderGenerator(); var cullingVisitor = new ShaderGenerator(); var zTestVisitor = new ShaderGenerator(); var zWriteVisitor = new ShaderGenerator(); materialOptions.GetBlend(blendingVisitor); materialOptions.GetCull(cullingVisitor); materialOptions.GetDepthTest(zTestVisitor); materialOptions.GetDepthWrite(zWriteVisitor); var interpolators = new ShaderGenerator(); var localVertexShader = new ShaderGenerator(); var localPixelShader = new ShaderGenerator(); var localSurfaceInputs = new ShaderGenerator(); var surfaceOutputRemap = new ShaderGenerator(); var reqs = ShaderGraphRequirements.none; ShaderGenerator.GenerateStandardTransforms( 3, 10, interpolators, localVertexShader, localPixelShader, localSurfaceInputs, requirements, reqs, CoordinateSpace.World); ShaderGenerator defines = new ShaderGenerator(); if (masterNode.IsSlotConnected(UnlitMasterNode.AlphaThresholdSlotId)) defines.AddShaderChunk("#define _AlphaClip 1", true); if (masterNode.surfaceType == SurfaceType.Transparent && masterNode.alphaMode == AlphaMode.Premultiply) defines.AddShaderChunk("#define _ALPHAPREMULTIPLY_ON 1", true); var templateLocation = GetTemplatePath(template); foreach (var slot in usedSlots) { surfaceOutputRemap.AddShaderChunk(slot.shaderOutputName + " = surf." + slot.shaderOutputName + ";", true); } if (!File.Exists(templateLocation)) return string.Empty; var subShaderTemplate = File.ReadAllText(templateLocation); var resultPass = subShaderTemplate.Replace("${Defines}", defines.GetShaderString(3)); resultPass = resultPass.Replace("${Graph}", graph.GetShaderString(3)); resultPass = resultPass.Replace("${Interpolators}", interpolators.GetShaderString(3)); resultPass = resultPass.Replace("${VertexShader}", localVertexShader.GetShaderString(3)); resultPass = resultPass.Replace("${LocalPixelShader}", localPixelShader.GetShaderString(3)); resultPass = resultPass.Replace("${SurfaceInputs}", localSurfaceInputs.GetShaderString(3)); resultPass = resultPass.Replace("${SurfaceOutputRemap}", surfaceOutputRemap.GetShaderString(3)); resultPass = resultPass.Replace("${Tags}", string.Empty); resultPass = resultPass.Replace("${Blending}", blendingVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${Culling}", cullingVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${ZTest}", zTestVisitor.GetShaderString(2)); resultPass = resultPass.Replace("${ZWrite}", zWriteVisitor.GetShaderString(2)); return resultPass; } static string GetTemplatePath(string templateName) { string relativeTemplatePath = Path.Combine("LWRP", Path.Combine("Editor", Path.Combine("ShaderGraph", templateName))); foreach (var path in LightweightIncludePaths.GetPaths()) { var templatePath = Path.Combine(path, relativeTemplatePath); if (File.Exists(templatePath)) return templatePath; } throw new FileNotFoundException(string.Format(@"Cannot find a template with name ""{0}"".", templateName)); } public string GetSubshader(IMasterNode inMasterNode, GenerationMode mode) { var masterNode = inMasterNode as UnlitMasterNode; var subShader = new ShaderGenerator(); subShader.AddShaderChunk("SubShader", true); subShader.AddShaderChunk("{", true); subShader.Indent(); subShader.AddShaderChunk("Tags{ \"RenderType\" = \"Opaque\" \"RenderPipeline\" = \"LightweightPipeline\"}", true); var materialOptions = ShaderGenerator.GetMaterialOptions(masterNode.surfaceType, masterNode.alphaMode, masterNode.twoSided.isOn); var tagsVisitor = new ShaderGenerator(); materialOptions.GetTags(tagsVisitor); subShader.AddShaderChunk(tagsVisitor.GetShaderString(0), true); subShader.AddShaderChunk( GetShaderPassFromTemplate( "lightweightUnlitPass.template", masterNode, m_UnlitPass, mode, materialOptions), true); var extraPassesTemplateLocation = GetTemplatePath("lightweightUnlitExtraPasses.template"); if (File.Exists(extraPassesTemplateLocation)) { var extraPassesTemplate = File.ReadAllText(extraPassesTemplateLocation); extraPassesTemplate = extraPassesTemplate.Replace("${Culling}", materialOptions.cullMode.ToString()); subShader.AddShaderChunk(extraPassesTemplate, true); } subShader.Deindent(); subShader.AddShaderChunk("}", true); return subShader.GetShaderString(0); } } }