Olá.
Vou explicar como criar um efeito de estrutura de tópicos simples no novo Lightweight Render Pipeline (LWRP) no Unity. Para fazer isso, você precisa do Unity versão 2018.3 e superior, bem como do LWRP versão 4.0.0 e superior.
O esquema clássico consiste em um shader de duas passagens, mas o LWRP suporta apenas shaders de passagem única. Para corrigir essa desvantagem no LWRP, tornou-se possível adicionar uma passagem personalizada a certos estágios de renderização usando interfaces:
IAfterDepthPrePass IAfterOpaquePass IAfterOpaquePostProcess IAfterSkyboxPass IAfterTransparentPass IAfterRender
Preparação
Vamos precisar de dois shaders.
Primeiro vou usar Unlit Color. Em vez disso, você pode usar outro, o principal é adicionar a construção Stencil ao shader.
Cor apagada 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 } } }
O segundo é o próprio shader de contorno mais simples.
Esboço simples 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 } } }
Passe personalizado
A criação de um passe personalizado começa com a criação do MonoBehaviour usual e a implementação de uma das interfaces mencionadas acima. Usamos IAfterOpaquePass, pois o contorno só será aplicado a objetos opacos.
public class OutlinePass : MonoBehaviour, IAfterOpaquePass { public ScriptableRenderPass GetPassToEnqueue(RenderTextureDescriptor baseDescriptor, RenderTargetHandle colorAttachmentHandle, RenderTargetHandle depthAttachmentHandle) {
Este script deve ser adicionado à câmera. E através dele organizaremos a interação de nossa passagem com a lógica do jogo, mas mais sobre isso mais tarde.
Agora vamos começar a escrever a passagem em si. Para fazer isso, crie uma classe herdada de ScriptableRenderPass
public class OutlinePassImpl : ScriptableRenderPass { public OutlinePassImpl() {
No construtor, registramos o nome da passagem, criamos material e configurações para filtrar objetos visíveis após o resfriamento. No filtro, defina apenas objetos opacos, pois adicionaremos nosso passe após o passe opaco.
A função Execute é uma função de renderização para aprovação. Nele, criamos configurações para renderização, definimos o material criado no construtor e renderizamos todos os objetos visíveis que satisfazem o filtro criado.
OutlinePassImpl que acabou comigo public class OutlinePassImpl : ScriptableRenderPass { private Material outlineMaterial; private FilterRenderersSettings m_OutlineFilterSettings; private int OutlineColorId; public OutlinePassImpl(Color outlineColor) {
Agora vamos adicionar a classe OutlinePass. Aqui tudo é muito simples para criar uma instância da classe OutlinePassImpl e, através do link, você pode interagir com a passagem do usuário no modo de tempo de execução. Por exemplo, para alterar a cor do contorno.
OutlinePass que acabou comigo 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)); } }
Agora monte a cena para o teste.
- Criar material a partir do SimpleColor Shader
- Crie um cubo e pendure o material nele.
- Adicione um script OutlinePass à câmera e defina a cor.
- E clique em play
O esboço estará visível apenas na visualização do jogo.
Aqui está o resultado deve ser obtido.

Bônus: luz de fundo amigo-inimigo
Usando a configuração para filtrar objetos visíveis, é possível especificar uma camada ou renderizar para aplicar essa passagem a um objeto ou grupo de objetos específico e associá-lo à lógica do jogo.
Altere nosso passe para que todos os objetos com a camada "Friend" tenham um contorno verde e com a camada "Enemy" em vermelho.
OutlinePass e 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]); } } }
Na cena, adicione as camadas "Amigo" e "Inimigo", duplique o cubo várias vezes, atribua-as a "Amigo" ou "Inimigo", defina o Passe de estrutura de tópicos e execute-o.

E é isso que obtemos.

Conclusão
A nova renderização no Unity está se expandindo muito bem, o que facilita a criação de efeitos interessantes.
Espero que o artigo tenha sido útil para leitura. Se alguém tiver dúvidas, veja você nos comentários.