Crear un esquema en LWRP en Unity

Hola

Te diré cómo crear un efecto de contorno simple en la nueva tubería de renderizado ligero (LWRP) en Unity. Para hacer esto, necesita Unity versión 2018.3 y superior, así como LWRP versión 4.0.0 y superior.

El esquema clásico consiste en un sombreador de dos pasadas, pero LWRP solo admite sombreadores de una pasada. Para corregir este inconveniente en LWRP, se hizo posible agregar un pase personalizado a ciertas etapas de renderizado usando interfaces:

IAfterDepthPrePass IAfterOpaquePass IAfterOpaquePostProcess IAfterSkyboxPass IAfterTransparentPass IAfterRender 

Preparación


Necesitaremos dos sombreadores.

Primero usaré Unlit Color. En cambio, puede usar otro, lo principal es agregar la construcción Stencil al sombreador.

Color apagado
 Shader "Unlit/SimpleColor" { SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { Tags { "LightMode" = "LightweightForward" } Stencil { Ref 2 Comp always Pass replace } HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Core.hlsl" struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = TransformObjectToHClip(v.vertex.xyz); return o; } half4 frag (v2f i) : SV_Target { return half4(0.5h, 0.0h, 0.0h, 1.0h); } ENDHLSL } } } 


El segundo es el sombreador de contorno más simple en sí.

Esquema simple
 Shader "Unlit/SimpleOutline" { SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { Stencil { Ref 2 Comp notequal Pass keep } HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Core.hlsl" struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; half4 _OutlineColor; v2f vert (appdata v) { v2f o; v.vertex.xyz += 0.2 * normalize(v.vertex.xyz); o.vertex = TransformObjectToHClip(v.vertex.xyz); return o; } half4 frag (v2f i) : SV_Target { return _OutlineColor; } ENDHLSL } } } 


Pase personalizado


Escribir un pase personalizado comienza con la creación del MonoBehaviour habitual y la implementación de una de las interfaces mencionadas anteriormente. Usamos IAfterOpaquePass, ya que el contorno solo se aplicará a objetos opacos.

 public class OutlinePass : MonoBehaviour, IAfterOpaquePass { public ScriptableRenderPass GetPassToEnqueue(RenderTextureDescriptor baseDescriptor, RenderTargetHandle colorAttachmentHandle, RenderTargetHandle depthAttachmentHandle) { //... } } 

Este script debe agregarse a la cámara. Y a través de él organizaremos la interacción de nuestro pasaje con la lógica del juego, pero más sobre eso más adelante.

Ahora comencemos a escribir el pasaje mismo. Para hacer esto, cree una clase heredada de ScriptableRenderPass

 public class OutlinePassImpl : ScriptableRenderPass { public OutlinePassImpl() { //... } public override void Execute(ScriptableRenderer renderer, ScriptableRenderContext context, ref RenderingData renderingData) { //... } } 

En el constructor, registramos el nombre del pasaje, creamos material y configuraciones para filtrar objetos visibles después del enfriamiento. En el filtro, estableceremos solo objetos opacos, ya que agregaremos nuestro pase después del pase opaco.

La función Ejecutar es una función de procesamiento para pasar. En él, creamos configuraciones para el renderizado, establecemos el material creado en el constructor y renderizamos todos los objetos visibles que satisfacen el filtro creado.

OutlinePassImpl que me resultó
 public class OutlinePassImpl : ScriptableRenderPass { private Material outlineMaterial; private FilterRenderersSettings m_OutlineFilterSettings; private int OutlineColorId; public OutlinePassImpl(Color outlineColor) { //      ,   ,    // SimpleColor RegisterShaderPassName("LightweightForward"); //   outline shader,   outlineMaterial = CoreUtils.CreateEngineMaterial("Unlit/SimpleOutline"); OutlineColorId = Shader.PropertyToID("_OutlineColor"); outlineMaterial.SetColor(OutlineColorId, outlineColor); m_OutlineFilterSettings = new FilterRenderersSettings(true) { renderQueueRange = RenderQueueRange.opaque, }; } public override void Execute(ScriptableRenderer renderer, ScriptableRenderContext context, ref RenderingData renderingData) { Camera camera = renderingData.cameraData.camera; SortFlags sortFlags = renderingData.cameraData.defaultOpaqueSortFlags; // a       DrawRendererSettings drawSettings = CreateDrawRendererSettings(camera, sortFlags, RendererConfiguration.None, renderingData.supportsDynamicBatching); drawSettings.SetOverrideMaterial(outlineMaterial, 0); context.DrawRenderers(renderingData.cullResults.visibleRenderers, ref drawSettings, m_OutlineFilterSettings); } } 


Ahora agreguemos la clase OutlinePass. Aquí todo es muy simple para crear una instancia de la clase OutlinePassImpl y a través del enlace puede interactuar con el pase de usuario en modo de tiempo de ejecución. Por ejemplo, para cambiar el color del contorno.

OutlinePass que me resultó
 public class OutlinePass : MonoBehaviour, IAfterOpaquePass { public Color OutlineColor; private OutlinePassImpl outlinePass; public ScriptableRenderPass GetPassToEnqueue(RenderTextureDescriptor baseDescriptor, RenderTargetHandle colorAttachmentHandle, RenderTargetHandle depthAttachmentHandle) { return outlinePass ?? (outlinePass = new OutlinePassImpl(OutlineColor)); } } 


Ahora configure la escena para la prueba.

  1. Crear material desde el sombreador SimpleColor
  2. Crea un cubo y cuelga material sobre él.
  3. Agregue un script OutlinePass a la cámara y configure el color.
  4. Y haga clic en jugar

El esquema solo será visible en la Vista del juego.

Aquí está el resultado que se debe obtener.



Bonus: retroiluminación amigo-enemigo


Usando la configuración para filtrar objetos visibles, puede especificar una capa o capa de renderizado para aplicar este pasaje a un objeto específico o grupo de objetos y asociarlo con la lógica del juego.

Cambia nuestro pase para que todos los objetos con la capa "Amigo" tengan un contorno verde, y con la capa "Enemigo" roja.

OutlinePass y OutlinePassImpl
 public class OutlinePass : MonoBehaviour, IAfterOpaquePass { [System.Serializable] public class OutlineData { public Color Color; public LayerMask Layer; } public List<OutlineData> outlineDatas = new List<OutlineData>(); private OutlinePassImpl outlinePass; public ScriptableRenderPass GetPassToEnqueue(RenderTextureDescriptor baseDescriptor, RenderTargetHandle colorAttachmentHandle, RenderTargetHandle depthAttachmentHandle) { return outlinePass ?? (outlinePass = new OutlinePassImpl(outlineDatas)); } } public class OutlinePassImpl : ScriptableRenderPass { private Material[] outlineMaterial; private FilterRenderersSettings[] m_OutlineFilterSettings; public OutlinePassImpl(List<OutlinePass.OutlineData> outlineDatas) { RegisterShaderPassName("LightweightForward"); outlineMaterial = new Material[outlineDatas.Count]; m_OutlineFilterSettings = new FilterRenderersSettings[outlineDatas.Count]; Shader outlineShader = Shader.Find("Unlit/SimpleOutline"); int OutlineColorId = Shader.PropertyToID("_OutlineColor"); for (int i = 0; i < outlineDatas.Count; i++) { OutlinePass.OutlineData outline = outlineDatas[i]; Material material = CoreUtils.CreateEngineMaterial(outlineShader); material.SetColor(OutlineColorId, outline.Color); outlineMaterial[i] = material; m_OutlineFilterSettings[i] = new FilterRenderersSettings(true) { renderQueueRange = RenderQueueRange.opaque, layerMask = outline.Layer }; } } public override void Execute(ScriptableRenderer renderer, ScriptableRenderContext context, ref RenderingData renderingData) { Camera camera = renderingData.cameraData.camera; SortFlags sortFlags = renderingData.cameraData.defaultOpaqueSortFlags; DrawRendererSettings drawSettings = CreateDrawRendererSettings(camera, sortFlags, RendererConfiguration.None, renderingData.supportsDynamicBatching); for (int i = 0; i < outlineMaterial.Length; i++) { drawSettings.SetOverrideMaterial(outlineMaterial[i], 0); context.DrawRenderers(renderingData.cullResults.visibleRenderers, ref drawSettings, m_OutlineFilterSettings[i]); } } } 


En la escena, agregue las capas "Amigo" y "Enemigo", duplique el cubo varias veces, asígneles capas a "Amigo" o "Enemigo", configure el Pase de esquema y ejecútelo.



Y esto es lo que obtenemos.



Conclusión


La nueva representación en Unity se expande muy bien, lo que hace que la creación de efectos interesantes sea realmente fácil.

Espero que el artículo haya sido útil para leer. Si alguien tiene preguntas, nos vemos en los comentarios.

Source: https://habr.com/ru/post/es430792/


All Articles