您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

1203 行
49 KiB

using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine.MaterialGraph;
using UnityEngine.Graphing;
namespace UnityEngine.Experimental.ScriptableRenderLoop
{
[Serializable]
public abstract class AbstractHDRenderLoopMasterNode : AbstractMasterNode
{
public AbstractHDRenderLoopMasterNode()
{
name = GetName();
UpdateNodeAfterDeserialization();
}
protected abstract Type GetSurfaceType();
protected abstract string GetName();
protected abstract int GetMatchingMaterialID();
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,
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.displayName == "Normal") //WIP : should be a setting in attribute
{
AddSlot(new MaterialSlotDefaultInput(slot.index, slot.displayName, slot.shaderOutputName, Graphing.SlotType.Input, slot.valueType, new WorldSpaceNormalNode(), 0));
}
else
{
AddSlot(new MaterialSlot(slot.index, slot.displayName, slot.shaderOutputName, Graphing.SlotType.Input, slot.valueType, Vector4.zero));
}
}
}
}
private static void CollectFromNodesFromNodes(List<INode> nodeList, INode node, List<int> slotId)
{
// no where to start
if (node == null)
return;
// allready added this node
if (nodeList.Contains(node))
return;
// if we have a slot passed in but can not find it on the node abort
if (slotId != null && node.GetInputSlots<ISlot>().All(x => !slotId.Contains(x.id)))
return;
var validSlots = ListPool<int>.Get();
if (slotId != null)
slotId.ForEach(x => validSlots.Add(x));
else
validSlots.AddRange(node.GetInputSlots<ISlot>().Select(x => x.id));
foreach (var slot in validSlots)
{
foreach (var edge in node.owner.GetEdges(node.GetSlotReference(slot)))
{
var outputNode = node.owner.GetNodeFromGuid(edge.outputSlot.nodeGuid);
CollectFromNodesFromNodes(nodeList, outputNode, null);
}
}
nodeList.Add(node);
ListPool<int>.Release(validSlots);
}
private struct Vayring
{
public string attributeName;
public string semantic;
public string vayringName;
public SlotValueType type;
public string vertexCode;
public string pixelCode;
};
private string GenerateLitDataTemplate(GenerationMode mode, string useDataInput, string needFragInput, PropertyGenerator propertyGenerator, ShaderGenerator propertyUsagesVisitor, ShaderGenerator shaderFunctionVisitor)
{
var activeNodeList = new List<INode>();
var useDataInputRegex = new Regex(useDataInput);
var needFragInputRegex = new Regex(needFragInput);
var slotIDList = GetInputSlots<MaterialSlot>().Where(s => useDataInputRegex.IsMatch(s.shaderOutputName)).Select(s => s.id).ToList();
CollectFromNodesFromNodes(activeNodeList, this, slotIDList);
var vayrings = new List<Vayring>();
if (needFragInputRegex.IsMatch("meshUV0") || activeNodeList.OfType<IMayRequireMeshUV>().Any(x => x.RequiresMeshUV()))
{
vayrings.Add(new Vayring()
{
attributeName = "meshUV0",
semantic = "TEXCOORD0",
vayringName = "meshUV0",
type = SlotValueType.Vector2,
vertexCode = "output.meshUV0 = input.meshUV0;",
pixelCode = string.Format("float4 {0} = float4(fragInput.meshUV0, 0, 0);", ShaderGeneratorNames.UV0)
});
}
if (needFragInputRegex.IsMatch("normalWS") || 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);",
pixelCode = string.Format("float3 {0} = normalize(fragInput.normalWS);", ShaderGeneratorNames.WorldSpaceNormal)
});
}
if (needFragInputRegex.IsMatch("positionWS") || 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 (needFragInputRegex.IsMatch("viewDirectionWS") || activeNodeList.OfType<IMayRequireViewDirection>().Any(x => x.RequiresViewDirection()))
{
vayrings.Add(new Vayring()
{
vayringName = "viewDirectionWS",
type = SlotValueType.Vector3,
vertexCode = "output.viewDirectionWS = GetWorldSpaceNormalizeViewDir(TransformObjectToWorld(input.positionOS));",
pixelCode = string.Format("float3 {0} = normalize(fragInput.viewDirectionWS);", ShaderGeneratorNames.WorldSpaceViewDirection)
});
}
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)
{
var typeSize = _fnTypeToSize(vayring.type);
if (!string.IsNullOrEmpty(vayring.attributeName))
{
vertexAttributeVisitor.AddShaderChunk(string.Format("float{0} {1} : {2};", typeSize, vayring.attributeName, vayring.semantic), true);
}
vayringVisitor.AddShaderChunk(string.Format("float{0} {1};", typeSize, vayring.vayringName), false);
vertexShaderBodyVisitor.AddShaderChunk(vayring.vertexCode, false);
pixelShaderInitVisitor.AddShaderChunk(vayring.pixelCode, 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);
packInterpolatorVisitor.AddShaderChunk(string.Format("output.{0} = input.{1};", packed, source), false);
unpackInterpolatorVisitor.AddShaderChunk(string.Format("output.{0} = input.{1};", source, 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();
foreach (var node in activeNodeList)
{
if (node is IGeneratesBodyCode)
(node as IGeneratesBodyCode).GenerateNodeCode(pixelShaderBodyVisitor, mode);
}
foreach (var slot in GetInputSlots<MaterialSlot>())
{
if (!slotIDList.Contains(slot.id))
continue;
foreach (var edge in owner.GetEdges(slot.slotReference))
{
var outputRef = edge.outputSlot;
var fromNode = owner.GetNodeFromGuid<AbstractMaterialNode>(outputRef.nodeGuid);
if (fromNode == null)
continue;
var slotOutputName = slot.shaderOutputName;
var inputStruct = typeof(Lit.SurfaceData).GetFields().Any(o => o.Name == slotOutputName) ? "surfaceData" : "builtinData";
pixelShaderBodyVisitor.AddShaderChunk(inputStruct + "." + slot.shaderOutputName + " = " + fromNode.GetVariableNameForSlot(outputRef.slotId) + ";", true);
}
}
var template =
@"struct FragInput
{
float4 unPositionSS;
${VaryingAttributes}
};
void GetSurfaceAndBuiltinData(FragInput fragInput, out SurfaceData surfaceData, out BuiltinData builtinData)
{
ZERO_INITIALIZE(SurfaceData, surfaceData);
ZERO_INITIALIZE(BuiltinData, builtinData);
${PixelShaderInitialize}
${PixelShaderBody}
}
struct Attributes
{
float3 positionOS : POSITION;
${VertexAttributes}
};
struct Varyings
{
float4 positionHS;
${VaryingAttributes}
};
struct PackedVaryings
{
float4 positionHS : SV_Position;
${PackedVaryingAttributes}
};
PackedVaryings PackVaryings(Varyings input)
{
PackedVaryings output;
output.positionHS = input.positionHS;
${PackingVaryingCode}
return output;
}
FragInput UnpackVaryings(PackedVaryings input)
{
FragInput output;
ZERO_INITIALIZE(FragInput, output);
output.unPositionSS = input.positionHS;
${UnpackVaryingCode}
return output;
}
PackedVaryings VertDefault(Attributes input)
{
Varyings output;
output.positionHS = TransformWorldToHClip(TransformObjectToWorld(input.positionOS));
${VertexShaderBody}
return PackVaryings(output);
}";
var resultShader = template.Replace("${VaryingAttributes}", vayringVisitor.GetShaderString(1));
resultShader = resultShader.Replace("${PixelShaderInitialize}", pixelShaderInitVisitor.GetShaderString(1));
resultShader = resultShader.Replace("${PixelShaderBody}", pixelShaderBodyVisitor.GetShaderString(1));
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 GetShader(MaterialOptions options, GenerationMode mode, out List<PropertyGenerator.TextureInfo> configuredTextures)
{
configuredTextures = new List<PropertyGenerator.TextureInfo>();
var path = "Assets/ScriptableRenderLoop/HDRenderLoop/Shaders/Material/Lit/Lit.template";
if (!System.IO.File.Exists(path))
return "";
var templateText = System.IO.File.ReadAllText(path);
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("#{LitTemplate.*}");
var findUseDataInput = new System.Text.RegularExpressions.Regex("useDataInput:{(.*?)}");
var findNeedFragInput = new System.Text.RegularExpressions.Regex("needFragInput:{(.*?)}");
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);
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", " ");
return Regex.Replace(resultShader, @"\r\n|\n\r|\n|\r", Environment.NewLine);
}
}
[Serializable]
[Title("HDRenderLoop/StandardLit")]
public class StandardtLit : AbstractHDRenderLoopMasterNode
{
protected override string GetName()
{
return "MasterNodeStandardLit";
}
protected override Type GetSurfaceType()
{
return typeof(Lit.SurfaceData);
}
protected override int GetMatchingMaterialID()
{
return (int)Lit.MaterialId.LitStandard;
}
}
[Serializable]
[Title("HDRenderLoop/SubsurfaceScatteringLit")]
public class SubsurfaceScatteringLit : AbstractHDRenderLoopMasterNode
{
protected override string GetName()
{
return "MasterNodeSubsurfaceScatteringLit";
}
protected override Type GetSurfaceType()
{
return typeof(Lit.SurfaceData);
}
protected override int GetMatchingMaterialID()
{
return (int)Lit.MaterialId.LitSSS;
}
}
[Serializable]
[Title("HDRenderLoop/SubsurfaceClearCoatLit")]
public class SubsurfaceClearCoatLit : AbstractHDRenderLoopMasterNode
{
protected override string GetName()
{
return "MasterNodeSubsurfaceClearCoatLit";
}
protected override Type GetSurfaceType()
{
return typeof(Lit.SurfaceData);
}
protected override int GetMatchingMaterialID()
{
return (int)Lit.MaterialId.LitClearCoat;
}
}
[Serializable]
[Title("HDRenderLoop/SpecularColorLit")]
public class SpecularColorLit : AbstractHDRenderLoopMasterNode
{
protected override string GetName()
{
return "MasterNodeSpecularColorLit";
}
protected override Type GetSurfaceType()
{
return typeof(Lit.SurfaceData);
}
protected override int GetMatchingMaterialID()
{
return (int)Lit.MaterialId.LitSpecular;
}
}
}
namespace UnityEngine.Experimental.ScriptableRenderLoop
{
[ExecuteInEditMode]
// This HDRenderLoop assume linear lighting. Don't work with gamma.
public class HDRenderLoop : ScriptableRenderLoop
{
private const string k_HDRenderLoopPath = "Assets/ScriptableRenderLoop/HDRenderLoop/HDRenderLoop.asset";
// Must be in sync with DebugViewMaterial.hlsl
public enum DebugViewVaryingMode
{
TexCoord0 = 1,
TexCoord1 = 2,
TexCoord2 = 3,
VertexTangentWS = 4,
VertexBitangentWS = 5,
VertexNormalWS = 6,
VertexColor = 7,
}
// Must be in sync with DebugViewMaterial.hlsl
public enum DebugViewGbufferMode
{
Depth = 10,
BakeDiffuseLighting = 11,
}
public class DebugParameters
{
// Material Debugging
public int debugViewMaterial = 0;
// Rendering debugging
public bool displayOpaqueObjects = true;
public bool displayTransparentObjects = true;
public bool useForwardRenderingOnly = false;
public bool enableTonemap = true;
public float exposure = 0;
}
private DebugParameters m_DebugParameters = new DebugParameters();
public DebugParameters debugParameters
{
get { return m_DebugParameters; }
}
#if UNITY_EDITOR
[MenuItem("Renderloop/CreateHDRenderLoop")]
static void CreateHDRenderLoop()
{
var instance = ScriptableObject.CreateInstance<HDRenderLoop>();
UnityEditor.AssetDatabase.CreateAsset(instance, k_HDRenderLoopPath);
}
#endif
public class GBufferManager
{
public const int MaxGbuffer = 8;
public void SetBufferDescription(int index, string stringId, RenderTextureFormat inFormat, RenderTextureReadWrite inSRGBWrite)
{
IDs[index] = Shader.PropertyToID(stringId);
RTIDs[index] = new RenderTargetIdentifier(IDs[index]);
formats[index] = inFormat;
sRGBWrites[index] = inSRGBWrite;
}
public void InitGBuffers(int width, int height, CommandBuffer cmd)
{
for (int index = 0; index < gbufferCount; index++)
{
/* RTs[index] = */ cmd.GetTemporaryRT(IDs[index], width, height, 0, FilterMode.Point, formats[index], sRGBWrites[index]);
}
}
public RenderTargetIdentifier[] GetGBuffers(CommandBuffer cmd)
{
var colorMRTs = new RenderTargetIdentifier[gbufferCount];
for (int index = 0; index < gbufferCount; index++)
{
colorMRTs[index] = RTIDs[index];
}
return colorMRTs;
}
/*
public void BindBuffers(Material mat)
{
for (int index = 0; index < gbufferCount; index++)
{
mat.SetTexture(IDs[index], RTs[index]);
}
}
*/
public int gbufferCount { get; set; }
int[] IDs = new int[MaxGbuffer];
RenderTargetIdentifier[] RTIDs = new RenderTargetIdentifier[MaxGbuffer];
RenderTextureFormat[] formats = new RenderTextureFormat[MaxGbuffer];
RenderTextureReadWrite[] sRGBWrites = new RenderTextureReadWrite[MaxGbuffer];
}
public const int MaxLights = 32;
public const int MaxShadows = 16; // Max shadow allowed on screen simultaneously - a point light is 6 shadows
public const int MaxProbes = 32;
[SerializeField]
ShadowSettings m_ShadowSettings = ShadowSettings.Default;
ShadowRenderPass m_ShadowPass;
[SerializeField]
TextureSettings m_TextureSettings = TextureSettings.Default;
Material m_DeferredMaterial;
Material m_FinalPassMaterial;
// TODO: Find a way to automatically create/iterate through these kind of class
Lit.RenderLoop m_LitRenderLoop;
// Debug
Material m_DebugViewMaterialGBuffer;
GBufferManager m_gbufferManager = new GBufferManager();
static private int s_CameraColorBuffer;
static private int s_CameraDepthBuffer;
static private ComputeBuffer s_punctualLightList;
static private ComputeBuffer s_envLightList;
static private ComputeBuffer s_punctualShadowList;
private TextureCacheCubemap m_cubeReflTexArray;
void OnEnable()
{
Rebuild();
}
void OnValidate()
{
Rebuild();
}
void ClearComputeBuffers()
{
if (s_punctualLightList != null)
s_punctualLightList.Release();
if (s_punctualShadowList != null)
s_punctualShadowList.Release();
if (s_envLightList != null)
s_envLightList.Release();
}
Material CreateEngineMaterial(string shaderPath)
{
var mat = new Material(Shader.Find(shaderPath) as Shader)
{
hideFlags = HideFlags.HideAndDontSave
};
return mat;
}
public override void Rebuild()
{
ClearComputeBuffers();
s_CameraColorBuffer = Shader.PropertyToID("_CameraColorTexture");
s_CameraDepthBuffer = Shader.PropertyToID("_CameraDepthTexture");
s_punctualLightList = new ComputeBuffer(MaxLights, System.Runtime.InteropServices.Marshal.SizeOf(typeof(PunctualLightData)));
s_envLightList = new ComputeBuffer(MaxLights, System.Runtime.InteropServices.Marshal.SizeOf(typeof(EnvLightData)));
s_punctualShadowList = new ComputeBuffer(MaxShadows, System.Runtime.InteropServices.Marshal.SizeOf(typeof(PunctualShadowData)));
m_DeferredMaterial = CreateEngineMaterial("Hidden/HDRenderLoop/Deferred");
m_FinalPassMaterial = CreateEngineMaterial("Hidden/HDRenderLoop/FinalPass");
// Debug
m_DebugViewMaterialGBuffer = CreateEngineMaterial("Hidden/HDRenderLoop/DebugViewMaterialGBuffer");
m_ShadowPass = new ShadowRenderPass (m_ShadowSettings);
m_cubeReflTexArray = new TextureCacheCubemap();
m_cubeReflTexArray.AllocTextureArray(32, (int)m_TextureSettings.reflectionCubemapSize, TextureFormat.BC6H, true);
// Init Lit material buffer - GBuffer and init
m_LitRenderLoop = new Lit.RenderLoop(); // Our object can be garbacge collected, so need to be allocate here
m_gbufferManager.gbufferCount = m_LitRenderLoop.GetGBufferCount();
for (int gbufferIndex = 0; gbufferIndex < m_gbufferManager.gbufferCount; ++gbufferIndex)
{
m_gbufferManager.SetBufferDescription(gbufferIndex, "_CameraGBufferTexture" + gbufferIndex, m_LitRenderLoop.RTFormat[gbufferIndex], m_LitRenderLoop.RTReadWrite[gbufferIndex]);
}
m_LitRenderLoop.Rebuild();
}
void OnDisable()
{
m_LitRenderLoop.OnDisable();
s_punctualLightList.Release();
s_envLightList.Release();
s_punctualShadowList.Release();
if (m_DeferredMaterial) DestroyImmediate(m_DeferredMaterial);
if (m_FinalPassMaterial) DestroyImmediate(m_FinalPassMaterial);
m_cubeReflTexArray.Release();
}
void InitAndClearBuffer(Camera camera, RenderLoop renderLoop)
{
// We clear only the depth buffer, no need to clear the various color buffer as we overwrite them.
// Clear depth/stencil and init buffers
{
var cmd = new CommandBuffer();
cmd.name = "InitGBuffers and clear Depth/Stencil";
// Init buffer
// With scriptable render loop we must allocate ourself depth and color buffer (We must be independent of backbuffer for now, hope to fix that later).
// Also we manage ourself the HDR format, here allocating fp16 directly.
// With scriptable render loop we can allocate temporary RT in a command buffer, they will not be release with ExecuteCommandBuffer
// These temporary surface are release automatically at the end of the scriptable renderloop if not release explicitly
int w = camera.pixelWidth;
int h = camera.pixelHeight;
cmd.GetTemporaryRT(s_CameraColorBuffer, w, h, 0, FilterMode.Point, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Default);
cmd.GetTemporaryRT(s_CameraDepthBuffer, w, h, 24, FilterMode.Point, RenderTextureFormat.Depth);
m_gbufferManager.InitGBuffers(w, h, cmd);
cmd.SetRenderTarget(new RenderTargetIdentifier(s_CameraColorBuffer), new RenderTargetIdentifier(s_CameraDepthBuffer));
cmd.ClearRenderTarget(true, false, new Color(0, 0, 0, 0));
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
}
// TEMP: As we are in development and have not all the setup pass we still clear the color in emissive buffer and gbuffer, but this will be removed later.
// Clear HDR target
{
var cmd = new CommandBuffer();
cmd.name = "Clear HDR target";
cmd.SetRenderTarget(new RenderTargetIdentifier(s_CameraColorBuffer), new RenderTargetIdentifier(s_CameraDepthBuffer));
cmd.ClearRenderTarget(false, true, new Color(0, 0, 0, 0));
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
}
// Clear GBuffers
{
var cmd = new CommandBuffer();
cmd.name = "Clear GBuffer";
// Write into the Camera Depth buffer
cmd.SetRenderTarget(m_gbufferManager.GetGBuffers(cmd), new RenderTargetIdentifier(s_CameraDepthBuffer));
// Clear everything
// TODO: Clear is not required for color as we rewrite everything, will save performance.
cmd.ClearRenderTarget(false, true, new Color(0, 0, 0, 0));
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
}
// END TEMP
}
void RenderOpaqueRenderList(CullResults cull, Camera camera, RenderLoop renderLoop, string passName)
{
if (!debugParameters.displayOpaqueObjects)
return;
DrawRendererSettings settings = new DrawRendererSettings(cull, camera, new ShaderPassName(passName));
settings.sorting.sortOptions = SortOptions.SortByMaterialThenMesh;
settings.inputFilter.SetQueuesOpaque();
settings.rendererConfiguration = RendererConfiguration.PerObjectLightProbe | RendererConfiguration.PerObjectReflectionProbes | RendererConfiguration.PerObjectLightmaps | RendererConfiguration.PerObjectLightProbeProxyVolume;
renderLoop.DrawRenderers(ref settings);
}
void RenderTransparentRenderList(CullResults cull, Camera camera, RenderLoop renderLoop, string passName)
{
if (!debugParameters.displayTransparentObjects)
return;
var settings = new DrawRendererSettings(cull, camera, new ShaderPassName(passName))
{
rendererConfiguration = RendererConfiguration.PerObjectLightProbe | RendererConfiguration.PerObjectReflectionProbes,
sorting = { sortOptions = SortOptions.SortByMaterialThenMesh }
};
settings.inputFilter.SetQueuesTransparent();
renderLoop.DrawRenderers(ref settings);
}
void RenderGBuffer(CullResults cull, Camera camera, RenderLoop renderLoop)
{
if (debugParameters.useForwardRenderingOnly)
{
return ;
}
// setup GBuffer for rendering
var cmd = new CommandBuffer { name = "GBuffer Pass" };
cmd.SetRenderTarget(m_gbufferManager.GetGBuffers(cmd), new RenderTargetIdentifier(s_CameraDepthBuffer));
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
// render opaque objects into GBuffer
RenderOpaqueRenderList(cull, camera, renderLoop, "GBuffer");
}
void RenderDebugViewMaterial(CullResults cull, Camera camera, RenderLoop renderLoop)
{
// Render Opaque forward
{
var cmd = new CommandBuffer { name = "DebugView Material Mode Pass" };
cmd.SetRenderTarget(new RenderTargetIdentifier(s_CameraColorBuffer), new RenderTargetIdentifier(s_CameraDepthBuffer));
cmd.ClearRenderTarget(true, true, new Color(0, 0, 0, 0));
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
Shader.SetGlobalInt("_DebugViewMaterial", (int)debugParameters.debugViewMaterial);
RenderOpaqueRenderList(cull, camera, renderLoop, "DebugViewMaterial");
}
// Render GBUffer opaque
{
Vector4 screenSize = ComputeScreenSize(camera);
m_DebugViewMaterialGBuffer.SetVector("_ScreenSize", screenSize);
m_DebugViewMaterialGBuffer.SetFloat("_DebugViewMaterial", (float)debugParameters.debugViewMaterial);
// m_gbufferManager.BindBuffers(m_DeferredMaterial);
// TODO: Bind depth textures
var cmd = new CommandBuffer { name = "GBuffer Debug Pass" };
cmd.Blit(null, new RenderTargetIdentifier(s_CameraColorBuffer), m_DebugViewMaterialGBuffer, 0);
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
}
// Render forward transparent
{
RenderTransparentRenderList(cull, camera, renderLoop, "DebugViewMaterial");
}
// Last blit
{
var cmd = new CommandBuffer { name = "Blit DebugView Material Debug" };
cmd.Blit(s_CameraColorBuffer, BuiltinRenderTextureType.CameraTarget);
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
}
}
Matrix4x4 GetViewProjectionMatrix(Camera camera)
{
// The actual projection matrix used in shaders is actually massaged a bit to work across all platforms
// (different Z value ranges etc.)
var gpuProj = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false);
var gpuVP = gpuProj * camera.worldToCameraMatrix;
return gpuVP;
}
Vector4 ComputeScreenSize(Camera camera)
{
return new Vector4(camera.pixelWidth, camera.pixelHeight, 1.0f / camera.pixelWidth, 1.0f / camera.pixelHeight);
}
void RenderDeferredLighting(Camera camera, RenderLoop renderLoop)
{
if (debugParameters.useForwardRenderingOnly)
{
return;
}
// Bind material data
m_LitRenderLoop.Bind();
var invViewProj = GetViewProjectionMatrix(camera).inverse;
m_DeferredMaterial.SetMatrix("_InvViewProjMatrix", invViewProj);
var screenSize = ComputeScreenSize(camera);
m_DeferredMaterial.SetVector("_ScreenSize", screenSize);
// m_gbufferManager.BindBuffers(m_DeferredMaterial);
// TODO: Bind depth textures
var cmd = new CommandBuffer { name = "Deferred Ligthing Pass" };
cmd.Blit(null, new RenderTargetIdentifier(s_CameraColorBuffer), m_DeferredMaterial, 0);
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
}
void RenderForward(CullResults cullResults, Camera camera, RenderLoop renderLoop)
{
// Bind material data
m_LitRenderLoop.Bind();
var cmd = new CommandBuffer { name = "Forward Pass" };
cmd.SetRenderTarget(new RenderTargetIdentifier(s_CameraColorBuffer), new RenderTargetIdentifier(s_CameraDepthBuffer));
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
if (debugParameters.useForwardRenderingOnly)
{
RenderOpaqueRenderList(cullResults, camera, renderLoop, "Forward");
}
RenderTransparentRenderList(cullResults, camera, renderLoop, "Forward");
}
void RenderForwardUnlit(CullResults cullResults, Camera camera, RenderLoop renderLoop)
{
// Bind material data
m_LitRenderLoop.Bind();
var cmd = new CommandBuffer { name = "Forward Unlit Pass" };
cmd.SetRenderTarget(new RenderTargetIdentifier(s_CameraColorBuffer), new RenderTargetIdentifier(s_CameraDepthBuffer));
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
RenderOpaqueRenderList(cullResults, camera, renderLoop, "ForwardUnlit");
RenderTransparentRenderList(cullResults, camera, renderLoop, "ForwardUnlit");
}
void FinalPass(RenderLoop renderLoop)
{
// Those could be tweakable for the neutral tonemapper, but in the case of the LookDev we don't need that
const float blackIn = 0.02f;
const float whiteIn = 10.0f;
const float blackOut = 0.0f;
const float whiteOut = 10.0f;
const float whiteLevel = 5.3f;
const float whiteClip = 10.0f;
const float dialUnits = 20.0f;
const float halfDialUnits = dialUnits * 0.5f;
// converting from artist dial units to easy shader-lerps (0-1)
var tonemapCoeff1 = new Vector4((blackIn * dialUnits) + 1.0f, (blackOut * halfDialUnits) + 1.0f, (whiteIn / dialUnits), (1.0f - (whiteOut / dialUnits)));
var tonemapCoeff2 = new Vector4(0.0f, 0.0f, whiteLevel, whiteClip / halfDialUnits);
m_FinalPassMaterial.SetVector("_ToneMapCoeffs1", tonemapCoeff1);
m_FinalPassMaterial.SetVector("_ToneMapCoeffs2", tonemapCoeff2);
m_FinalPassMaterial.SetFloat("_EnableToneMap", debugParameters.enableTonemap ? 1.0f : 0.0f);
m_FinalPassMaterial.SetFloat("_Exposure", debugParameters.exposure);
var cmd = new CommandBuffer { name = "FinalPass" };
// Resolve our HDR texture to CameraTarget.
cmd.Blit(s_CameraColorBuffer, BuiltinRenderTextureType.CameraTarget, m_FinalPassMaterial, 0);
renderLoop.ExecuteCommandBuffer(cmd);
cmd.Dispose();
}
void NewFrame()
{
// update texture caches
m_cubeReflTexArray.NewFrame();
}
//---------------------------------------------------------------------------------------------------------------------------------------------------
void UpdatePunctualLights(VisibleLight[] visibleLights, ref ShadowOutput shadowOutput)
{
var lights = new List<PunctualLightData>();
var shadows = new List<PunctualShadowData>();
for (int lightIndex = 0; lightIndex < Math.Min(visibleLights.Length, MaxLights); lightIndex++)
{
var light = visibleLights[lightIndex];
if (light.lightType != LightType.Spot && light.lightType != LightType.Point && light.lightType != LightType.Directional)
continue;
var additionalLightData = light.light.GetComponent<AdditionalLightData>();
var l = new PunctualLightData();
if (light.lightType == LightType.Directional)
{
l.useDistanceAttenuation = 0.0f;
// positionWS store Light direction for directional and is opposite to the forward direction
l.positionWS = -light.light.transform.forward;
l.invSqrAttenuationRadius = 0.0f;
}
else
{
l.useDistanceAttenuation = 1.0f;
l.positionWS = light.light.transform.position;
l.invSqrAttenuationRadius = 1.0f / (light.range * light.range);
}
// Correct intensity calculation (Different from Unity)
var lightColorR = light.light.intensity * Mathf.GammaToLinearSpace(light.light.color.r);
var lightColorG = light.light.intensity * Mathf.GammaToLinearSpace(light.light.color.g);
var lightColorB = light.light.intensity * Mathf.GammaToLinearSpace(light.light.color.b);
l.color.Set(lightColorR, lightColorG, lightColorB);
l.forward = light.light.transform.forward; // Note: Light direction is oriented backward (-Z)
l.up = light.light.transform.up;
l.right = light.light.transform.right;
if (light.lightType == LightType.Spot)
{
var spotAngle = light.spotAngle;
var innerConePercent = AdditionalLightData.GetInnerSpotPercent01(additionalLightData);
var cosSpotOuterHalfAngle = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * Mathf.Deg2Rad), 0.0f, 1.0f);
var cosSpotInnerHalfAngle = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * innerConePercent * Mathf.Deg2Rad), 0.0f, 1.0f); // inner cone
var val = Mathf.Max(0.001f, (cosSpotInnerHalfAngle - cosSpotOuterHalfAngle));
l.angleScale = 1.0f / val;
l.angleOffset = -cosSpotOuterHalfAngle * l.angleScale;
}
else
{
// 1.0f, 2.0f are neutral value allowing GetAngleAnttenuation in shader code to return 1.0
l.angleScale = 1.0f;
l.angleOffset = 2.0f;
}
l.diffuseScale = AdditionalLightData.GetAffectDiffuse(additionalLightData) ? 1.0f : 0.0f;
l.specularScale = AdditionalLightData.GetAffectSpecular(additionalLightData) ? 1.0f : 0.0f;
l.shadowDimmer = AdditionalLightData.GetShadowDimmer(additionalLightData);
l.IESIndex = -1;
l.cookieIndex = -1;
l.shadowIndex = -1;
// Setup shadow data arrays
bool hasShadows = shadowOutput.GetShadowSliceCountLightIndex(lightIndex) != 0;
bool hasNotReachMaxLimit = shadows.Count + (light.lightType == LightType.Point ? 6 : 1) <= MaxShadows;
if (hasShadows && hasNotReachMaxLimit) // Note < MaxShadows should be check at shadowOutput creation
{
// When we have a point light, we assumed that there is 6 consecutive PunctualShadowData
l.shadowIndex = shadows.Count;
for (int sliceIndex = 0; sliceIndex < shadowOutput.GetShadowSliceCountLightIndex(lightIndex); ++sliceIndex)
{
PunctualShadowData s = new PunctualShadowData();
int shadowSliceIndex = shadowOutput.GetShadowSliceIndex(lightIndex, sliceIndex);
s.worldToShadow = shadowOutput.shadowSlices[shadowSliceIndex].shadowTransform.transpose; // Transpose for hlsl reading ?
if (light.lightType == LightType.Spot)
{
s.shadowType = ShadowType.Spot;
}
else if (light.lightType == LightType.Point)
{
s.shadowType = ShadowType.Point;
}
else
{
s.shadowType = ShadowType.Directional;
}
s.bias = light.light.shadowBias;
shadows.Add(s);
}
}
lights.Add(l);
}
s_punctualLightList.SetData(lights.ToArray());
s_punctualShadowList.SetData(shadows.ToArray());
Shader.SetGlobalBuffer("_PunctualLightList", s_punctualLightList);
Shader.SetGlobalInt("_PunctualLightCount", lights.Count);
Shader.SetGlobalBuffer("_PunctualShadowList", s_punctualShadowList);
}
void UpdateReflectionProbes(VisibleReflectionProbe[] activeReflectionProbes)
{
var lights = new List<EnvLightData>();
for (int lightIndex = 0; lightIndex < Math.Min(activeReflectionProbes.Length, MaxProbes); lightIndex++)
{
var probe = activeReflectionProbes[lightIndex];
if (probe.texture == null)
continue;
var l = new EnvLightData();
// CAUTION: localToWorld is the transform for the widget of the reflection probe. i.e the world position of the point use to do the cubemap capture (mean it include the local offset)
l.positionWS = probe.localToWorld.GetColumn(3);
l.envShapeType = EnvShapeType.None;
// TODO: Support sphere in the interface
if (probe.boxProjection != 0)
{
l.envShapeType = EnvShapeType.Box;
}
// remove scale from the matrix (Scale in this matrix is use to scale the widget)
l.right = probe.localToWorld.GetColumn(0);
l.right.Normalize();
l.up = probe.localToWorld.GetColumn(1);
l.up.Normalize();
l.forward = probe.localToWorld.GetColumn(2);
l.forward.Normalize();
// Artists prefer to have blend distance inside the volume!
// So we let the current UI but we assume blendDistance is an inside factor instead
// Blend distance can't be larger than the max radius
// probe.bounds.extents is BoxSize / 2
float maxBlendDist = Mathf.Min(probe.bounds.extents.x, Mathf.Min(probe.bounds.extents.y, probe.bounds.extents.z));
float blendDistance = Mathf.Min(maxBlendDist, probe.blendDistance);
l.innerDistance = probe.bounds.extents - new Vector3(blendDistance, blendDistance, blendDistance);
l.envIndex = m_cubeReflTexArray.FetchSlice(probe.texture);
l.offsetLS = probe.center; // center is misnamed, it is the offset (in local space) from center of the bounding box to the cubemap capture point
l.blendDistance = blendDistance;
lights.Add(l);
}
s_envLightList.SetData(lights.ToArray());
Shader.SetGlobalBuffer("_EnvLightList", s_envLightList);
Shader.SetGlobalInt("_EnvLightCount", lights.Count);
Shader.SetGlobalTexture("_EnvTextures", m_cubeReflTexArray.GetTexCache());
}
public override void Render(Camera[] cameras, RenderLoop renderLoop)
{
if (!m_LitRenderLoop.isInit)
{
m_LitRenderLoop.RenderInit(renderLoop);
}
// Do anything we need to do upon a new frame.
NewFrame();
// Set Frame constant buffer
// TODO...
foreach (var camera in cameras)
{
// Set camera constant buffer
// TODO...
CullingParameters cullingParams;
if (!CullResults.GetCullingParameters(camera, out cullingParams))
continue;
m_ShadowPass.UpdateCullingParameters (ref cullingParams);
var cullResults = CullResults.Cull(ref cullingParams, renderLoop);
renderLoop.SetupCameraProperties(camera);
InitAndClearBuffer(camera, renderLoop);
RenderGBuffer(cullResults, camera, renderLoop);
if (debugParameters.debugViewMaterial != 0)
{
RenderDebugViewMaterial(cullResults, camera, renderLoop);
}
else
{
ShadowOutput shadows;
m_ShadowPass.Render(renderLoop, cullResults, out shadows);
renderLoop.SetupCameraProperties(camera); // Need to recall SetupCameraProperties after m_ShadowPass.Render
UpdatePunctualLights(cullResults.visibleLights, ref shadows);
UpdateReflectionProbes(cullResults.visibleReflectionProbes);
RenderDeferredLighting(camera, renderLoop);
RenderForward(cullResults, camera, renderLoop);
RenderForwardUnlit(cullResults, camera, renderLoop);
FinalPass(renderLoop);
}
renderLoop.Submit();
}
// Post effects
}
#if UNITY_EDITOR
public override UnityEditor.SupportedRenderingFeatures GetSupportedRenderingFeatures()
{
var features = new UnityEditor.SupportedRenderingFeatures
{
reflectionProbe = UnityEditor.SupportedRenderingFeatures.ReflectionProbe.Rotation
};
return features;
}
#endif
}
}