I have about 2+ years of experience in computer gaming development and I just started writing my own shaders to help me utilize performance. I have this problem that I'm trying to work around but after several tries nothing seems to work.

Basically, I have a plane that's made of 16641 vertices - thats 32768 triangles and I know that is pretty much. The plain has a texture on it with some colors. Now, what I am trying to do is placing units on that plane. Where they stand, the vertex under them will have a certain color but all other vertexes on the plain will have the color white. The idea is to discard and don't draw vertexes that aren't white.

Now in the shader, I tried two things.

1 - In the vertex input I check if the vertex, called o, had the o.color.b value != 0. If so it was given the alpha value of 0 and would not be seen when rendering.

2 - In the vertex output, I checked the same thing ( if(i.color.b !=0) discard;) and discarded the fragment so it would not be rendered. That way I thought Unity should not count these triangles and thus wouldn't waste performance.

Now when I run my game and open the stats, with the plane active I have 85.8k triangles and only 28 draw calls. With the plane un-active I have 20.2k triangles and 26 draw calls (Note : there are some units, interface, skies etc around that counts up to 26 draw calls, thus the plane is just 2 draw calls).

I thought this solution would be clever to save draw calls (developing for mobile games) by treating this plane and the colors under the units as one instead of making them all a separate gameobject. Turns out, I'm way over budget on the triangles. Does anyone know if this is possible and if so, give a good answer or explenation?

Here is the following shader.

Shader "Custom/Ottar_VertexBasic" {
Properties {
    _MainTex ("Particle Texture", 2D) = "white" {}
    _Transparency("Transparency", Range(0,1)) = 1

SubShader {
    Tags { "Queue" = "Transparent" }
    Blend SrcAlpha OneMinusSrcAlpha
    AlphaTest Greater .01
    ColorMask RGB
    Lighting Off
    ZWrite Off
    //Fog { Color (0,0,0,0) }
    Pass {
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_builtin
            #pragma fragmentoption ARB_precision_hint_fastest
            #include "UnityCG.cginc"

            uniform float4  _MainTex_ST;
            float _Transparency;
            struct vI {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
                float4 color : COLOR;

            uniform sampler2D _MainTex;
            struct vO {
                float4 pos : SV_POSITION;
                float2  uv;
                float4 color;

            vO vert (vI v) {
                vO o;
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                float4 viewPos = mul(UNITY_MATRIX_MV, v.vertex);
                o.color = float4(v.color.rgb, v.color.a*1);
                //if(o.color.b != 0) o.color.a = 0;
                //else o.color.a = 1;
                return o;

            float4 frag (vO i) : COLOR {

                if(i.color.b !=0) discard;
                half4 texcol = tex2D( _MainTex, i.uv );
                return texcol*i.color*_Transparency;
Fallback "Particles/Alpha Blended"

1 Answer 1


Unity cannot know which triangles are eliminated in the GPU shaders, it counts the number of triangles sent to the GPU, not just the ones that end up drawn.

Using the discard keyword forces the shader to run on all pixels, breaking the GPU's hidden surface removal optimization.

That is on top of processing all the triangles.

If you want to eliminate triangles at the vertex shader set all its vertices to the same value (such as zero). It will create a degenerate triangle that will be eliminated by the GPU.

        vO vert (vI v) {
            vO o;
            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
            o.color = float4(v.color.rgb, v.color.a*1);
            if(o.color.b != 0) { // all vertices must end up with the same color.b
                o.pos = 0;
            return o;

And disable blending if you don't need it:

Blend One Zero

  • \$\begingroup\$ This might actually work - will try to build soon and see how it performs. But I need two things though, o.color.a = 0 still has to be in there and I need the blending option. Thank you, I'll let you know if this was the solution needed. \$\endgroup\$ Jan 14, 2015 at 16:34
  • \$\begingroup\$ Also, check the part about degenerate strip on the wikipedia page for triangle strip (en.wikipedia.org/wiki/Triangle_strip) its a convenient trick to eliminate triangles when you don't have access to the primitive restart feature or when you want to eliminate single triangles without updating the entire mesh vertex buffer by degenerating triangles using the vertex indices. \$\endgroup\$ Jan 14, 2015 at 16:40
  • \$\begingroup\$ Yes I knew about the triangle strip method. but I didn't get it to work - maybe I will try it for the final release. But if I may disturb you with my last question about this case. For some reason when I build on Samsung Note4 I always seem to get the fallback shader but not the one I wrote here above. Does this have anything to do with the SrcAlpha OneMicusSrcAlpha ? \$\endgroup\$ Jan 17, 2015 at 15:17
  • \$\begingroup\$ Or even stranger thing - this is just not working at all. It does work in Unity and the editor but does not work when I build on the phone \$\endgroup\$ Jan 17, 2015 at 15:20
  • \$\begingroup\$ You can also nuke an individual vertex by setting its projected position to a NaN or infinity. This will abort any triangle using the vertex, rather than stretching one corner to (0, 0, 0) while the other corners (which happened to not fail the test) stay in their usual places. \$\endgroup\$
    – DMGregory
    Aug 7, 2018 at 15:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .