10
votes

Here is my shader, which replaces one color with another:

Shader "Custom/SwapColors" 
{ 
    Properties
    {
        _MainTex("Texture",2D)="white"{}
        swap_from("Swap From",COLOR)=(1,1,1,1)
        swap_to("Swap To",COLOR)=(1,1,1,1)
        threshold("Threshold",Float)=0.00001
    }
    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
        }
        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            sampler2D _MainTex;
            float4 swap_from;
            float4 swap_to;
            float threshold;

            struct VertexInput
            {
                float4 pos:POSITION;
                float2 uv:TEXCOORD0;
            };
            struct FragmentInput
            {
                float4 pos:SV_POSITION;
                float2 uv:TEXCOORD0;
            };

            FragmentInput vert(VertexInput data)
            {
                FragmentInput res;
                res.pos=mul(UNITY_MATRIX_MVP,data.pos);
                res.uv=data.uv;
                return res;
            }
            bool eq(fixed f,fixed s)
            {
                return abs(f-s)<threshold;
            }
            float4 frag(FragmentInput data):COLOR
            {
                float4 res=tex2D(_MainTex,data.uv.xy).rgba;
                if(eq(res.r,swap_from.r)&&eq(res.g,swap_from.g)&&eq(res.b,swap_from.b))
                    res.rgb=swap_to.rgb;
                return res;
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

Here is a Test script attached to gameobject which contains main camera. In it I try to render a line and a textured quad.

mat is assigned with SwapColors with a texture.

sprite_mat is assigned with default sprite shader with the same texture.

using UnityEngine;
using System.Collections; 

public class Test : MonoBehaviour 
{
    [SerializeField]
    Material mat;
    [SerializeField]
    Material sprite_mat;
    void OnPostRender()
    {


        GL.PushMatrix();
        GL.LoadProjectionMatrix(Camera.main.projectionMatrix);
        GL.modelview=Camera.main.worldToCameraMatrix;

        GL.Begin(GL.LINES);
        sprite_mat.SetPass(0);
        GL.Vertex(new Vector3(0,0));
        GL.Vertex(new Vector3(5,0));
        GL.End();


        GL.Begin(GL.QUADS);
        sprite_mat.SetPass(0);

        mat.SetPass(0);//<------------------------------------ PROBLEM

        GL.TexCoord(new Vector3(0,0));
        GL.Vertex(new Vector2(0,0));

        GL.TexCoord(new Vector3(1,0));
        GL.Vertex(new Vector2(1,0));

        GL.TexCoord(new Vector3(1,1));
        GL.Vertex(new Vector2(1,1));

        GL.TexCoord(new Vector3(0,1));
        GL.Vertex(new Vector2(0,1));
        GL.End();


        GL.PopMatrix();
    }
}

When I render the quad with sprite_mat, everything is ok.

When I use mat, not only the quad disappear, but the line too, despite it's rendered with sprite_mat.

If I create another quad in editor and set its material to mat, it's rendered correctly.

My main question is how can setPass affect geometry rendered after another unrelated setPass.

Example project: forum.unity3d.com/attachments/errorgl-7z.138105/


Edit: I found a default Unity sprite shader and compared it with my. The quad disappeared because it faced opposite direction from the camera. It's fixed by Cull Off.

If I add line

float4 color:COLOR;

to VertexInput structure, material with my shader stops affecting geometry with other materials. I don't understand what is happening.

1
Before I get the project and try to reproduce the issue - can you please provide the Unity version you are using? - itchee

1 Answers

0
votes

I don't think you can SetPass(0) twice between GL.Begin(..) and GL.End(..).

You can set various passes with SetPass(0); [..] SetPass(1); but looking at your shader I don't think that is what you want to do.

So are you trying to draw the quad with 2 passes? One with the default Sprite material and the other with your custom material? Or just draw it twice?

I do not fully understand what do you want to do here.