Blending between two textures per pixel?

Hello everyone,

I am playing around with a Shader that blends from one texture to another. I do this using the distance between two object as the blend factor. Right now it blends the whole texture at once, however I would like it if it checked per pixel how much it should blend. So when you walk towards something, it will blend, starting with the parts closest to you (this would create a much better effect).

How could I go about implementing this?

This is the Shader I’m using:

Shader "Custom/testShader" {
    Properties{
        _Tint("Tint Color", Color) = (.9, .9, .9, 1.0)
        _TexMat1("Base (RGB)", 2D) = "white" {}
        _TexMat2("Base (RGB)", 2D) = "white" {}
        _Blend("Blend", Range(0.0,1.0)) = 0.0
    }

        Category{
        ZWrite On
        Alphatest Greater 0
        Tags{ Queue = Transparent RenderType = Transparent }
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask RGB
        SubShader{
        Pass{

        Material{
        Diffuse[_Tint]
        Ambient[_Tint]
    }
        Lighting On

        SetTexture[_TexMat1]{ combine texture }
        SetTexture[_TexMat2]{ constantColor(0,0,0,[_Blend]) combine texture lerp(constant) previous }
        SetTexture[_TexMat2]{ combine previous + -primary, previous * primary }
    }
    }
        FallBack " Diffuse", 1
    }
}

And this is the Code I use to blend using distance:

using UnityEngine;
using System.Collections;

public class RenderWhileClose : MonoBehaviour {
    public GameObject target;
    public float minDistance = 5.0f;
    public float maxDistance = 10.0f;
   
        Renderer render;
    float distance;

    void Start () {
        render = GetComponent<Renderer>();
    }
   
    void Update () {
        distance = Vector3.Distance(transform.position, target.transform.position);
        float lerp = Mathf.Clamp((distance - minDistance) / (maxDistance - minDistance), 0, 1);
        render.material.SetFloat("_Blend", lerp);
    }
}

Thanks in advance!

The shader you’re using is an old style fixed function shader and the effect you’re looking to do can’t be done with that type of shader. It’s also kind of deprecated in Unity and gets converted into a vertex / fragment shader behind the scenes. You can see the shader it generates by selecting the shader in the editor and clicking on show generated.

If you want to create the effect you’re looking for I suggest reading this tutorial:

Yes, all 5 parts. It does a really good job of explaining the various parts of the Unity shader system add well as shaders and rendering in general.

Once you’ve gotten far enough along to write you’re shader add either a vertex fragment shader or a surface shader you’ll need to change your script to pass the world position of the target object which you can use to do the distance calculations in the shader itself.

Wow, thanks a bunch. That tutorial was super helpful.

I decided to make a Surface shader using the example of the circle on the soldier from that tutorial. Now I get the desired effect (changes per pixel) and it looks great. However I am having difficulties getting some of the functionality from my old shader into this new one.

There is 2 things I want from my old shader and I’m not sure how I could approach them in this new surface shader. 1: The second texture is no longer transparent (even though I use o.Alpha = c.a;) and 2: I am not sure how I can create a blend effect between the two textures, right now its either texture 1 or texture 2.

Could you help me a little more with this?

My new Shader:

Shader "Custom/MatrixShader" {
    Properties {
        _TexMat1("Texture 1", 2D) = "white" {}
        _TexMat2("Texture 2", 2D) = "white" {}
        _Blend("Blend", Range(0.0,1.0)) = 0.0
        _PlayerPos("PlayerPos", Vector) = (0, 0, 0, 0)
    }
    SubShader {
        Tags { "Queue"="Transparent" "RenderType"="Transparent" }
        CGPROGRAM
        #pragma surface surf Lambert
        struct Input {
            float2 uv_TexMat1;
            float2 uv_TexMat2;
            float3 worldPos;
        };

        sampler2D _TexMat1;
        sampler2D _TexMat2;
        float3 _PlayerPos;

        void surf (Input IN, inout SurfaceOutput o) {
            float d = distance(_PlayerPos, IN.worldPos);
            float dN = 1 - saturate(d / 10.0f);

            if (dN > 0.5) {
                o.Albedo = tex2D(_TexMat1, IN.uv_TexMat1).rgb;
            }
            else {
                fixed4 c = tex2D(_TexMat2, IN.uv_TexMat2);
                o.Albedo = c.rgb;
                o.Alpha = c.a;
            }
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Script that updates the second texture (for animation) and the PlayerPos for distance stuff:

using UnityEngine;
using System.Collections;

public class distanceTest2 : MonoBehaviour {
    public GameObject target;
    public Texture2D[] frames;
    public float framesPerSecond = 30.0f;
    public bool animate = true;
    Renderer render;

    void Start()
    {
        render = GetComponent<Renderer>();
    }

    void Update()
    {
        if (animate)
        {
            float index = (Time.time * framesPerSecond) % frames.Length;
            render.material.mainTexture = frames[(int)index];
            render.material.SetTexture("_TexMat2", frames[(int)index]);
            render.material.SetVector("_PlayerPos", target.transform.position);
        }

    }
}

As you can see from the opening post, I no longer have the combine texture function etc. I do not know enough about the syntax of these shaders to really grasp what functions I should be calling etc. If you have another tutorial for it (or I missed it in your previous tutorial, please) please let me know!

Thanks in advance, you have already helped me a ton!

Nevermind, I fixed it :slight_smile:

Hello, Can you give me an insight on how you managed to fix it? I am also interested to know how to do per pixel blending of textures . Thanks

can u give me a simple project now i want to do it too