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

Était-ce utile?

La 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.

Autres conseils

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();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top