Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Share a function between two passes inside CG Shader for unity3d

I'm writing a shader in CG language for Unity3d.
If you make a shader for transparent object you need to create two similar passes in SubShader. The first one to render only back faces (with Cull Front) and the second one to render only front faces (with Cull Back). But the code for vertex and fragment function is the same for two passes.

Is it possible not to double a code and declare some functions, that would be shared between passes?
I want to have something like in my code example:

Shader "cool shader" {
Properties {
    ...
}
SubShader {

    CGPROGRAM
    // need to declare vertexOutput somewhow here
    float4 sharedFragFoo(vertexOutput i) : COLOR  // How to make smth like this?
    {
        ....
        return float4(...);
    }
    ENDCG

    pass {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        vertexOutput vert(vertexInput v) {
            vertexOutput o;     
            ...
            return o;
        }

        float4 frag(vertexOutput i) : COLOR
        {
            return sharedFragFoo(i); // call the shared between passes function
        }

        ENDCG
    }

    pass {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        vertexOutput vert(vertexInput v) {
            vertexOutput o;
            ...
            return o;
        }

        float4 frag(vertexOutput i) : COLOR
        {
            return sharedFragFoo(i); // call the shared between passes function
        }

        ENDCG
    }
}
}

UPD: Found out how to do it using includes.
But is it possible to do inside one file?

like image 879
Temak Avatar asked May 29 '15 12:05

Temak


2 Answers

You can do it in one file using CGINCLUDE. If you look at the shader for MobileBlur ("Hidden/FastBlur") by Unity it has shared code at the top and passes below.

Here are just the key parts - note CGINCLUDE/ENDCG outside of the SubShader/Pass

Shader "YourShader"
{
    ...

    CGINCLUDE

    #include "UnityCG.cginc"

    struct shared_v2f
    {
        float4 pos : SV_POSITION;
    }

    shared_v2f myVert( appdate_img v )
    {
        shared_v2f o;

        o.pos = mul (UNITY_MATRIX_MVP, v.vertex);

        return o;
    }

    fixed4 myFrag( shared_v2f i ) : SV_Target
    {
        return fixed4( 1.0, 0.5, 0.0, 1.0 );
    }

    ENDCG

    SubShader
    {
        ...

        Pass
        {
            CGPROGRAM 

            #pragma vertex myVert
            #pragma fragment myFrag

            ENDCG
        }
    }
}
like image 179
Hugo Scott-Slade Avatar answered Jan 04 '23 04:01

Hugo Scott-Slade


Answering my own question. Wierdo!
Hope it will help somebody else.

You can write all that betwee CGPROGRAM and ENDCG in separate *.cginc file and include it inside each pass.
Important! But you need to write #pragma vertex vert and #pragma fragment frag inside your main shader file, otherwise it will compile but won't work. I suppose that the reason is that pragma'ss are processed before include's.

Here is my code sample.
Main shader definition file:

    Shader "cool shader" {
    Properties {
        // properties
    }
    SubShader {
        ...

        pass {
            Cull Front
            ZWrite Off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "shared.cginc"
            ENDCG
        }

        pass {
            Cull Back
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "shared.cginc"
            ENDCG
        }
    }
}

Shared file shared.cginc:

#ifndef SHARED_FOO
#define SHARED_FOO

uniform sampler2D _MainTex;
uniform float4 _MainTex_ST;
uniform float4 _Color;
// other variables....

struct vertexInput {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
};

struct vertexOutput {
    float4 pos : SV_POSITION;
    float4 tex : TEXCOORD0;
    float4 posWorld : TEXCOORD1;
    float4 posInObjectCoords : TEXCOORD2;
    float3 normalDir : TEXCOORD3;
};

vertexOutput vert(vertexInput v) {
    vertexOutput o;
    // do staff 
    return o;
}

float4 frag(vertexOutput i) : COLOR
{
    // do staff 
    return float4(...);
}
#endif // SHARED_FOO
like image 32
Temak Avatar answered Jan 04 '23 06:01

Temak