Criando um esboço sobre LWRP no Unity

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() { //... } public override void Execute(ScriptableRenderer renderer, ScriptableRenderContext context, ref RenderingData renderingData) { //... } } 

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) { //      ,   ,    // 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); } } 


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.

  1. Criar material a partir do SimpleColor Shader
  2. Crie um cubo e pendure o material nele.
  3. Adicione um script OutlinePass à câmera e defina a cor.
  4. 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.

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


All Articles