Skip to main content
nixed unused function
Source Link
DMGregory
  • 141k
  • 23
  • 258
  • 401
const static int maxLayerCount = 8;
const static float epsilon = 1E-4;

int layerCount;
float3 baseColours[maxLayerCount];
float baseStartHeights[maxLayerCount];
float baseBlends[maxLayerCount];
float baseColourStrength[maxLayerCount];
float baseTextureScales[maxLayerCount];

float inverseLerp(float a, float b, float value) {
    return saturate((value-a)/(b-a));
}

float3 triplanar(float3 worldPos, float scale, float3 blendAxes, Texture2DArray textures, SamplerState ss, int textureIndex) {
    float3 scaledWorldPos = worldPos / scale;
    float3 xProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.y, scaledWorldPos.z), textureIndex) * blendAxes.x;
    float3 yProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.x, scaledWorldPos.z), textureIndex) * blendAxes.y;
    float3 zProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.x, scaledWorldPos.y), textureIndex) * blendAxes.z;
    return xProjection + yProjection + zProjection;
}

void layer_terrain_float(float3 worldPos, float heightPercent, float3 worldNormal, Texture2DArray textures, SamplerState ss, int layerCount, out float3 albedo) {
    float3 blendAxes = abs(worldNormal);
    blendAxes /= blendAxes.x + blendAxes.y + blendAxes.z;

    albedo = 0.0f;

    for (int i = 0; i < layerCount; i ++) {
        float drawStrength = inverseLerp(-baseBlends[i]/2 - epsilon, baseBlends[i]/2, heightPercent - baseStartHeights[i]);

        float3 baseColour = baseColours[i] * baseColourStrength[i];
        float3 textureColour = triplanar(worldPos, baseTextureScales[i], blendAxes, textures, ss, i) * (1-baseColourStrength[i]);

        albedo = albedo * (1-drawStrength) + (baseColour+textureColour) * drawStrength;
    }
}
const static int maxLayerCount = 8;
const static float epsilon = 1E-4;

int layerCount;
float3 baseColours[maxLayerCount];
float baseStartHeights[maxLayerCount];
float baseBlends[maxLayerCount];
float baseColourStrength[maxLayerCount];
float baseTextureScales[maxLayerCount];

float inverseLerp(float a, float b, float value) {
    return saturate((value-a)/(b-a));
}

float3 triplanar(float3 worldPos, float scale, float3 blendAxes, Texture2DArray textures, SamplerState ss, int textureIndex) {
    float3 scaledWorldPos = worldPos / scale;
    float3 xProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.y, scaledWorldPos.z), textureIndex) * blendAxes.x;
    float3 yProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.x, scaledWorldPos.z), textureIndex) * blendAxes.y;
    float3 zProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.x, scaledWorldPos.y), textureIndex) * blendAxes.z;
    return xProjection + yProjection + zProjection;
}

void layer_terrain_float(float3 worldPos, float heightPercent, float3 worldNormal, Texture2DArray textures, SamplerState ss, int layerCount, out float3 albedo) {
    float3 blendAxes = abs(worldNormal);
    blendAxes /= blendAxes.x + blendAxes.y + blendAxes.z;

    albedo = 0.0f;

    for (int i = 0; i < layerCount; i ++) {
        float drawStrength = inverseLerp(-baseBlends[i]/2 - epsilon, baseBlends[i]/2, heightPercent - baseStartHeights[i]);

        float3 baseColour = baseColours[i] * baseColourStrength[i];
        float3 textureColour = triplanar(worldPos, baseTextureScales[i], blendAxes, textures, ss, i) * (1-baseColourStrength[i]);

        albedo = albedo * (1-drawStrength) + (baseColour+textureColour) * drawStrength;
    }
}
const static int maxLayerCount = 8;
const static float epsilon = 1E-4;

int layerCount;
float3 baseColours[maxLayerCount];
float baseStartHeights[maxLayerCount];
float baseBlends[maxLayerCount];
float baseColourStrength[maxLayerCount];
float baseTextureScales[maxLayerCount];

