Прежде всего, немного теории. Нам понадобится знать то, что конечный цвет
пиксела с составляющими (r,g,b) и освещенного цветом (light_r,light_g,light_b)
считается как
result_r = r * light_r / max(light_r);
result_g = g * light_g / max(light_g);
result_b = b * light_b / max(light_b);
Здесь max(light_r) - это максимально возможное значение для light_r. Не
максимальное по всем тем градациям освещенности, что мы используем, а вообще
максимальное для всех возможных градаций. Например, если у нас значения для
light_r могут гулять от 0 до 255, а текущий источник света имеет цвет (30,
40,50), то соответственно градации освещенности будут равны (k*30,k*40,k*50),
где 0 <= k <= 1, то max(light_r) = 255, а не 30. Немного путаное объяснение,
но вообще это известная и понятная почти всем вещь.
5.6.1. 256-цветные режимы
Метод 1: заранее посчитать таблицу, переводящую пару (цвет, освещенность) в
цвет. Можно не менять палитру, а искать для каждой пары (цвет, освещенность)
наилучшим образом приближающий ее цвет; а можно (как описано в demo.design
FAQ) взять палитру и сгенерировать из нее true color картинку 256x256, в
которой пиксел (x, y) нарисован цветом (уже true color!), соответствующим
цвету x и освещенности y, а потом перевести ее чем-нибудь типа Image Alchemy
в 256 цветов. После чего использовать палитру получившейся картинки, а в
качестве таблицы (colorTable) будет сама получившаяся картинка:
outputColor = colorTable[intensity][color].
Метод 2: если нас устроит использовать немного цветов и градаций освещения,
то тогда в палитру можно впихнуть все возможные градации всех используемых
цветов. Тогда определение нужного индекса в палитре - это одно умножение,
а лучше - сдвиг, и одно сложение. Пример: пусть у нас есть 8 цветов и 32
градации освещенности. Палитру заполняем так: 32 градации первого цвета,
второго, ..., восьмого. Тогда (для этого примера)
outputColor = (color << 5) + intensity.
5.6.2. 24/32-битные режимы
Здесь все делается теми же самыми таблицами. Только таблица переводит не
цвет в цвет, а компоненту цвета в компоненту цвета. То есть, создаем
таблицы redTable[numShades], greenTable[numShades], blueTable[numShades],
а потом для каждой компоненты каждого пиксела и нужной градации освещенности
по этой таблице определяем выходное значение компоненты:
r = redTable[intensity],
g = greenTable[intensity],
b = blueTable[intensity].
Каждая компонента в этих режимах - это отдельный байт, поэтому никаких
проблем не возникает.
5.6.3. 15/16-битные режимы
Метод 1: тупой, но действенный. Использовать большую таблицу и занести в нее
все возможные комбинации цвета и градации освещения. Таблица получится совсем
не маленькая, размером 65536*32 = 2 мегабайта. Я написал здесь 32, потому как
в этих режимах на компоненту отводится по 5 бит (за исключением 6-битной
зеленой компоненты в 16-битном режим), и делать больше градаций освещенности,
чем 32, бессмысленно.
Метод 2: делать все так же, как в 24/32-битных режимах. Проблемы возникнут
из-за того, что придется с муками выдирать нужные несколько бит компоненты
из пиксела. Таблицы для компонент лучше заранее сделать со всеми нужными
сдвигами, т.е. значения элементов таблиц должны быть такого вида:
000bbbbb - синий, 8 бит
00000gggggg00000 - зеленый, 16 бит
rrrrr000 - красный, 8 бит
Тогда конечный цвет считается примерно так:
outputColor =
(redTable[(color >> 10) & 0x2F] << 8) +
greenTable[(color >> 5) & 0x1F] +
blueTable[color & 0x1F].
На ассемблере это делается, видимо, побыстрее - и покрасивее. Примерно так:
; ...
mov bx,color
shr bx,10
and bx,02Fh
mov ah,redTable[bx]
mov bx,color
and bx,01Fh
mov al,blueTable[bx]
mov bx,color
shr bx,5 ; можно заменить на
and bx,01Fh ; shr bx,4
shl bx,1 ; and bx,02Eh
or ax,greenTable[bx]
mov outputColor,ax
; ...
Метод 3: рисовать все в 24/32-бита, освещение соответсвенно с текстурой
совмещать по пункту 5.6.2, а потом непосредственно при выводе на экран делать
преобразование из 24/32-бит в 15/16. Или использовать PTC и предоставить
делать нужное преобразование именно ему. PTC - это такая графическая система
для C++, взять ее можно на http://www.gaffer.org/ptc.