Search Unity

Creating a particle outline shader

Discussion in 'Shaders' started by rbjacob101, Aug 23, 2019.

  1. rbjacob101

    rbjacob101

    Joined:
    Jul 17, 2018
    Posts:
    3
    Hi there!

    I wish to create an unlit shader for a particle system that emits cube meshes, such that each emitted mesh has a hard black outline around it.

    Here is the pass for the outline:

    struct appdata {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    };

    struct v2f {
    float4 pos : POSITION;
    float4 color : COLOR;
    };

    uniform float _Outline;
    uniform float4 _OutlineColor;

    v2f vert(appdata v) {
    v2f o;

    v.vertex *= ( 1 + _Outline);

    o.pos = UnityObjectToClipPos(v.vertex);

    o.color = _OutlineColor;
    return o;
    }

    half4 frag(v2f i) :COLOR { return i.color; }


    (And after this is a simple pass to render the unlit geometry of the mesh itself...)

    As you can see, we are simply stretching the vertices outward... but from what?

    For a single cube mesh, the shader works perfectly:



    However, when applied to a particle system emitting cube meshes, the shader breaks down:



    My suspicion is that the line
    v.vertex *= ( 1 + _Outline);
    stretches the vertices outward from the object center, not the mesh center.

    Does anyone have a replacement shader or insight on how to fix this problem?

    Thanks,
    rbjacob
     
    richardkettlewell likes this.
  2. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    1,153
    Hi,

    All vertices in a particle system are in world space.
    They are not relative to the particle or the particle system.

    To fix this, you can use Custom Vertex Streams. Enable the option in the Particle System Renderer, and add the "Center" stream to the list. Center is the center position of each particle (in world space). Take note of which TEXCOORD it gets mapped to.

    Next, in your shader, declare an input to your vertex shader called Center, which occupies the same TEXCOORD.
    Then, modify your shader code to be:

    Code (CSharp):
    1. float3 vert = v.vertex - v.center;
    2. vert *= (1 + _Outline);
    3. v.vertex = vert + v.center;
     
    rbjacob101 likes this.
  3. rbjacob101

    rbjacob101

    Joined:
    Jul 17, 2018
    Posts:
    3
    Thanks for the quick response, I will look into Vertex Streams when I am home and let you know if it works.
    rbjacob101
     
  4. rbjacob101

    rbjacob101

    Joined:
    Jul 17, 2018
    Posts:
    3
    @richardkettlewell Yep, vertex streams are the way to go! Thanks for letting me know.

    For anyone interested, here's the vert and frag code:


    struct appdata {
    float3 vertex : POSITION;
    float4 color : COLOR;
    float3 center : TEXCOORD0;
    };

    struct v2f {
    float4 pos : POSITION;
    float4 color : COLOR;
    float3 center : TEXCOORD0;

    };
    uniform float _Outline;
    uniform float4 _OutlineColor;
    v2f vert(appdata v) {
    v2f o;
    o.center = v.center;
    float3 vert = v.vertex - v.center;
    vert *= ( 1 + _Outline);
    v.vertex = vert + v.center;
    o.pos = UnityObjectToClipPos(v.vertex);

    o.color = _OutlineColor;
    return o;
    }

    half4 frag(v2f i) :COLOR { return i.color; }
     
    richardkettlewell likes this.