float3 triplanar(float3 worldPos, float scale, float3 blendAxes, Texture2DArray textures, SamplerState ss, int textureIndex) {
    float3 scaledWorldPos = worldPos / scale;
    float3 xProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.y, scaledWorldPos.z), textureIndex) * blendAxes.x;
    float3 yProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.x, scaledWorldPos.z), textureIndex) * blendAxes.y;
    float3 zProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.x, scaledWorldPos.y), textureIndex) * blendAxes.z;
    return xProjection + yProjection + zProjection;
}

void layer_terrain_float(float3 worldPos, float heightPercent, float3 worldNormal, Texture2DArray textures, SamplerState ss, int layerCount, out float3 albedo) {
    float3 blendAxes = abs(worldNormal);
    blendAxes /= blendAxes.x + blendAxes.y + blendAxes.z;

    albedo = 0.0f;

    for (int i = 0; i < layerCount; i ++) {
        float drawStrength = inverseLerp(-baseBlends[i]/2 - epsilon, baseBlends[i]/2, heightPercent - baseStartHeights[i]);

        float3 baseColour = baseColours[i] * baseColourStrength[i];
        float3 textureColour = triplanar(worldPos, baseTextureScales[i], blendAxes, textures, ss, i) * (1-baseColourStrength[i]);

        albedo = albedo * (1-drawStrength) + (baseColour+textureColour) * drawStrength;
    }
}
Source Link
DMGregory
  • 141k
  • 23
  • 258
  • 401

Here's a quick stab at how to convert your shader code to a custom function node we can apply in a Shader Graph.

First, we'll make a new text file to hold the shader code. I called mine "shaderGraphArray.cginc"

const static int maxLayerCount = 8;
const static float epsilon = 1E-4;

int layerCount;
float3 baseColours[maxLayerCount];
float baseStartHeights[maxLayerCount];
float baseBlends[maxLayerCount];
float baseColourStrength[maxLayerCount];
float baseTextureScales[maxLayerCount];

float inverseLerp(float a, float b, float value) {
    return saturate((value-a)/(b-a));
}

float3 triplanar(float3 worldPos, float scale, float3 blendAxes, Texture2DArray textures, SamplerState ss, int textureIndex) {
    float3 scaledWorldPos = worldPos / scale;
    float3 xProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.y, scaledWorldPos.z), textureIndex) * blendAxes.x;
    float3 yProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.x, scaledWorldPos.z), textureIndex) * blendAxes.y;
    float3 zProjection = SAMPLE_TEXTURE2D_ARRAY(textures, ss, float2(scaledWorldPos.x, scaledWorldPos.y), textureIndex) * blendAxes.z;
    return xProjection + yProjection + zProjection;
}

void layer_terrain_float(float3 worldPos, float heightPercent, float3 worldNormal, Texture2DArray textures, SamplerState ss, int layerCount, out float3 albedo) {
    float3 blendAxes = abs(worldNormal);
    blendAxes /= blendAxes.x + blendAxes.y + blendAxes.z;

    albedo = 0.0f;

    for (int i = 0; i < layerCount; i ++) {
        float drawStrength = inverseLerp(-baseBlends[i]/2 - epsilon, baseBlends[i]/2, heightPercent - baseStartHeights[i]);

        float3 baseColour = baseColours[i] * baseColourStrength[i];
        float3 textureColour = triplanar(worldPos, baseTextureScales[i], blendAxes, textures, ss, i) * (1-baseColourStrength[i]);

        albedo = albedo * (1-drawStrength) + (baseColour+textureColour) * drawStrength;
    }
}

Things to note here:

  • I refactored the main workhorse of iterating through the layers into a void function that takes all its inputs as arguments and puts its output into an out argument.

  • I changed the texture-sampling macros from the old, pre-scriptable-render-pipeline UNITY_SAMPLE_TEX2DARRAY to the new D3D11+ style SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index). Based on this thread, I was able to find a list of all these macros here.

  • These macros now want a sampler state, so I included that as another argument to pass down.

The rest should be a pretty straightforward conversion.

Then I called this custom function from a shader graph as shown here:

Shader graph example