Search code examples
typeshlsl

Numerical condition never true?


Overview

Last week I was creating a condition based pixel shader in HLSL. The goal was to color only pixels which met a specific condition. In my case the condition was based on "time", which I say loosely because it's not what we consider traditional time. I was rendering a line in 3D space which is a trivial task. However, only a certain percentage of the line should be displayed based on the current user selected time.

I created a Line class and added a SliderControl to my form to allow the user to control the current point in time. This was simple enough to accomplish, as was setting up all of the underlying code.


The Issue

While I was creating the underlying code, I made a simple mistake with my time types. In the constant buffer for the current time, I used double; within my Vertex structures (arbitrary definition and input, along with pixel shader input), I used float. This caused the condition in my pixel shader to always result in false. The reason for the difference was that the original data is double in type, but I ended up choosing float to match everything else in the code, plus there was some kind of issue with using double types in HLSL.


The HLSL

The code was very straightforward:

cbuffer TimeBuffer : register(b4) {
    double CurrentTime;
}
struct VertexInput {
    float Time;
    //...
}
struct PixelInput {
    float Time;
    //...
}
float4 PSMain(PixelInput input) : SV_Target {
    float4 result = 0;
    if (input.Time < CurrentTime)
        result = input.Diffuse;

    return result;
}

The Question

Why is the line never being rendered?


Solution

  • The issue here is due to the type difference between the cbuffer and the struct for inputs. Simply make the types match:

    cbuffer TimeBuffer : register(b4) {
        float CurrentTime;
    }
    struct VertexInput {
        float Time;
        //...
    }
    struct PixelInput {
        float Time;
        //...
    }
    float4 PSMain(PixelInput input) : SV_Target {
        float4 result = 0;
        if (input.Time < CurrentTime)
            result = input.Diffuse;
    
        return result;
    }
    

    Simply changing the initial assignment of result to float4(0, 1, 0, 0) or some other solid color will show that the line is indeed being rendered, but the condition is never evaluating as true.


    A good thorough answer to why this is such an issue can be found here. I did not originally understand that this was an issue, because I assumed that double and float could be compared just like any other numeric type. I was wrong however, and as the post linked above stated:

    The important factors under consideration with float or double numbers are:

    Precision: The precision of a floating point number is how many digits it can represent without losing any information it contains.

    Rounding: There is a non-obvious differences between binary and decimal (base 10) numbers. Consider the fraction 1/10. In decimal, this can be easily represented as 0.1, and 0.1 can be thought of as an easily representable number. However, in binary, 0.1 is represented by the infinite sequence: 0.00011001100110011….


    However, this also seems to matter with int and float instead of double and float.