Question

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)?

Was it helpful?

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.

OTHER TIPS

Here is a very simple way (but probably not fast); yet it works. I do not claim it is the best solution, but it works well and it is very flexible and easy that allows you to use GDI+.

First: Add the reference "System.Drawing" from the project menu.

Second: Create this class in a new file.

using Microsoft.Xna.Framework.Graphics;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace WindowsGame1 // This should be the namespace of your application.
{
    class CircleDrawer
    {
        public static Texture2D GetCircle(GraphicsDevice device, System.Drawing.Color    color, int radius)
        {
            Image img = new Bitmap(radius, radius);
            Graphics g = Graphics.FromImage(img);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.FillEllipse(new SolidBrush(color), 0, 0, radius, radius);
            img.Save("temp.png", System.Drawing.Imaging.ImageFormat.Png);
            Texture2D result = Texture2D.FromFile(device, "temp.png");
            System.IO.File.Delete("temp.png");
            return result;
        }
    }
}

Third: That's all now you have a class with a method that is ready to draw a circle

Let's see an example how to apply it:

Just write these four lines in the "Draw" method of the game class

        Texture2D txt = CircleDrawer.GetCircle(GraphicsDevice, 
            System.Drawing.Color.Green, 40);
        spriteBatch.Begin();
        spriteBatch.Draw(txt, new Rectangle(10, 10, 60, 60), Color.White);
        spriteBatch.End();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top