Search code examples
xnahlslpixel-shader

Draw a circle with HLSL and some math


I'm working with 2D textures, and I want to draw a simple circle, using this function to know if a pixel is or isn't on the circle and then assigning it a color: Equation

where (a,b) is the center of the circle.

I don't mind if this is a stupid, inefficient way to achieve the effect, I just want to learn how to manipulate floats to draw that circle. Assuming that all the part in C# is correct, and that the Texture is a square, how can I correct my code?

sampler s0;   
int radius;
static const float X_c = radius/2;
static const float Y_c = radius/2;

float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0   
{   
    float4 color = tex2D(s0, coords);   
    float2 coords_sec = coords*radius*2; //To map coordinates in [0, radius*2] instead of [0,1]

    //The same equation as above, just putting to the left radius
    if ( ( (int)pow((coords_sec.x - X_c),2)
           + (int)pow((coords_sec.y - Y_c),2)
            - (int)pow(radius,2) ) == 0.0 )

    {
        color.r = 1; //I want to draw a red circle
    }

    return color;
}

technique Technique1
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();  //Dice quale versione del pixelshader compilare
    }
}

Ps. It seems that I don't draw nothing. I suspect that the if condition is Always false

Second question: is there a simple guide, blog or book to help me understand these concepts (about 2D)?


Solution

  • Firstly, the cast to int is counter productive. You have float values and don't want to loose precision by casting to int, which will always be one of {0, 1 ... 4 * radius^2}.

    Furthermore, the comparison to == 0 needs some luck to be hit. If you want to fill the circle, use <= 0 or if you want to draw a line try something like abs(...) <= epsilon where epsilon is some small number that describes the line's thickness.

    Be aware that you just set the r-component of the returned color of 1. This does not need to result in a red circle (e.g. if you already have a white texture).

    I would do something like the following:

    float4 PixelShaderFunction(float2 coords: TEXCOORD0) : COLOR0   
    {
        float dx = coords.x - 0.5f;
        float dy = coords.y - 0.5f;
        if(dx * dx + dy * dy <= 0.25f)
            return float4(1.0f, 0.0f, 0.0f, 1.0f);
        else
            return float4(0.0f, 0.0f, 0.0f, 0.0f);
    }
    

    Notice that you don't need an explicit radius or center because the texture coordinates are normalized.