"if-then" logic in shaders?

I’m a total n00b to shaders and I’m wondering if it’s possible to do boolean logic and simple if-then constructions in shaders. I’m looking to do something like this:

#define four textures and set them in the GUI

if mask.r <= .1
    color = tex1
if mask.r <= .4  mask.r > .1
    color = tex2
if mask.r <= .6  mask.r > .4
    color = tex3
if mask.r <= .8  mask.r > .6
    color = tex4

In other words, I want the alpha value of a heightmap to control which texture gets drawn for each pixel so I can blend four textures into each other using just the heightmap. It would be like TerainTwoLayer, but with four layers instead. Possible?

don’t use if - then unless you enforce shader model 3

that way you can be sure that the hardware is at least partially capable to handle it. SM2 hardware will die down badly with if logic commonly

Just use a ARGB mask texture,and set each layer’s intensity based on the intensity of the given color-channel,that’s the classical approach.
I suggest you process your heightmap manually in photoshop by selecting certain ranges of colors like you want to do in a shader and coloring them r,g,b for your mask texture.

If you really want to mix more textures with a single mask, you can use the approach apple_motion posted here. If you’re mixing a total of four textures, though, just multiply them by the different channels of the mask. Reducing your mask to a single channel won’t get you any speed boost.

Easier said than done for me I’m afraid; I’m dynamically generating the heightmap in Unity on each run so tweaking it in Photoshop is a no-go. Maybe I can generate a four-color blending map from my heightmap…

That should be pretty easy, and it’s definitely worth the preprocessing and memory in order to be more compatible. While you’re at it, you could even do a slope analysis and apply textures based on that as well as height.

I ended up generating a blending map at runtime based off the previously-generated heightmap. Here is the heightmap I have generated from my geometry:

Here is the blending map that gets generated from the heightmap (everything not black is actually transparent):

Just in case someone might find such a thing useful, here’s my code (I’m sure it’s wildly inefficient):

function CreateHeightmap()
{
    var heightmap : Texture2D = new Texture2D(textureResolution, textureResolution);
    var origin : Vector3 = Vector3(0.0, 0.0, 0.0);
    var heightmapArray :Color[] = new Color[textureResolution * textureResolution];
    
    
    var uvs1 = mesh.uv;
    var uv_x : int = 0;
    var uv_y : int = 0;
    
    var topLeftCornerPos : int = 0;
    var topRightCornerPos : int = 0;
    
    var currentPos : int = 0;
    var cpcX : int = 0;
    var cpcY : int = 0;
    
    var thisDistance : float = 0.0;
    var thisColorValue : float = 0.0;
    var thisColor : Color  = Color.black;
    
    var boxHeight : int = (size * 2) + 1;
    
    
    // draw a gradient on each vertex position in UV space whose whiteness
    // corresponds to its vertex's distance from the center of the globe
    for (i = 0; i < mesh.vertices.Length; i++)
    {
        uv_x = Mathf.RoundToInt(uvs1[i].x * textureResolution);
        uv_y = Mathf.RoundToInt(uvs1[i].y * textureResolution);
        
        var height : float = (Vector3.Distance(origin, mesh.vertices[i]));
        if (height > 1)
        {
            height = (height - 1) * 20;
            if (height > 1) height = 1;
        }
        else
        {
            // ocean pixel, we don't care
            height = 0;
        }
        
        var arrayPos : int = ((uv_y - 1) * textureResolution) + uv_x;
        
        // define a square in which to paint the gradient
        topLeftCornerPos = arrayPos - size - (textureResolution * size);
        topRightCornerPos = arrayPos + size - (textureResolution * size);
        
        for (var q = topLeftCornerPos; q < topRightCornerPos; q++)
        {
            for (var k = 1; k < boxHeight; k++)
            {
                currentPos = q + (k * textureResolution);
                if (currentPos < textureResolution * textureResolution)
                {
                    // calculate this pixel's distance from
                    // the center of the gradient
                    cpcX = currentPos % textureResolution;
                    cpcY = Mathf.Floor(currentPos / textureResolution);
                    
                    // calculate raw distance
                    thisDistance = Vector2.Distance(Vector2(cpcX, cpcY), Vector2(uv_x, uv_y));
                    // translate distance to be 0 - 1 
                    thisDistance = (1 - (thisDistance / boxHeight));
                    if (thisDistance > 1) thisDistance = 1;
                    
                    thisColorValue = (thisDistance * height) / (boxHeight / 4);
                    thisColor = Color(thisColorValue, thisColorValue, thisColorValue);
                    heightmapArray[currentPos] = thisColor + heightmapArray[currentPos]; 
                }
            }
        }
    }
    heightmap.SetPixels(heightmapArray, 0);
    heightmap.Apply();
    
    // write the heightmap to a file for perusal
    var bytes = heightmap.EncodeToPNG();
    File.WriteAllBytes(Application.dataPath + "/../Assets/Dynamics/heightmap.png", bytes);
    return heightmapArray;
}


function CreateBlendingMap(heightmapArray : Color[])
{
    var blendingMap : Texture2D = new Texture2D(textureResolution, textureResolution);
    var blendingMapArray : Color[] = new Color[textureResolution * textureResolution];
    var heightmapValue : float = 0.0;
    
    
    for (var i=0; i < blendingMapArray.length; i++)
    {
        // grab the red component of this pixel
        heightmapValue = heightmapArray[i].r;
        blendingMapArray[i] = DetermineBlendedPixelColorValue(heightmapValue);
    }
    
    // write the blendingMap to disk for perusal
    blendingMap.SetPixels(blendingMapArray, 0);
    blendingMap.Apply();

    bytes = blendingMap.EncodeToPNG();
    File.WriteAllBytes(Application.dataPath + "/../Assets/Dynamics/blendingMap.png", bytes);
}


function DetermineBlendedPixelColorValue(height: float)
{
    // don't go through this song-and-dance for water
    if (height < 0.00){return Color.red;}
    
    // not water, figure out what color to use for the blending map
    var range : float = (1.0 / 7);
    var currentMod : float = 0.0;
    var currentColor : Color = Color(0,0,0,0);
    
    // dirt
    currentMod = (range - Mathf.Abs(height)) / range;
    if (currentMod > 0)
    {
        currentColor = currentColor + (Color(1,0,0,0) * currentMod);
    }
    
    // grass
    currentMod = (range - Mathf.Abs(height - 0.15)) / range;
    if (currentMod > 0)
    {
        currentColor += (Color(0,1,0,0) * currentMod);
    }
    
    // more grass
    currentMod = (range - Mathf.Abs(height - 0.30)) / range;
    if (currentMod > 0)
    {
        currentColor += (Color(0,1,0,0) * currentMod);
    }
    
    // even more grass
    currentMod = (range - Mathf.Abs(height - 0.45)) / range;
    if (currentMod > 0)
    {
        currentColor += (Color(0,1,0,0) * currentMod);
    }
    
    //rocks
    currentMod = (range - Mathf.Abs(height - 0.60)) / range;
    if (currentMod > 0)
    {
        currentColor += (Color(0,0,1,0) * currentMod);
    }
    
    // more rocks
    currentMod = (range - Mathf.Abs(height - 0.75)) / range;
    if (currentMod > 0)
    {
        currentColor += (Color(0,0,1,0) * currentMod);
    }
    
    // mountain
    currentMod = (range - Mathf.Abs(height - 0.90)) / range;
    if (currentMod > 0)
    {
        currentColor += (Color.black * currentMod);
    }
    
    // fix for mountains to make it black
    if (height >= 0.9)
    {
        currentColor = Color.black;
    }
    return currentColor;
}