Search Unity

Invoking Geometry Shader for every vertex of a mesh

Discussion in 'Shaders' started by Julien_, Jul 4, 2015.

  1. Julien_

    Julien_

    Joined:
    Nov 12, 2013
    Posts:
    43
    Hey guys,

    I'm having issues getting geometry shaders to work properly.
    I'm trying to implement a billboard particle shader using this code:

    Code (csharp):
    1.  
    2. Shader "Custom/SpriteParticleTest"
    3. {
    4.  
    5.    Properties
    6.    {
    7.      _Sprite("Sprite", 2D) = "white" {}
    8.      _Color("Color", Color) = (0.38,0.26,0.98,1.0)
    9.      _Size("Size", float) = 0.1
    10.    }
    11.    SubShader
    12.    {
    13.      Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
    14.      Blend SrcAlpha One
    15.      AlphaTest Greater .01
    16.      ColorMask RGB
    17.      Cull Off Lighting Off ZWrite Off
    18.  
    19.      Pass
    20.      {
    21.      
    22.  
    23.      CGPROGRAM
    24.      #pragma target 5.0
    25.  
    26.      #pragma vertex vert
    27.      #pragma geometry geom
    28.      #pragma fragment frag
    29.  
    30.      #include "UnityCG.cginc"
    31.      fixed4 _Color;
    32.      float _Size;
    33.      sampler2D _Sprite;
    34.      
    35.      struct appdata_t {
    36.        float4 vertex : POSITION;
    37.        fixed4 color : COLOR;
    38.        float2 texcoord : TEXCOORD0;
    39.      };
    40.        
    41.      struct v2g
    42.      {
    43.        float4 pos : SV_POSITION;
    44.        fixed4 color : COLOR;
    45.      };
    46.  
    47.      v2g vert(appdata_t v)
    48.      {
    49.        v2g OUT;
    50.        OUT.pos = mul (UNITY_MATRIX_MVP, float4(v.vertex.xyz,1.0f));
    51.        OUT.color = _Color;
    52.        return OUT;
    53.      }
    54.      
    55.      struct g2f {
    56.        float4 pos : SV_POSITION;
    57.        float2 uv : TEXCOORD0;
    58.        fixed4 color : COLOR;
    59.      };
    60.  
    61.      [maxvertexcount(4)]
    62.      void geom(point v2g IN[1], inout TriangleStream<g2f> outStream)
    63.      {
    64.        float dx = _Size;
    65.        float dy = _Size * _ScreenParams.x / _ScreenParams.y;
    66.        g2f OUT;
    67.        OUT.pos = IN[0].pos + float4(-dx, dy,0,0); OUT.uv=float2(0,0); OUT.color = IN[0].color; outStream.Append(OUT);
    68.        OUT.pos = IN[0].pos + float4( dx, dy,0,0); OUT.uv=float2(1,0); OUT.color = IN[0].color; outStream.Append(OUT);
    69.        OUT.pos = IN[0].pos + float4(-dx,-dy,0,0); OUT.uv=float2(0,1); OUT.color = IN[0].color; outStream.Append(OUT);
    70.        OUT.pos = IN[0].pos + float4( dx,-dy,0,0); OUT.uv=float2(1,1); OUT.color = IN[0].color; outStream.Append(OUT);
    71.        outStream.RestartStrip();
    72.      }
    73.  
    74.      float4 frag (g2f IN) : COLOR
    75.      {
    76.          
    77.        fixed4 col = 2.0f * IN.color * _Color * tex2D(_Sprite, IN.uv);
    78.        return col;    
    79.      }
    80.  
    81.      ENDCG
    82.  
    83.      }
    84.    }
    85.  
    86. Fallback Off
    87. }
    88.  

    The goal is to render a particle sprite for every vertex of the mesh that uses this shader. However the geometry shader doesn't seem to get invoked for every vertex. When I create a material with this shader and attach it to a cube it produces the following output:



    As you can see the geometry shader doesn't output quads for some of the vertices. Also the lower right vertex seems to produce multiple quads (since it's brighter and I'm using additive blending).

    To me it looks like the GS somehow receives triangles as an input even though I specified "point" as the GS primitive. Maybe I'm totally wrong though.

    Any help appreciated!
     
  2. Dolkar

    Dolkar

    Joined:
    Jun 8, 2013
    Posts:
    576
    Now that you bring that up.. I remember reading a thread where some guy had trouble with the triangle adjacency type. The conclusion was that unity does not properly set the input type in the rendering code and always sets triangles as the input type... making it the only geometry shader input type that seems to work, unfortunately. I'd probably go through the effort to actually test it out and send a bug report, but I've done that twice already for similar issues and the cases are still open to this day...

    As a workaround, I suppose you could turn every vertex of every triangle into a quad and hope the overdraw does not become a problem.
     
  3. Julien_

    Julien_

    Joined:
    Nov 12, 2013
    Posts:
    43
    Thanks for confirming, that this is most likely a bug. I will definitely send a bug report.

    My workaround for now if anyone's interested: I'm filling a ComputeBuffer with the vertex positions of my mesh and use Graphics.DrawProcedural(). You can specify MeshTopology.Points to pass the positions to the gpu, then read them from a StructuredBuffer in a vertex shader and pass them to the geometry shader.

    I'm not sure how efficient that is though, since it comes with the overhead of creating the buffer and calling SetData() (potentially every frame for dynamic objects) on it. Works well with just one object with only a few hundred vertices.
     
    Thomas-Mountainborn likes this.
  4. Dolkar

    Dolkar

    Joined:
    Jun 8, 2013
    Posts:
    576
    In case you do and they respond, please let me know, thanks!
     
  5. Julien_

    Julien_

    Joined:
    Nov 12, 2013
    Posts:
    43
    @Dolkar I also never received an answer to my bug report, but the release notes of patch 5.2.2p2 contain some fixes that sound promising. If I have some more time, I wil test if my issue is fixed and post an update to this thread.
     
  6. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    1,416
    I know this is an old issue, but can anyone confirm that this has been resolved?
     
  7. danw_unity

    danw_unity

    Unity Technologies

    Joined:
    Oct 16, 2014
    Posts:
    24
    @castor76 I believe this works as intended, as explained here: https://forum.unity3d.com/threads/billboard-geometry-shader.169415/#post-2882620

    In short: the geometry shader expects points but the cube is made of triangles. There is no built-in Unity geometry made of points but you can create some in script easily. Something like this:

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class geo1 : MonoBehaviour {
    7.  
    8.     void Start()
    9.     {
    10.         MeshFilter mf = GetComponent<MeshFilter>();
    11.  
    12.         mf.mesh = new Mesh();
    13.  
    14.         Vector3[] verts = new Vector3[2];
    15.         verts[0].Set(0.0f, 0.0f, 0.0f);
    16.         verts[1].Set(10.0f, 1.0f, 0.0f);
    17.         mf.mesh.vertices = verts;
    18.  
    19.         // You can use UVs to pass information to the geometry shader
    20.         Vector2[] uvs = new Vector2[2];
    21.         uvs[0].x = 1.0f;
    22.         uvs[0].y = 1.0f;
    23.         uvs[1].x = 2.0f;
    24.         uvs[1].y = 0.5f;
    25.         mf.mesh.uv = uvs;
    26.  
    27.         int[] indices = new int[2];
    28.         indices[0] = 0;
    29.         indices[1] = 1;
    30.         mf.mesh.SetIndices(indices, MeshTopology.Points, 0);
    31.     }  
    32. }
    33.  
     
    Amplify_Paulo, Julien_ and castor76 like this.