Transparent Horizon Bending shader

Hi, shader experts!
I have this custom unlit surface shader that does a simple horizon bending for a mobile game:

OPAQUE VERSION

Shader "Custom/Curved Unlit" {
    Properties{
        _MainTex("Base (RGB)", 2D) = "white" {}
        _Curvature("Curvature", Float) = 0.001
    }

    SubShader{
        Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }

        CGPROGRAM

            #pragma surface surf NoLighting noambient vertex:vert

            sampler2D _MainTex;
            uniform float _Curvature;

            struct Input {
                half2 uv_MainTex;
            };

            void vert(inout appdata_full v)    {
                float4 vv = mul(_Object2World, v.vertex);
                vv.xyz -= _WorldSpaceCameraPos.xyz;
                vv = float4(0.0f, (vv.z * vv.z) * -_Curvature, 0.0f, 0.0f);
                v.vertex += mul(_World2Object, vv);
            }

            fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, fixed atten){
                fixed4 c;
                c.rgb = s.Albedo;
                c.a = s.Alpha;
                return c;
            }

            void surf(Input IN, inout SurfaceOutput o)    {
                o.Albedo = tex2D(_MainTex, IN.uv_MainTex);
            }

        ENDCG
    }

    Fallback "Mobile/VertexLit"
}

TRANSPARENT VERSION

Shader "Custom/Curved Trans" {
    Properties{
        _MainTex("Base (RGB)", 2D) = "white" {}
        _Curvature("Curvature", Float) = 0.001
    }

    SubShader{
    Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }

    CGPROGRAM

        #pragma surface surf NoLighting noambient vertex:vert alpha

        sampler2D _MainTex;
        uniform float _Curvature;

        struct Input {
            half2 uv_MainTex;
        };

        void vert(inout appdata_full v) {
            float4 vv = mul(_Object2World, v.vertex);
            vv.xyz -= _WorldSpaceCameraPos.xyz;
            vv = float4(0.0f, (vv.z * vv.z) * -_Curvature, 0.0f, 0.0f);
            v.vertex += mul(_World2Object, vv);
        }

        fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, fixed atten) {
            fixed4 c;
            c.rgb = s.Albedo;
            c.a = s.Alpha;
            return c;
        }

        void surf(Input IN, inout SurfaceOutput o) {
            float4 col = tex2D(_MainTex, IN.uv_MainTex);
            o.Albedo = col.rgb;
            o.Alpha = col.a;
        }

    ENDCG
    }

    Fallback "Mobile/VertexLit"
}

As you can see, it works good. The opaque objects use the opaque version and the blue energy balls use the transparent version of the shader.

But those blue energy balls should be soft additive particles, and my shader doesn’t produce an additive effect.
I tried to look in the builtin particles shaders but they use SetTexture[_MainTex] to handle the alpha blending and I don’t know how to use that in my shader.

How can I modify the transparent version to have soft additive blending?

Thanks! :slight_smile:

If you want an unlit shader then there’s little to no reason to use a surface shader. I would suggest switching to using vertex / fragment shaders.

As for the soft particles, look at the source for the non-mobile particle shaders.

1 Like

I think I’ve successfully converted the Particles/Additive (Soft) shader to its Horizon Bended variant.
I also deleted the fog and the soft particles support (I don’t need it).

Here are the two versions:

UNLIT OPAQUE

Shader "Horizon Bended/Unlit" {

    Properties{
        _MainTex("Base (RGB)", 2D) = "white" {}
        _Curvature("Curvature", Float) = 2000
        _Curved("Curved", Int) = 0
    }

    Category{

        Tags{ "Queue" = "Geometry" "RenderType" = "Geometry" }
     
        SubShader{
     
            Pass{

                CGPROGRAM

                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"

                struct appdata_t {
                    float4 vertex : POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord : TEXCOORD0;
                };

                struct v2f {
                    float4 vertex : SV_POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord : TEXCOORD0;
                };


                float4 _MainTex_ST;
                sampler2D _MainTex;
                float _Curvature;
                int _Curved;


                v2f vert(appdata_t v) {
                    v2f o;
                    if (!_Curved) o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                    else {
                        float4 pos = mul(UNITY_MATRIX_MV, v.vertex);
                        float distanceSquared = pos.x * pos.x + pos.z * pos.z;
                        float radius = 1 / _Curvature;
                        pos.y -= radius - sqrt(max(1.0 - distanceSquared / (radius *radius), 0.0)) * radius;
                        o.vertex = mul(UNITY_MATRIX_P, pos);
                        o.color = v.color;
                    }
                    o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                    return o;
                }


                fixed4 frag(v2f i) : SV_Target{
                    half4 col = i.color * tex2D(_MainTex, i.texcoord);
                    col.rgb *= col.a;
                    return col;
                }

                ENDCG

            }

        }

    }
}

UNLIT ADDITIVE

