Закругленные углы в XNA?

10

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

Матиас Ликкегор Лоренцен
источник
В основном: используйте одну текстуру для линии, одну текстуру для круглых концов линии. Вращайте и масштабируйте спрайты, которые используют эти две текстуры соответствующим образом.
Ольховский

Ответы:

8

Вы можете визуализировать свой примитив и создать шейдер, который может создавать эти закругленные углы.
Вот простой пиксельный шейдер в псевдокоде, который может нарисовать округленный квадрат:

xDist = abs(x-0.5)
yDist = abs(y-0.5)
xD = xDist*xDist*4
yD = yDist*yDist*4
alpha = floor((1-xD)*(1-yD)*5)

Результат этого пиксельного шейдера:

введите описание изображения здесь

Если вы используете шейдеры, вы можете сделать действительно модный пользовательский интерфейс, даже анимированный.

Для меня здорово прототипировать простые пиксельные шейдеры - это программа EvalDraw

Piotrek
источник
5

Другой способ сделать это - использовать «растягивание кнопки» (также называемое «растяжка коробки» или «девять патчей»). По сути, вы создаете изображение, которое состоит из 9 частей:

Ресурс кнопки

Чтобы нарисовать эту кнопку любого размера, вы рисуете каждую фигуру (сверху вниз, слева направо):

  1. Нарисуйте немасштабированный в левом верхнем углу прямоугольника назначения.
  2. Нарисуйте по горизонтали (с width - ((1) + (2)).Width) в верхней части целевого прямоугольника с левым смещением на ширину (1).
  3. Нарисуйте немасштабированный в правом верхнем углу прямоугольника назначения.
  4. Рисуем в вертикальном (с height - ((1) + (2)).Height) виде слева от целевого прямоугольника с верхним смещением на высоту (1).
  5. Рисование масштабируется в обоих направлениях (с шириной (2) и высотой (4)), смещенной на ширину и высоту (1).
  6. Нарисуйте масштабированный вертикально (той же высоты, что и (4)) справа от целевого прямоугольника, смещенный на высоту (1).
  7. Нарисуйте немасштабированный в левом нижнем углу прямоугольника назначения.
  8. Нарисуйте масштабированное по горизонтали (на той же высоте, что и (2)) в нижней части целевого прямоугольника, смещенное на ширину (1).
  9. Нарисуйте немасштабированный в правом нижнем углу прямоугольника назначения.

Если вы посмотрите на кнопку, то увидите, что не имеет значения, если (2), (5) и (7) масштабировать по горизонтали (потому что это по существу прямая линия); таким же образом (4), (5) и (6) можно масштабировать по вертикали, не влияя на качество изображения.

Джонатан Дикинсон
источник
Да, это не дает прямого ответа на вопрос - но кто-то может все еще найти это полезным.
Джонатан Дикинсон
4
Он называется «девять патчей» и на самом деле он гораздо более гибкий (и более дешевый!), Чем использование шейдера, поскольку вы можете заставить его выглядеть так, как вы хотите, с помощью текстуры. Затем вы можете поместить все ваши изображения виджетов в один атлас текстуры. Большинство графических интерфейсов делают это таким образом.
Елисей
0

Вот код для подхода «девять патчей»:

public static class SpriteBatchExtensions
{
    public static void DrawRoundedRect(this SpriteBatch spriteBatch, Rectangle destinationRectangle, 
        Texture2D texture, int border, Color color)
    {
        // Top left
        spriteBatch.Draw(
            texture, 
            new Rectangle(destinationRectangle.Location, new Point(border)), 
            new Rectangle(0, 0, border, border), 
            color);

        // Top
        spriteBatch.Draw(
            texture, 
            new Rectangle(destinationRectangle.Location + new Point(border, 0), 
                new Point(destinationRectangle.Width - border * 2, border)), 
            new Rectangle(border, 0, texture.Width - border * 2, border), 
            color);

        // Top right
        spriteBatch.Draw(
            texture, 
            new Rectangle(destinationRectangle.Location + new Point(destinationRectangle.Width - border, 0), new Point(border)), 
            new Rectangle(texture.Width - border, 0, border, border), 
            color);

        // Middle left
        spriteBatch.Draw(
            texture, 
            new Rectangle(destinationRectangle.Location + new Point(0, border), new Point(border, destinationRectangle.Height - border * 2)), 
            new Rectangle(0, border, border, texture.Height - border * 2), 
            color);

        // Middle
        spriteBatch.Draw(
            texture, 
            new Rectangle(destinationRectangle.Location + new Point(border), destinationRectangle.Size - new Point(border * 2)), 
            new Rectangle(border, border, texture.Width - border * 2, texture.Height - border * 2), 
            color);

        // Middle right
        spriteBatch.Draw(
            texture, 
            new Rectangle(destinationRectangle.Location + new Point(destinationRectangle.Width - border, border), 
                new Point(border, destinationRectangle.Height - border * 2)), 
            new Rectangle(texture.Width - border, border, border, texture.Height - border * 2), 
            color);

        // Bottom left
        spriteBatch.Draw(
            texture, 
            new Rectangle(destinationRectangle.Location + new Point(0, destinationRectangle.Height - border), new Point(border)), 
            new Rectangle(0, texture.Height - border, border, border), 
            color);

        // Bottom
        spriteBatch.Draw(
            texture, 
            new Rectangle(destinationRectangle.Location + new Point(border, destinationRectangle.Height - border), 
                new Point(destinationRectangle.Width - border * 2, border)), 
            new Rectangle(border, texture.Height - border, texture.Width - border * 2, border), 
            color);

        // Bottom right
        spriteBatch.Draw(
            texture, 
            new Rectangle(destinationRectangle.Location + destinationRectangle.Size - new Point(border), new Point(border)), 
            new Rectangle(texture.Width - border, texture.Height - border, border, border), 
            color);
    }
}

Он вызывается как:

spriteBatch.DrawRoundedRect(
    dest, // The coordinates of the Rectangle to be drawn
    rectangleTexture, // Texture for the whole rounded rectangle
    16, // Distance from the edges of the texture to the "middle" patch
    Color.OrangeRed);
sdgfsdh
источник
это не работает для меня ... Я просто получаю нормальный прямоугольник. Вот как я это использовалTexture2D _texture = new Texture2D(GraphicsDevice, 1, 1); _texture.SetData(new Color[] { Color.Blue }); SpriteBatch sb = new SpriteBatch(GraphicsDevice); sb.Begin(); //sb.Draw(_texture, new Rectangle(100, 100, 100, 100), Color.White); sb.DrawRoundedRect(_texture, new Rectangle(100, 100, 100, 100), Color.Pink, 16); sb.End();
Леонардо Секкиа
Ваша текстура - только один синий пиксель, этот подход предполагает, что закругленные углы являются частью вашей текстуры.
sdgfsdh
извините - очень
плохо знакомы