0
\$\begingroup\$

I want to return a colour for a position t, given an arbitrary number of colour stops - a stop is comprised of a position, and a colour.

Like selecting a colour from a multiple-colour gradient, linearly interpolated between stops.

Positions and point t are floating point numbers and can be negative. Positions and their respective colours are ordered lowest to highest in the arrays.

I would like to avoid conditional statements, as this is for use within a shader.

Code would ideally be in the form:

float3 MultiColorLerp(float positions[], float3[] colors, float t) {
    // Returns the color at the point t.
}
\$\endgroup\$
4
  • \$\begingroup\$ So you have a line with points (positions) each point has an assigned color, and given a position on this line, you want to find the interpolated color? \$\endgroup\$
    – TomTsagk
    Nov 5, 2018 at 11:22
  • 1
    \$\begingroup\$ The best way to get a gradient without conditionals in a shader is to write your gradient into a texture and sample it. \$\endgroup\$
    – DMGregory
    Nov 5, 2018 at 13:21
  • \$\begingroup\$ @DMGregory Agreed, that seems the best approach. I'll create the desired texture before involving this shader. \$\endgroup\$ Nov 5, 2018 at 13:29
  • \$\begingroup\$ Unity also includes a prebuilt gradient class. I understand if you need your own implementation, but it's worth looking into. link: docs.unity3d.com/ScriptReference/Gradient.html \$\endgroup\$
    – Zebraman
    Nov 6, 2018 at 14:06

1 Answer 1

0
\$\begingroup\$

Since we don't know the size of the arrays here, we need to pass that as a parameter, and we'll need at least a little conditional branching to control the loop.

We can technically avoid other if-type branching using ther step function to mask-to-zero any colours from segments of the gradient that t isn't in, but I don't recommend it. This does all the work of rendering a pixel from every span of the gradient instead of just the span we're in. You might well find that a little branching to find the right span and compute just that one colour is more efficient.

float3 MultiColorLerp(float positions[], float3[] colors, float t, int numStops) {
    // If t is less than all stops, use the first color.
    float3 color = colors[0] * step(t, positions[0]);

    // If t is greater than all stops, use the last color.
    color += colors[numStops - 1] * step(positions[numStops - 1], t);

    // Compute blended color for each span, and mask out all but the span containing t
    int start;
    float inRange;
    float s;
    for(int end = 1; end < numStops; end++) {
        start = end - 1;
        inRange = step(positions[start], t) * step(t, positions[end]);
        s = (t - positions[start])/(positions[end]- positions[start]);

        color += inRange * lerp(colors[start], colors[end], s);
    }
}

Compare this to a version where we're more accepting of branches:

float3 MultiColorLerp(float positions[], float3[] colors, float t, int numStops) {
    // Handle t outside the gradient's range.
    if(t <= positions[0])
        return colors[0];

    if(t >= positions[numStops - 1])
        return colors[numStops - 1])

    // Find the span t sits within.
    int end = 1;
    while(positions[end] < t)
        end++;

    // Blend colors for this span.
    float s = (t - positions[end - 1])/(positions[end] - positions[end - 1]);

    return lerp(colors[end - 1], colors[end], s);
}

Since these branches will be reasonably coherent (within any one span of the gradient, all adjacent fragments will take the same path — it's only where the gradient crosses one of its position keys that you'll have neighbours branching inconsistently), I'd be willing to bet a strategy like this one would actually perform better, especially if your gradients have many stops.

\$\endgroup\$
1
  • \$\begingroup\$ Your second solution is indeed performing a little better than the first. The first solution also produced a strange result when t or the positions were negative numbers - appears to be fixed by clamping the final result between 0 and 1. Thanks for your input, much appreciated :) \$\endgroup\$ Nov 5, 2018 at 15:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .