using System.Collections.Generic; using UnityEngine.Rendering; namespace UnityEngine.PostProcessing { using Mode = BuiltinDebugViewsModel.Mode; public sealed class BuiltinDebugViewsComponent : PostProcessingComponentCommandBuffer { static class Uniforms { internal static readonly int _DepthScale = Shader.PropertyToID("_DepthScale"); internal static readonly int _TempRT = Shader.PropertyToID("_TempRT"); internal static readonly int _Opacity = Shader.PropertyToID("_Opacity"); internal static readonly int _MainTex = Shader.PropertyToID("_MainTex"); internal static readonly int _TempRT2 = Shader.PropertyToID("_TempRT2"); internal static readonly int _Amplitude = Shader.PropertyToID("_Amplitude"); internal static readonly int _Scale = Shader.PropertyToID("_Scale"); } const string k_ShaderString = "Hidden/Post FX/Builtin Debug Views"; enum Pass { Depth, Normals, MovecOpacity, MovecImaging, MovecArrows } ArrowArray m_Arrows; class ArrowArray { public Mesh mesh { get; private set; } public int columnCount { get; private set; } public int rowCount { get; private set; } public void BuildMesh(int columns, int rows) { // Base shape var arrow = new Vector3[6] { new Vector3(0f, 0f, 0f), new Vector3(0f, 1f, 0f), new Vector3(0f, 1f, 0f), new Vector3(-1f, 1f, 0f), new Vector3(0f, 1f, 0f), new Vector3(1f, 1f, 0f) }; // make the vertex array int vcount = 6 * columns * rows; var vertices = new List(vcount); var uvs = new List(vcount); for (int iy = 0; iy < rows; iy++) { for (int ix = 0; ix < columns; ix++) { var uv = new Vector2( (0.5f + ix) / columns, (0.5f + iy) / rows ); for (int i = 0; i < 6; i++) { vertices.Add(arrow[i]); uvs.Add(uv); } } } // make the index array var indices = new int[vcount]; for (int i = 0; i < vcount; i++) indices[i] = i; // initialize the mesh object mesh = new Mesh { hideFlags = HideFlags.DontSave }; mesh.SetVertices(vertices); mesh.SetUVs(0, uvs); mesh.SetIndices(indices, MeshTopology.Lines, 0); mesh.UploadMeshData(true); // update the properties columnCount = columns; rowCount = rows; } public void Release() { GraphicsUtils.Destroy(mesh); mesh = null; } } public override bool active { get { return model.IsModeActive(Mode.Depth) || model.IsModeActive(Mode.Normals) || model.IsModeActive(Mode.MotionVectors); } } public override DepthTextureMode GetCameraFlags() { var mode = model.settings.mode; var flags = DepthTextureMode.None; switch (mode) { case Mode.Normals: flags |= DepthTextureMode.DepthNormals; break; case Mode.MotionVectors: flags |= DepthTextureMode.MotionVectors | DepthTextureMode.Depth; break; case Mode.Depth: flags |= DepthTextureMode.Depth; break; } return flags; } public override CameraEvent GetCameraEvent() { return model.settings.mode == Mode.MotionVectors ? CameraEvent.BeforeImageEffects : CameraEvent.BeforeImageEffectsOpaque; } public override string GetName() { return "Builtin Debug Views"; } public override void PopulateCommandBuffer(CommandBuffer cb) { var settings = model.settings; var material = context.materialFactory.Get(k_ShaderString); material.shaderKeywords = null; if (context.isGBufferAvailable) material.EnableKeyword("SOURCE_GBUFFER"); switch (settings.mode) { case Mode.Depth: DepthPass(cb); break; case Mode.Normals: DepthNormalsPass(cb); break; case Mode.MotionVectors: MotionVectorsPass(cb); break; } context.Interrupt(); } void DepthPass(CommandBuffer cb) { var material = context.materialFactory.Get(k_ShaderString); var settings = model.settings.depth; cb.SetGlobalFloat(Uniforms._DepthScale, 1f / settings.scale); cb.Blit((Texture)null, BuiltinRenderTextureType.CameraTarget, material, (int)Pass.Depth); } void DepthNormalsPass(CommandBuffer cb) { var material = context.materialFactory.Get(k_ShaderString); cb.Blit((Texture)null, BuiltinRenderTextureType.CameraTarget, material, (int)Pass.Normals); } void MotionVectorsPass(CommandBuffer cb) { #if UNITY_EDITOR // Don't render motion vectors preview when the editor is not playing as it can in some // cases results in ugly artifacts (i.e. when resizing the game view). if (!Application.isPlaying) return; #endif var material = context.materialFactory.Get(k_ShaderString); var settings = model.settings.motionVectors; // Blit the original source image int tempRT = Uniforms._TempRT; cb.GetTemporaryRT(tempRT, context.width, context.height, 0, FilterMode.Bilinear); cb.SetGlobalFloat(Uniforms._Opacity, settings.sourceOpacity); cb.SetGlobalTexture(Uniforms._MainTex, BuiltinRenderTextureType.CameraTarget); cb.Blit(BuiltinRenderTextureType.CameraTarget, tempRT, material, (int)Pass.MovecOpacity); // Motion vectors (imaging) if (settings.motionImageOpacity > 0f && settings.motionImageAmplitude > 0f) { int tempRT2 = Uniforms._TempRT2; cb.GetTemporaryRT(tempRT2, context.width, context.height, 0, FilterMode.Bilinear); cb.SetGlobalFloat(Uniforms._Opacity, settings.motionImageOpacity); cb.SetGlobalFloat(Uniforms._Amplitude, settings.motionImageAmplitude); cb.SetGlobalTexture(Uniforms._MainTex, tempRT); cb.Blit(tempRT, tempRT2, material, (int)Pass.MovecImaging); cb.ReleaseTemporaryRT(tempRT); tempRT = tempRT2; } // Motion vectors (arrows) if (settings.motionVectorsOpacity > 0f && settings.motionVectorsAmplitude > 0f) { PrepareArrows(); float sy = 1f / settings.motionVectorsResolution; float sx = sy * context.height / context.width; cb.SetGlobalVector(Uniforms._Scale, new Vector2(sx, sy)); cb.SetGlobalFloat(Uniforms._Opacity, settings.motionVectorsOpacity); cb.SetGlobalFloat(Uniforms._Amplitude, settings.motionVectorsAmplitude); cb.DrawMesh(m_Arrows.mesh, Matrix4x4.identity, material, 0, (int)Pass.MovecArrows); } cb.SetGlobalTexture(Uniforms._MainTex, tempRT); cb.Blit(tempRT, BuiltinRenderTextureType.CameraTarget); cb.ReleaseTemporaryRT(tempRT); } void PrepareArrows() { int row = model.settings.motionVectors.motionVectorsResolution; int col = row * Screen.width / Screen.height; if (m_Arrows == null) m_Arrows = new ArrowArray(); if (m_Arrows.columnCount != col || m_Arrows.rowCount != row) { m_Arrows.Release(); m_Arrows.BuildMesh(col, row); } } public override void OnDisable() { if (m_Arrows != null) m_Arrows.Release(); m_Arrows = null; } } }