Нарисуйте круг с помощью HLSL и немного математики

Я работаю с 2D-текстурами и хочу нарисовать простой круг, используя эту функцию, чтобы узнать, находится ли пиксель в круге или нет, а затем назначить ему цвет: Уравнение

где (a,b) — центр окружности.

Я не возражаю, если это глупый и неэффективный способ достижения эффекта, я просто хочу научиться манипулировать плавающими элементами, чтобы рисовать этот круг. Предполагая, что вся часть в C # верна, а текстура представляет собой квадрат, как я могу исправить свой код?

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
    }
}

Пс. Кажется, я ничего не рисую. Я подозреваю, что условие if всегда ложно

Второй вопрос: есть ли простое руководство, блог или книга, которые помогут мне понять эти концепции (о 2D)?


person Francesco Bonizzi    schedule 26.11.2013    source источник


Ответы (2)


Во-первых, приведение к int контрпродуктивно. У вас есть значения float, и вы не хотите терять точность при приведении к int, которое всегда будет одним из {0, 1 ... 4 * radius^2}.

Кроме того, для сравнения с == 0 нужна удача. Если вы хотите заполнить круг, используйте <= 0 или, если вы хотите нарисовать линию, попробуйте что-то вроде abs(...) <= epsilon, где эпсилон — это небольшое число, описывающее толщину линии.

Имейте в виду, что вы только что установили r-компонент возвращаемого цвета равным 1. Это не обязательно должно приводить к красному кругу (например, если у вас уже есть белая текстура).

Я бы сделал что-то вроде следующего:

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);
}

Обратите внимание, что вам не нужен явный радиус или центр, потому что координаты текстуры нормализованы.

person Nico Schertler    schedule 27.11.2013
comment
Спасибо! Еще один вопрос: если я хочу нарисовать закрашенный круг с градиентом от черного (в центре) до белого, как я могу изменить возвращаемое значение? Я думал о том, чтобы сделать return4(1.0f - dxdx + dydy , то же самое для g и b, 1.0f), но на самом деле это не градиент... - person Francesco Bonizzi; 27.11.2013
comment
Попробуйте 0.5f - sqrt(dx*dx+dy*dy) или 0.25f - dx * dx - dy * dy. - person Nico Schertler; 27.11.2013

Вот очень простой способ (но, наверное, не быстрый); но это работает. Я не утверждаю, что это лучшее решение, но оно работает хорошо, очень гибкое и простое, что позволяет использовать GDI+.

Во-первых: добавьте ссылку «System.Drawing» из меню проекта.

Второе: создайте этот класс в новом файле.

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;
        }
    }
}

Третье: Вот и все, теперь у вас есть класс с методом, который готов рисовать круг.

Давайте посмотрим на примере, как это применить:

Просто напишите эти четыре строчки в методе Draw игрового класса.

        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();
person user3621770    schedule 09.05.2014
comment
Это не дает ответа на вопрос, как визуализировать круг с помощью HLSL. - person DannyMeister; 27.01.2015