PDA

Просмотр полной версии : закраска треугольника (shading), с интерполяцией


heilong
17.05.2006, 11:14
Сейчас изучаю возможности Flash 8 по текстурированию и закраске треугольников. В перспективе - идея сделать несложный 3d engine с текстурами и нормальным (не flat, а Gouraud) светом (что отсутствует в большинстве даже продвинутых flash 3d engines).

Кому лениво читать, можно пропустить все кроме выделенного жирным.

С текстурированием всё довольно просто, с помощью MovieClip.attachBitmap и MovieClip.transform.matrix а также mask по форме нужного треугольника все неплохо работает, и даже скорость вполне сносная.

С освещением сложнее, быстрого и некривого решения пока не нашёл. Итак, сразу сформулирую вопрос:

Имеется треугольник с вершинами A (0, 0); B (256, 0); C (0, 256).
Для каждой вершины задан свой цвет, например A - красный, B - зелёный, C - синий.
Нужно проинтерполировать цвет по всей площади треугольника.
То есть закрасить его так чтобы в каждой его точке цвет определялся в зависимости от расстояния от каждой вершины. Другими словами, нужен градиент не linear, а с тремя вершинами. При этом важно чтобы это работало быстро. Какие есть идеи?

Теперь варианты как это делал я:
1) Делается BitmapData(256, 256), присоединяется к нужному клипу.
for (y = 0; y < 256; ++y) {
// линейная интерполяция по катету AC, для r, g, b
цвет1_r = цвет_в_А_r * (256 - y) / 256 + цвет_в_C_r * y / 256;
...
такая же интерполяция по гипотенузе BC...
цвет2_r = ...
for (x = 0; x < 256; ++x) {
// интерполяция между цвет1 и цвет 2, для r, g, b
цвет_r = цвет_1_r * ( 256 - x) / 256 + цвет_2_r * x /256;
..
setPixel(x, y, (цвет_r << 16) + (цвет << 8) + цвет_b);
}
}

Типичный растровый вариант, показывающий что надо сделать. В компилируемом языке пишется на асме (вместе с текстурой), всячески оптимизируется, на Flash-е же безбожно тормозит. По качеству получается ровно то что нужно.

2) Делается BitmapData(2, 2).
setPixel(0, 0, цветА)
setPixel(1, 0, цветB)
setPixel(0, 1, цветC)
цветD = ... // посчитать средний по компонентам r,g,b цвет между цветB и цветC
setPixel(1, 1, цветD)
Дальше BitmapData присоединяется к клипу с опцией smoothing = true и
клип увеличивается (с помощью MovieClip.transform.matrix) до 256х256.
В итоге flash сглаживает картинку и получается приблизительно то что нужно.
Правда по качесту далеко не идеально потому что вокруг середины гипотенузы
получается пятно от пиксела (1, 1). Хорошо заметно, если цветB=цветC=зелёный
(например). По скорости работает очень неплохо.


Качество можно несколько улучшить если соединить способы 1) и 2) и делать битмап не 2х2 а, например, 4х4. При этом к сожаления все равно не идеальное качество, а при увеличении размера скорость падает довольно ощутимо.
Ещё думал, как бы это реализовать с помощью градиента(нескольких градиентов), но так и не придумал.

Можно поиграться с моим кодом: http://heilong.oceanography.ru/flash/lab/
Для наглядности в нём можно закомментировать текстурирование (строка 22), преобразования на плоскости (строка 111) и произвольные цвета вершин (строки 51-59), тогда как раз будет заметно неидеальное качество. Чтобы было лучше видно, можно поставить посильнее увеличение (строка 44).

Кто осилил мои "много букв", милости прошу к обсуждению/идеям.