|
Проблема
определения RGB компонент
в 16-битном режиме (OpenGL, glReadPixels)
|
Георгий Мошкин
tmtlib@narod.ru
Проблема правильного определения RGB компонент
в 16-битном режиме. Необходимо точно определить
значение цвета, который выведен
Условия:
glShadeModel(GL_FLAT);
glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
В 32bpp режиме всё работает нормально.
какой RGB цвет выведен на экран, такой и считывается:
пишем 0,100,0 считываем 0,100,0
пишем 5,4,5 считываем 5,4,5
пишем 230,240,250 считываем 230,240,250
А вот в 16-битном режиме эти значения искажаются:
0,100,0 превращается в 0,101,0
5,4,5 превращается в 8,4,8
230,240,250 превращается в 239, 243, 255
и т.д..
Вывожу треугольник цвета R,G,B (0,100,0)
glColor3ub(0,100,0);
glBegin (GL_TRIANGLES);
glVertex2f (0, 0);
glVertex2f (1, 0);
glVertex2f (1, 1);
glEnd;
Считываю цвет
pix : Array [0..2] of GLUbyte;
glPixelStorei(GL_UNPACK_ALIGNMENT,
1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels (X, Y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, @pix);
И вывожу сообщение с компонентами цвета
ShowMessage(IntToStr(pix[0])+', '+IntToStr(pix[1])+', '+IntToStr(pix[2]));
Цвет оказывается неправильным.
Встаёт вопрос как это исправить? Решение
следующее:
Вопрос разрешился следующим образом:
r,g,b:GLInt;
glGetIntegerv (GL_RED_BITS, @r);
glGetIntegerv (GL_GREEN_BITS, @g);
glGetIntegerv (GL_BLUE_BITS, @b);
Таким образом мы получаем текущий цветовой
режим (сколько битов на цвет)
565, 888 и т.п.
565 <-> r=5 g=6 b=5
на основе этих данных (количество бит r,g,b)
генерирую другие цвета с использованием
функций shr shl.
Например, для пяти бит синего цвета
for i:=0 to 31 do
ShowMessage(IntToStr( i shl 3 ));
(так как 11111 = 31)
Короче говоря, на вход glColor3ub нужно подавать
компоненты, преобразованные из r,g,b бит в 8 бит. Для режима 565:
для r: 0-31 shl 3
для g: 0-63 shl 2
для b: 0-31 shl 3
Прочитанные данные из glReadPixels нужно
перевести обратно в 5,6 и 5 бит соответственно компонентам pix[0], pix[1]
и pix[2].
Мне же не нужны реальные цвета на экране.
Я хочу по записанному цвету определить номер объекта. То есть что записал,
то и считал. Это вспомогательные цвета, которые человек никогда не увидит
(сначала пишется в задний буфер "цветная" картинка, потом затирается
реальной картинкой).
Например, я вывел 100 треугольников разных цветов. То есть у каждого треугольника
свой цвет. Нажимаю мышкой на экране в координате (x,y), потом делаю glreadpixels
в этой точке и по цвету узнаю какой это был треугольник.
Для Radiosity это можно тоже использовать: номер патча(n) записывать в
красный цвет (R=n), текстурные координаты(u,v) - в синий и зелёный (G=i,
B=v). Хотя, если полигонов больше 256, то можно взять часть бит из другого
цвета.
Может кому пригодится:
1) Определяем количество бит, используемых под каждый цвет с помощью glGetIntegerv
(смотри выше). Количество бит записываем R,G,B
[code]
r,g,b:GLInt;
glGetIntegerv (GL_RED_BITS, @r);
glGetIntegerv (GL_GREEN_BITS, @g);
glGetIntegerv (GL_BLUE_BITS, @b);
[/code]
2) Определяем количество оттенков, которые
можно обеспечить этими битами. Например, для 16-битного режима 5,6,5 (R=5,
G=6, B=5):
Красный цвет (R) имеет 2^5 = 32 оттенка (rn=32)
Зелёный цвет (G) имеет 2^6 = 64 оттенка (rn=64)
Синий цвет (B) имеет 2^5 = 32 оттенка (rn=32)
[code]
uses Math;
rn:=round(power(2,r));
gn:=round(power(2,g));
bn:=round(power(2,b));
[/code]
3) При выводе цвета используем только оттенки
от rn,gn,bn (0..rn-1,0..gn-1,0..bn-1).
Например, вывод всех доступных оттенков цветов:
[code]
for i:=0 to rn-1 do
for j:=0 to gn-1 do
for k:=0 to bn-1 do
begin
glColor3ub(i shl (8-r), j shl (8-g), k shl (8-b)); // SHL-ом
смещаем биты влево
glBegin (GL_POINTS);
glVertex2f(xcoord(i,j,k),ycoord(i,j,k));
glEnd;
end;
[/code]
В строчке с glColor3ub мы преобразуем r,g,b
бит в 8,8,8 бит:
[code]
i shl (8-r), j shl
(8-g), k shl (8-b)
[/code]
Например, самый яркий цвет режима "r,g,b
5,6,5" будет 31,63,31.
В битах это будет:
31=11111
63=111111
31=11111
После shl на 8-5=3, 8-6=2, 8-5=3:
11111000
11111100
11111000
4) При чтении цвета достаточно сместить оттенок
обратно вправо SHR-ом:
[code]
glReadPixels (X, Y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, @pix);
ShowMessage(IntToStr(pix[0] shr (8-r))+',
'
+IntToStr(pix[1] shr (8-g))+', '
+IntToStr(pix[2] shr (8-b)));
[/code]
Считанный
11111000 -> 11111
11111100 -> 111111
11111000 -> 11111
Итак, лишние биты справа просто не используются
=)
Если режим 8,8,8, то оттенки будут меняться от 0 до 255 (0..255,0..255,0.255)
и
функции shl/shr не понадобятся. Так и будет, потому что 8-8=0 и смещения
будут
нулевые : shl 0 / shr 0. В 16-битных режимах мы узнаём количество бит
на цвет, а
затем и количество оттенков. И действуем по следующей схеме:
номер оттенка N бит -> преобразуем в 8
бит -> выводим на экран
считываем с экрана 8 бит -> преобразуем в N бит -> получили то,
что и записали
По сути дела мы же и записали 5 бит, просто
сместили их влево. А биты справа нас не интересует. На экран всё равно
выведен 8-битный цвет, у которого есть 5 бит, а остальные 3 бита нас не
волнуют. После считывания мы берём только 5 бит, которые записали. Для
этого смещаем SHR-ом вправо, чтобы не было проблем. В итоге из 8 бит остаётсё
только 5 (для режима 5,6,5).
|
|