Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question Rendering a trail behind instanced meshes, and culling by depth

Discussion in 'Shaders' started by officialnsa, Apr 13, 2022.

  1. officialnsa

    officialnsa

    Joined:
    Oct 8, 2020
    Posts:
    2
    Hi there!

    I'm doing a project that involves a very large particle effect with thousands of particles. I'm currently trying to get them to render trails, ideally ones that look kind of opaque, a bit like this image:



    I currently have a very basic trail effect based on another Unity forum post, but the first main issue I've hit is that using this method (i.e., having a second camera that only renders the particles), loses any depth culling, i.e. the particles will render on top of objects in the scene even if said objects are closer to the camera. I feel like I could remedy this with some use of depth buffers, but I also wanted to ask and see if I wasn't just going about it all in the wrong way. I'm a strong programmer but I'm still pretty new to graphics programming in general, so I'm concerned I might just be missing some fundamental truths about how the render pipeline works.

    Here's my code:
    TrailBlitScript.cs

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class TrailBlitScript : MonoBehaviour
    6. {
    7.     // https://forum.unity.com/threads/trail-shader.455338/
    8.     // Start is called before the first frame update
    9.     public Shader trailShader;
    10.     public Shader layerShader;
    11.     Material trailMat, layerMat;
    12.  
    13.     RenderTexture mLastFrameTex;
    14.     public Camera RenderCam;
    15.     void Start()
    16.     {
    17.         RenderCam.clearFlags = CameraClearFlags.Skybox | CameraClearFlags.SolidColor;
    18.         RenderCam.depthTextureMode = DepthTextureMode.Depth;
    19.         trailMat = new Material(trailShader);
    20.         layerMat = new Material(layerShader);
    21.     }
    22.  
    23.     void OnRenderImage(RenderTexture src, RenderTexture dest)
    24.     {
    25.         if (mLastFrameTex == null)
    26.         {
    27.             mLastFrameTex = new RenderTexture(src.width, src.height, 24, RenderTextureFormat.ARGB32);
    28.             trailMat.SetTexture("_TrailTex", mLastFrameTex);
    29.             layerMat.SetTexture("_TrailTex", mLastFrameTex);
    30.         }
    31.  
    32.         Graphics.Blit(src, dest, layerMat);
    33.         RenderTexture pointsCurrentView = RenderTexture.GetTemporary(src.width, src.height, 24, RenderTextureFormat.ARGB32);
    34.         RenderTexture PointsWithTrails = RenderTexture.GetTemporary(src.width, src.height, 24, RenderTextureFormat.ARGB32);
    35.  
    36.         RenderTexture.active = pointsCurrentView;
    37.         GL.Clear(true, true, Color.clear);
    38.         RenderTexture.active = PointsWithTrails;
    39.         GL.Clear(true, true, Color.clear);
    40.         RenderTexture.active = null;
    41.  
    42.         RenderCam.targetTexture = pointsCurrentView;
    43.         RenderCam.Render();
    44.         RenderCam.targetTexture = null;
    45.  
    46.         Graphics.Blit(pointsCurrentView, PointsWithTrails, trailMat);
    47.  
    48.         Graphics.Blit(PointsWithTrails, mLastFrameTex);
    49.         Graphics.Blit(src, dest, layerMat);
    50.         pointsCurrentView.Release();
    51.         PointsWithTrails.Release();
    52.     }
    53. }
    TrailBlitShader.shader

    Code (csharp):
    1. Shader "Hidden/TrailBlitShader"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         _TrailTex ("Texture", 2D) = "white" {}
    7.     }
    8.     SubShader
    9.     {
    10.  
    11.         Cull Off ZWrite Off ZTest Always
    12.         Pass
    13.         {
    14.             CGPROGRAM
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.  
    18.             #include "UnityCG.cginc"
    19.  
    20.             struct appdata
    21.             {
    22.                 float4 vertex : POSITION;
    23.                 float2 uv : TEXCOORD0;
    24.             };
    25.  
    26.             struct v2f
    27.             {
    28.                 float2 uv : TEXCOORD0;
    29.                 float4 vertex : SV_POSITION;
    30.             };
    31.  
    32.             sampler2D _MainTex;
    33.             float4 _MainTex_ST;
    34.             sampler2D _CameraDepthTexture; //unused but ready if needed
    35.             sampler2D _TrailTex;
    36.             float4 _TrailTex_ST;
    37.  
    38.             v2f vert (appdata v)
    39.             {
    40.                 v2f o;
    41.                 o.vertex = UnityObjectToClipPos(v.vertex);
    42.                 o.uv = v.uv;
    43.                 return o;
    44.             }
    45.  
    46.             fixed4 frag (v2f i) : SV_Target
    47.             {
    48.                 fixed4 col = tex2D(_MainTex, i.uv);
    49.                 fixed4 col_prev = tex2D(_TrailTex, i.uv) * 0.95;
    50.                 if (col_prev.a < 0.05) {
    51.                     col_prev = (fixed4)0.;
    52.                 }
    53.  
    54.                 return max(col, col_prev);
    55.             }
    56.             ENDCG
    57.         }
    58.     }
    59. }

    If I'm going for a visual style like the image I included, is this just going about it all wrong? Should I instead be looking to procedurally generate meshes for that? This project is intended for VR, so I don't want to implement anything too complex for fear of overtaxing the system's resources.