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

Height-based gradient shader -- can't figure out how to add texture

Discussion in 'Shaders' started by Slowmanrunning, Aug 20, 2014.

  1. Slowmanrunning

    Slowmanrunning

    Joined:
    Jan 15, 2014
    Posts:
    3
    I'm working on a shader which paints a gradient based on relative height. Here's a picture to give you some idea of it:

    To do this, I blended between color A and color B based off of the UV coordinates, an offset called 'Gradient Position' in the above picture, and an additional 'Gradient Softness' factor. (0.0 = a hard, instant transition from Color A to Color B, 1.0 = a very gradual transition from Color A to Color B) For meshes I plan on having use this effect, I have to create a height-based UV map for them:

    What I can't figure out, is how to combine the gradient with some sand, grass, or other texture. Currently what happens when I apply a texture is this:

    I'm not really sure how to make my shader access the separate UVMap I made (2nd picture, bottom right, "UVRegular") and use that UVMap for the main texture.

    Any advice on some way I could handle this would be greatly appreciated.
     
    Last edited: Aug 20, 2014
  2. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Rather than sample your texture based on height, you probably want to sample it based on the regular UVs, and then blend the samples based on height. This is similar to what you've done successfully with colours: you're not choosing colour A and B based on height, but instead using height to blend between them.
     
  3. kebrus

    kebrus

    Joined:
    Oct 10, 2011
    Posts:
    415
    The problem is, to sample a texture you need appropriate UVs, but you are using the UVs for the height based effect to sample the texture

    in order to fix it, you either have two sets of UVs (one for the textures and another for the effect) or you simply remove the UVs for the height based effect and use just one set of UVs for the textures

    think about it, you called it height-based effect, so why don't you use the height for the effect? instead of using UVs you can simply use the Y position (either local of global) of vertices to produce the same effect you don't need UVs
     
  4. Slowmanrunning

    Slowmanrunning

    Joined:
    Jan 15, 2014
    Posts:
    3
    I currently have the 2 sets of UVs, I just can't figure out how to use the second one. I didn't want to use global or local position to create the gradient because I wanted the possibility to manipulate the UVMaps.

    I ran the Debug/UV1 and Debug/UV2 code here: http://docs.unity3d.com/Manual/SL-VertexProgramInputs.html
    And from the looks of it my model does have 2 UV maps, I'm just not sure how to access the second one. I tried having:

    struct vertexInput {
    float4 vertex : POSITION;
    float4 texcoord0 : TEXCOORD0;
    };

    struct fragmentInput{
    float4 position : SV_POSITION;
    float2 texcoord0 : TEXCOORD0;
    };
    In the Height based pass, and then:

    struct Input {
    float2 uv_MainTex : TEXCOORD1;
    };
    In the texture/toon lighting surface pass, but it doesn't seem to work.
     
    Last edited: Aug 20, 2014
  5. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
    If you are doing it for your project and you have control over the meshes, I suggest using vertex colors - it is simpler and more efficient.
     
  6. Slowmanrunning

    Slowmanrunning

    Joined:
    Jan 15, 2014
    Posts:
    3
    Aha! It seems I got it working.



    The terrain is a little rough due to being a low poly, slapped together testing mesh, but the effect is there and looking good.

    Turned out the answer to "how to access different uv maps in a surface shader" was to change:

    sampler2D _MainTex;

    struct Input {
    float2 uv_MainTex : TEXCOORD0;
    };

    void surf (Input IN, inout SurfaceOutput o) {
    half4 c = tex2D(_MainTex, IN.uv_MainTex);
    o.Albedo = c.rgb;
    o.Alpha = c.a;
    }
    As follows:

    sampler2D _MainTex;

    struct Input {
    float2 uv2_MainTex : TEXCOORD0;
    };

    void surf (Input IN, inout SurfaceOutput o) {
    half4 c = tex2D(_MainTex, IN.uv2_MainTex);
    o.Albedo = c.rgb;
    o.Alpha = c.a;
    }
    As a bit more explenation why I want the gradient to be uv based and not strictly height based: picture a terrain with grassy hills as above, but instead this mesh is much bigger and there's a valley. All the hills/highlights in the valley would lean far too heavily towards one color, while the hills/highlights up above the valley would lean far too heavily against the other. With UVs you could cheat that effect by modifying the UV map. Actually, using 2 uv maps this shader could do any sort of color highlighting/gradient shift you want, not just height-based.
     
    domkia likes this.
  7. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
    Ok, just proposing a slightly different workflow for the sake of experimentation.

    You have a landscape with vertices in 3 rows : Bottom, middle and top. You paint vertices with 0,0,0,0 at bottom, 1,1,1,1 at top and what you want in the middle.

    Then you interpolate from color a to color b based on the vertex colors. You can paint them say in greyscale, and "cheat" as you wish in a more controllable manner (painting vertices is more intuitive than adjusting uvs). If you feel adventurous you can use only alpha for the above operation and mix 3 textures using the rgb channels.