Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Blending between two textures per pixel?

Discussion in 'Shaders' started by M0ff3l, Jun 12, 2016.

  1. M0ff3l

    M0ff3l

    Joined:
    Jun 12, 2016
    Posts:
    4
    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:

    Code (CSharp):
    1. Shader "Custom/testShader" {
    2.     Properties{
    3.         _Tint("Tint Color", Color) = (.9, .9, .9, 1.0)
    4.         _TexMat1("Base (RGB)", 2D) = "white" {}
    5.         _TexMat2("Base (RGB)", 2D) = "white" {}
    6.         _Blend("Blend", Range(0.0,1.0)) = 0.0
    7.     }
    8.  
    9.         Category{
    10.         ZWrite On
    11.         Alphatest Greater 0
    12.         Tags{ Queue = Transparent RenderType = Transparent }
    13.         Blend SrcAlpha OneMinusSrcAlpha
    14.         ColorMask RGB
    15.         SubShader{
    16.         Pass{
    17.  
    18.         Material{
    19.         Diffuse[_Tint]
    20.         Ambient[_Tint]
    21.     }
    22.         Lighting On
    23.  
    24.         SetTexture[_TexMat1]{ combine texture }
    25.         SetTexture[_TexMat2]{ constantColor(0,0,0,[_Blend]) combine texture lerp(constant) previous }
    26.         SetTexture[_TexMat2]{ combine previous + -primary, previous * primary }
    27.     }
    28.     }
    29.         FallBack " Diffuse", 1
    30.     }
    31. }
    And this is the Code I use to blend using distance:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class RenderWhileClose : MonoBehaviour {
    5.     public GameObject target;
    6.     public float minDistance = 5.0f;
    7.     public float maxDistance = 10.0f;
    8.    
    9.         Renderer render;
    10.     float distance;
    11.  
    12.     void Start () {
    13.         render = GetComponent<Renderer>();
    14.     }
    15.    
    16.     void Update () {
    17.         distance = Vector3.Distance(transform.position, target.transform.position);
    18.         float lerp = Mathf.Clamp((distance - minDistance) / (maxDistance - minDistance), 0, 1);
    19.         render.material.SetFloat("_Blend", lerp);
    20.     }
    21. }
    Thanks in advance!
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    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:
    http://www.alanzucconi.com/2015/06/10/a-gentle-introduction-to-shaders-in-unity3d/

    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.
     
  3. M0ff3l

    M0ff3l

    Joined:
    Jun 12, 2016
    Posts:
    4
    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:

    Code (CSharp):
    1. Shader "Custom/MatrixShader" {
    2.     Properties {
    3.         _TexMat1("Texture 1", 2D) = "white" {}
    4.         _TexMat2("Texture 2", 2D) = "white" {}
    5.         _Blend("Blend", Range(0.0,1.0)) = 0.0
    6.         _PlayerPos("PlayerPos", Vector) = (0, 0, 0, 0)
    7.     }
    8.     SubShader {
    9.         Tags { "Queue"="Transparent" "RenderType"="Transparent" }
    10.         CGPROGRAM
    11.         #pragma surface surf Lambert
    12.         struct Input {
    13.             float2 uv_TexMat1;
    14.             float2 uv_TexMat2;
    15.             float3 worldPos;
    16.         };
    17.  
    18.         sampler2D _TexMat1;
    19.         sampler2D _TexMat2;
    20.         float3 _PlayerPos;
    21.  
    22.         void surf (Input IN, inout SurfaceOutput o) {
    23.             float d = distance(_PlayerPos, IN.worldPos);
    24.             float dN = 1 - saturate(d / 10.0f);
    25.  
    26.             if (dN > 0.5) {
    27.                 o.Albedo = tex2D(_TexMat1, IN.uv_TexMat1).rgb;
    28.             }
    29.             else {
    30.                 fixed4 c = tex2D(_TexMat2, IN.uv_TexMat2);
    31.                 o.Albedo = c.rgb;
    32.                 o.Alpha = c.a;
    33.             }
    34.         }
    35.         ENDCG
    36.     }
    37.     FallBack "Diffuse"
    38. }
    39.  
    Script that updates the second texture (for animation) and the PlayerPos for distance stuff:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class distanceTest2 : MonoBehaviour {
    5.     public GameObject target;
    6.     public Texture2D[] frames;
    7.     public float framesPerSecond = 30.0f;
    8.     public bool animate = true;
    9.     Renderer render;
    10.  
    11.     void Start()
    12.     {
    13.         render = GetComponent<Renderer>();
    14.     }
    15.  
    16.     void Update()
    17.     {
    18.         if (animate)
    19.         {
    20.             float index = (Time.time * framesPerSecond) % frames.Length;
    21.             render.material.mainTexture = frames[(int)index];
    22.             render.material.SetTexture("_TexMat2", frames[(int)index]);
    23.             render.material.SetVector("_PlayerPos", target.transform.position);
    24.         }
    25.  
    26.     }
    27. }
    28.  
    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!
     
  4. M0ff3l

    M0ff3l

    Joined:
    Jun 12, 2016
    Posts:
    4
    Nevermind, I fixed it :)
     
  5. Gopee

    Gopee

    Joined:
    Jul 12, 2013
    Posts:
    1
    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
     
  6. tearzna

    tearzna

    Joined:
    Jul 11, 2015
    Posts:
    2
    can u give me a simple project now i want to do it too