Shader "Horizon Bended/Additive" {

    Properties{
        _MainTex("Base (RGB)", 2D) = "white" {}
        _Curvature("Curvature", Float) = 2000
        _Curved("Curved", Int) = 0
    }

    Category{

        Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }

        Blend One OneMinusSrcColor
        ColorMask RGB
        Cull Off Lighting Off ZWrite Off

        SubShader{

            Pass{

                CGPROGRAM

                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"

                struct appdata_t {
                    float4 vertex : POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord : TEXCOORD0;
                };

                struct v2f {
                    float4 vertex : SV_POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord : TEXCOORD0;
                };


                float4 _MainTex_ST;
                sampler2D _MainTex;
                float _Curvature;
                int _Curved;


                v2f vert(appdata_t v){
                    v2f o;
                    if (!_Curved) o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                    else {
                        float4 pos = mul(UNITY_MATRIX_MV, v.vertex);
                        float distanceSquared = pos.x * pos.x + pos.z * pos.z;
                        float radius = 1 / _Curvature;
                        pos.y -= radius - sqrt(max(1.0 - distanceSquared / (radius *radius), 0.0)) * radius;
                        o.vertex = mul(UNITY_MATRIX_P, pos);
                        o.color = v.color;
                    }
                    o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                    return o;
                }


                fixed4 frag(v2f i) : SV_Target{
                    half4 col = i.color * tex2D(_MainTex, i.texcoord);
                    col.rgb *= col.a;
                    return col;
                }
             
                    ENDCG

            }

        }

    }
}

Thanks for the good advice!! :slight_smile:

thanks buddy !
helped me very much. and served my purpose well.
Much appreciated.
Do you have a Curved Shader on which I can apply color instead of texture ???

1 Like

Hi, Glad to hear that!
If you want to use a color multiplier you have to add a color property (i.e. _Color) and then multiply each pixel by that color in the fragment shader procedure:

Shader "Horizon Bended/Unlit" {

   Properties{
       _Color("Color", Color) = (1,1,1,1)
       _MainTex("Texture", 2D) = "white" {}
       _Curvature("Curvature", Float) = 2000
       _Curved("Curved", Int) = 0
   }

   Category{

       Tags{ "Queue" = "Geometry" "RenderType" = "Geometry" }

       SubShader{

           Pass{

               CGPROGRAM

               #pragma vertex vert
               #pragma fragment frag
               #include "UnityCG.cginc"

               struct appdata_t {
                   float4 vertex : POSITION;
                   fixed4 color : COLOR;
                   float2 texcoord : TEXCOORD0;
               };

               struct v2f {
                   float4 vertex : SV_POSITION;
                   fixed4 color : COLOR;
                   float2 uv : TEXCOORD0;
               };

               float4 _Color;
               float _Curvature;
               int _Curved;
               sampler2D _MainTex;
               float4 _MainTex_ST;

               v2f vert(appdata_t v) {
                   v2f o;
                   if (!_Curved) o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                   else {
                       float4 pos = mul(UNITY_MATRIX_MV, v.vertex);
                       float distanceSquared = pos.x * pos.x + pos.z * pos.z;
                       float radius = 1 / _Curvature;
                       pos.y -= radius - sqrt(max(1.0 - distanceSquared / (radius *radius), 0.0)) * radius;
                       o.vertex = mul(UNITY_MATRIX_P, pos);
                       o.color = v.color;
                   }
                   o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                   return o;
               }


               fixed4 frag(v2f i) : SV_Target{
                   half4 col = tex2D(_MainTex, i.uv) * _Color;
                   return col;
               }      
              
               ENDCG
           }
       }
   }
}

thanks allot !
it really helped me. Last thing if you have a minute.
Objects with alpha blended shader appear above than others. Is there a way I can fix it ?
Thanks allot for your help though. Again :slight_smile:

Have you tried to change the render queue to 2000? Not sure if this would cause bad transparency issues…

actually yes the issue in the transparent version was that I wanted to bend both axis so it’s more like a sphere but the Additive shader script is bit different and I am not getting how I edit it

Like previously it was ! for only one axis bend
vv = float4(0.0f, (vv.z * vv.z) * -_Curvature, 0.0f, 0.0f);

I changed it to
vv = float4( 0.0f, ((vv.z * vv.z) + (vv.x * vv.x)) * - _Curvature, 0.0f, 0.0f );

but the Additive shader is differently written. I am so sorry if I am disturbing you. Actually I am totally new to shader scripting.

It’s differently written 'cause I’m referring to the last version of the shader, not the first in this thread. It uses a better algorithm to compute the bending.
Actually, I made this shader some months ago and I need some time to make it sphere-bending. Stay tuned, I’ll reply if I’ll succed! :slight_smile:

1 Like

Thanks allot buddy. I will be grateful for that.

I have one question, can you please say how you done camera following effect with shader?

I’m not sure to understand your request.

Are you asking how the camera follows the bent path?
If so, I can say the camera doesn’t follow a curved path at all, it always goes straight, like the ball and the other moving objects in the scene. It’s just an effect that renders far surfaces in a different position.