В этой статье мы рассмотрим алгоритм и пример программы, растеризующей треугольники с интерполяцией атрибутов вершин на основе барицентрических координат. Звучит громоздко? Не пугайтесь, все не настолько сложно.
В результате разработки, мы получим программу, которая будет рисовать на экране вот такое изображение:
Итак, обо всем по порядку.
Допустим у нас имеется шесть треугольников. Даны их координаты и заданы атрибуты их вершин (цвета в вершинах). Необходимо растеризовать с интерполяцией атрибутов вершин (или проще — раскрасить, зная цвета вершин) эти треугольники. Получим своеобразный градиент.
Примечание. Интерполяция — это получение промежуточных значений величины, на основе набора известных значений.
Допустим, у нас имеется pictureBox размером 800 x 600 пикселей. За начало координат примем левый верхний угол.
В цикле for будем перемещаться по координатной плоскости (по пикселям) построчно — слева направо. Наша задача состоит в том, что бы определить, принадлежат ли координаты текущего пикселя какому-либо из треугольников. Для решения этой задачи нам потребуется использовать барицентрические координаты. Вот их формулы:
где x1, y1, x2, y2, x3, y3 — координаты вершин треугольника, а x, y — координаты текущей точки. Текущая точка принадлежит треугольнику, если выполняется условие:
(то есть все три барицентрические координаты должны принадлежать отрезку [0, 1])
Подробнее прочитать про барицентрические координаты можно здесь и здесь.
В итоге имеем такой алгоритм растеризации треугольника:
1) Значению цвета текущей точки присваивается цвет фона.
2) Вычисляются барицентрические координаты текущей точки относительно вершин текущего треугольника.
3) Выполняется проверка условия принадлежности точки треугольнику.
4) Если условие верно, то точка принадлежит треугольнику, и значению ее цвета присваивается цвет, полученный при интерполяции атрибутов вершин этого треугольника. Иначе: переходим к следующему треугольнику (если такой [еще] имеется) и пункту 2.
5) Вывод текущей точки, переход к следующей и пункту 1.
Итак, создадим в Visual Studio проект Windows Forms (С#). Поместим в форму элемент управления pictureBox и зададим в свойствах его размеры 800 x 600:
Теперь создадим в этом проекте файл (назовем его, например, data.cs). Там у нас будет находится такой код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using System; namespace triangle { class data { public static int n = 6; public static double[] x1 = { 100, 250, 400, 100, 250, 400 }; public static double[] y1 = { 250, 100, 250, 350, 500, 350 }; public static double[] x2 = { 250, 400, 550, 250, 400, 550 }; public static double[] y2 = { 100, 250, 100, 500, 350, 500 }; public static double[] x3 = { 400, 550, 700, 400, 550, 700 }; public static double[] y3 = { 250, 100, 250, 350, 500, 350 }; public static UInt32[] colorA = { 0xFFcc1343, 0xFF7a50c0, 0xFF58d70c, 0xFFf6310a, 0xFFFF0000, 0xFFfcff00 }; public static UInt32[] colorB = { 0xFFeae817, 0xFFbe1fb8, 0xFF0fe6ee, 0xFFfff600, 0xFF00FF00, 0xFF2cf3f1 }; public static UInt32[] colorC = { 0xFF92eeaf, 0xFFea8208, 0xFFf91dec, 0xFFb482f8, 0xFF0000FF, 0xFF058726 }; } } |
В строке номер 7 указано количество треугольников (в нашем случае шесть треугольников). В строках 9-16 задаем координаты вершин треугольников (в первом столбике координаты первого треугольника, во втором — второго, и т.д.). В строках 18-20 указаны атрибуты вершин треугольников — их цвета (также по столбикам).
Далее. Переходим к коду класса Form1.cs. Используя директиву using, подключаем пространство имен из файла data.cs:
1 |
using triangle; |
Объявляем экземпляр класса Bitmap (необходим для хранения изображения):
1 |
public static Bitmap image; |
В конструкторе класса Form1.cs после InitializeComponent() добавляем такой код:
1 2 3 4 5 6 7 8 |
public Form1() { InitializeComponent(); image = new Bitmap(pictureBox1.Width, pictureBox1.Height); Rasterization(); pictureBox1.Image = image; } |
Здесь мы инициализируем image размерами pictureBox1, вызываем функцию Rasterization() (о ней далее) и переносим изображение из Bitmap image в pictureBox1.
Теперь добавим функцию Rasterization(), которая пройдется по всем пикселям на плоскости и занесет вычисленный цвет текущей точки в Bitmap image:
1 2 3 4 5 6 |
public void Rasterization() { for (int y = 0; y < pictureBox1.Height; y++) for (int x = 0; x < pictureBox1.Width; x++) image.SetPixel(x, y, Color.FromArgb((int)ShadeBackgroundPixel(x, y))); } |
ShadeBackgroundPixel(x, y) — вычисляет цвет пикселя с координатами (x, y).
Color.FromArgb() — переводит цвет из ARGB в класс Color, необходимый для работы функции image.SetPixel().
Переходим к функции ShadeBackgroundPixel(x, y), написанной на основе алгоритма, рассмотренного выше:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public UInt32 ShadeBackgroundPixel(int x, int y) { UInt32 pixelValue; //цвет пикселя с координатами (x, y) double l1, l2, l3; int i; pixelValue = 0xFFFFFFFF; //присваиваем цвет фона - белый for (i = 0; i < data.n; i++) { l1 = ((data.y2[i] - data.y3[i]) * ((double)(x) - data.x3[i]) + (data.x3[i] - data.x2[i]) * ((double)(y) - data.y3[i])) / ((data.y2[i] - data.y3[i]) * (data.x1[i] - data.x3[i]) + (data.x3[i] - data.x2[i]) * (data.y1[i] - data.y3[i])); l2 = ((data.y3[i] - data.y1[i]) * ((double)(x) - data.x3[i]) + (data.x1[i] - data.x3[i]) * ((double)(y) - data.y3[i])) / ((data.y2[i] - data.y3[i]) * (data.x1[i] - data.x3[i]) + (data.x3[i] - data.x2[i]) * (data.y1[i] - data.y3[i])); l3 = 1 - l1 - l2; if (l1 >= 0 && l1 <= 1 && l2 >= 0 && l2 <= 1 && l3 >= 0 && l3 <= 1) { pixelValue = (UInt32)0xFF000000 | ((UInt32)(l1 * ((data.colorA[i] & 0x00FF0000) >> 16) + l2 * ((data.colorB[i] & 0x00FF0000) >> 16) + l3 * ((data.colorC[i] & 0x00FF0000) >> 16)) << 16) | ((UInt32)(l1 * ((data.colorA[i] & 0x0000FF00) >> 8) + l2 * ((data.colorB[i] & 0x0000FF00) >> 8) + l3 * ((data.colorC[i] & 0x0000FF00) >> 8)) << 8) | (UInt32)(l1 * (data.colorA[i] & 0x000000FF) + l2 * (data.colorB[i] & 0x000000FF) + l3 * (data.colorC[i] & 0x000000FF)); break; } } return pixelValue; } |
Здесь мы работаем с типом данных UInt32 — это беззнаковое 32-х разрядное целое число.
Интерполяцию (строка 17), то есть вычисление цвета точки в треугольнике на основе цветов его вершин, проводим так: последовательно каждую из компонент цвета R, G и B вершины A умножаем на барицентрическую координату l1, компоненты вершины B на l2, C — на l3.
На этом все. Исходник программы можно скачать, нажав на кнопку внизу страницы. А наша программа работает вот так:
Скачать исходник
Поделиться в соц. сетях: