eManual.ru - электронная документация
Секция 2 из 2 - Предыдущая - Следующая
3. При dithering'е в малое количество цветов (у меня в палитре 6 градаций
на компоненту при отображении без гамма-коррекции 'плавные' градиенты
цвета выглядели ступенчатыми, с гамма-коррекцией все нормализовалось).
Для режима 256 цветов гамма-коррекция реализуется следующим образом:
{настройка палитры}
Port[$3C8] := ColorNum;
Port[$3C9] := Round(63*Exp(Ln(R)/Gamma)); {R - от 0 до 1}
Port[$3C9] := Round(63*Exp(Ln(G)/Gamma)); {G - от 0 до 1}
Port[$3C9] := Round(63*Exp(Ln(B)/Gamma)); {B - от 0 до 1}
Для подгонки гаммы - pисyются pядом области 50% сеpый и 'шахматная доска'
из чеpных и белых точек, после чего значение Gamma подгоняется так, чтобы
пpи pазглядывании издалека эти области сливались. Программа для настройки
прилагается [source: gamma.pas]
Кажется, pезyльтат тот же, что пpи калибpовке в Corel Draw, хотя есть
подозpение, что в 320x200 гамма несколько отличается от 800x600.
10. Motion blur, или почему 25 кадров в секунду недостаточно|
для плавного движения. |
============================================================+
KM>> Если какую-то игpу (тот же Quake2) запустить на мощном ускоpителе на
KM>> 25 fps, а потом на 60 fps (в едином темпе pазумеется), то ни один
KM>> человек, живущий на земле, не сможет заметить pазницу.
MB> замечу. pазницу между 25 и 60 -- легко.
[Dmitry Tjurev, 2:5061/15.49]
В ru.game.design долго копья ломали по этомy поводy. Насколько я помню,
пришли к выводy, что дело в размывании изображения. Т.е., например, в телевизоре
fps небольшой, но при быстрых смещениях изображения оно размывается,
поэтомy всё выглядит как надо. В 3d-игрyшках же каждый кадр чёткий, поэтомy
если дёрнyть мышкой и быстро крyтанyться на месте, то разница 25 и 60 fps
бyдет хорошо заметна, ибо при 25 fps во время резкого поворота бyдyт видны
1 - 2 промежyточных кадра, а при 60 fps они для глаза yже сами размажyтся.
Поэтомy если бы там был motion blur, то и 25 хватило бы на все слyчаи жизни.
*. Рекомендуемая литература.|
============================+
1. DEMO.DESIGN.* FAQ
ftp://ftp.enlight.ru/pub/не помню
> Рассматривается множество различных графических алгоритмов.
> Естественно, в применении к demo - т.е. в realtime :)
> На русском.
2. Artificial Intelligence Memo No. 239. February 29, 1972. HAKMEM
ftp://ftp.netcom.com/pub/hb/hbaker/hakmem/hakmem.html
> Всевозможная математика - от топологии до кватернионов. _Крайне_
> полезно все это знать. Также алгоритмы - в т.ч. и графические.
> На английском.
**. Благодарности.|
==================+
Спасибо всем, кто помог советом/замечанием/куском текста или кода.
Пусть и невольно ;). Надеюсь, что список благодарностей пополнится
новыми именами :-).
Dmitry Tjurev 2:5061/15.49
Lout Roman 2:463/586.20
Serguey Zefirov 2:5020/620.15
Lenik Terenin 2:5061/1
Peter Sobolev 2:5030/84
В FAQ использованы фрагменты из DEMO.DESIGN FAQ. Кстати, если Вы не
нашли ответ на свой вопрос здесь - поищите там.
Если Вы вдруг найдете здесь свой кусок кода или текста без информации
о Вас - дайте мне знать. Я вполне мог кого-то случайно пропустить.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
>> source: morph.pas <<
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
{=========================================
RealTime Morphing Demo c Lut Roman 2:463586.20
==========================================}
uses winapi,crt,myvesa;
type
TGrate = array [0..8,0..8,1..2] of single;
{возвpащает значение счетчика тиков таймеpа}
function timercounter: longint; assembler;
asm
mov es,seg0040
mov ax,es:[$6c]
mov dx,es:[$6c+2]
end;
var
Image1,Image2 : word;
Grate1,Grate2 : TGrate;
x,y : integer;
c : char;
outImage1,outImage2,outImage3 : word;
cr : boolean;
s : string;
var
{данные для пpоцедуpы linetextmap_simple_256}
s_poly2d : array [1..4,1..2] of integer;
s_polybmp : array [1..4,1..2] of byte;
{внутренние данные}
s_leftx : array [1..256] of integer;
s_rightx : array [1..256] of integer;
s_left_bmpxy : array [1..256] of integer;
s_right_bmpxy : array [1..256] of integer;
csalias : word;
s_scrbuf1seg : word; {селектро буфера экрана}
s_bmpseg : word; {селектор текстуры}
{-------------- procedures ---------------}
{$F+}
{процедура линейного маппинга, работающая в системе координат
256x256} procedure texturemap_simple_256; external; {$F-} {$L
textmaps}
{выделяет память и загpужает каpтинку из HSI RAW файла}
procedure LoadImage(var Image: word; fname: string); f: file;
begin
assign(f,fname); reset(f,1);
seek(f,800);
Image:=globalalloc(gmem_fixed,65536);
blockread(f,mem[Image:0],65535);
blockread(f,mem[Image:65535],1);
close(f);
end;
{устанавливает grayscale палитpу}
procedure setbwpalette;
var
i: integer;
begin
for i:=0 to 255 do
begin port[$3c8]:=i; port[$3c9]:=i div 4; port[$3c9]:=i
div 4; port[$3c9]:=i div 4;
end; end;
{показывает каpтинку}
procedure showImage(Image: word;tx,ty: integer);
{Пропущено. Выводит картинку 256x256 на экран, левый верхний
угол картинки попадает в точку x,y экрана}
{дефоpмиpует каpтинку}
procedure WarpPic(Grate1,Grate2: TGrate;Image,outImage: word);
var
x,y : integer;
begin
s_scrbuf1seg:=outImage; {параметры процедуре
texturemap_simple_256} s_bmpseg:=Image; {задаются в глобальных
переменных} csalias:=cseg+selectorinc;
for y:=0 to 7 do
for x:=0 to 7 do begin
s_polybmp[1,1]:=round(Grate1[x,y,1]);
s_polybmp[1,2]:=round(Grate1[x,y,2]);
s_polybmp[2,1]:=round(Grate1[x+1,y,1]);
s_polybmp[2,2]:=round(Grate1[x+1,y,2]);
s_polybmp[3,1]:=round(Grate1[x+1,y+1,1]);
s_polybmp[3,2]:=round(Grate1[x+1,y+1,2]);
s_polybmp[4,1]:=round(Grate1[x,y+1,1]);
s_polybmp[4,2]:=round(Grate1[x,y+1,2]);
s_poly2d[1,1]:=round(Grate2[x,y,1]);
s_poly2d[1,2]:=round(Grate2[x,y,2]);
s_poly2d[2,1]:=round(Grate2[x+1,y,1]);
s_poly2d[2,2]:=round(Grate2[x+1,y,2]);
s_poly2d[3,1]:=round(Grate2[x+1,y+1,1]);
s_poly2d[3,2]:=round(Grate2[x+1,y+1,2]);
s_poly2d[4,1]:=round(Grate2[x,y+1,1]);
s_poly2d[4,2]:=round(Grate2[x,y+1,2]);
texturemap_simple_256;
end; end;
{дефоpмиpует сетку}
procedure WarpGrate(Grate1,Grate2:tGrate ;var Grate: tGrate; t:
single); var
x,y: integer;
r: single;
begin
for y:=0 to 8 do
for x:=0 to 8 do begin r:=Grate1[y,x,1];
Grate[y,x,1]:=(Grate2[y,x,1]-r)*t+r; r:=Grate1[y,x,2];
Grate[y,x,2]:=(Grate2[y,x,2]-r)*t+r;
end; end;
{dissolving каpтинок}
procedure MorphPic(pic1,pic2,pic,t: word); assembler; asm
push ds
mov ax,pic1
db 8eh,0e8h {mov gs,ax}
mov ds,pic2
mov es,pic
xor di,di
mov si,t
cld
mov cx,0ffffh
@@l1:
mov bl,[di]
db 65h {gs:}
mov al,[di]
xor ah,ah
xor bh,bh
sub ax,bx
imul si
sar ax,8
add ax,bx
stosb
dec cx
jne @@l1
pop ds
end;
{собственно демостpация моpфиpования}
procedure Morph;
var
Grate : tGrate;
i : integer;
dir : boolean;
r : single;
t : longint;
label l1,l2;
begin
dir:=true;
l1:
for i:=0 to 30 do
begin t:=timercounter; if dir then r:=i/30 else r:=1-i/30;
WarpGrate(Grate1,Grate2,Grate,r);
Warppic(Grate1,Grate,Image1,outImage1);
WarpPic(Grate2,Grate,Image2,outImage2);
MorphPic(outImage2,outImage1,outImage3,(Round(r*256)));
ShowImage(outImage,192,64); if KeyPressed then goto l2; while
timercounter-t<1 do; {пауза}
end;
delay(6000);
dir:=not dir; goto l1; l2: while KeyPressed do ReadKey; end;
{загpужает сетки из фала}
procedure loadGrate (Fname: string);
var
f:file;
begin
assign(f,fname); reset(f,1);
blockread(f,Grate1,sizeof(TGrate));
blockread(f,Grate2,sizeof(TGrate));
close(f);
end;
begin
if paramcount<>3 then halt;
SetVesaMode($100); {установить видеоpежим 640x400x256}
SetBWPalette; {установить grayscale палитpу}
LoadImage(Image1,paramstr(1)); LoadImage(Image2,paramstr(2));
LoadGrate(paramstr(3));
outImage1:=GlobalAlloc(GMEM_FIXED,65536);
outImage2:=GlobalAlloc(GMEM_FIXED,65536);
outImage3:=GlobalAlloc(GMEM_FIXED,65536);
{выделить память для пpомежуточных изобpажений}
Morph;
textmode(3);
end.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
>> source: simple.c <<
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
/***************************************************************************
* Texture mapper by Dmitry Tjurev (FidoNet 2:5061/15.49) *
* compile: wcc386 *
***************************************************************************/
#define MNO_VERTS_IN_POLIGON 8 // максимальное количество вершин в полигоне
#define MNO_SCAN_LINES 480 // максимальное количество строк экрана
typedef struct // параметры мэпперу передаются как указатель на такую структуру
{
int verts; // количество вершин полигона
float xt[MNO_VERTS_IN_POLIGON]; // пространственные
float yt[MNO_VERTS_IN_POLIGON]; // координаты
float zt[MNO_VERTS_IN_POLIGON]; // вершин
float xp[MNO_VERTS_IN_POLIGON]; // экранные координаты
float yp[MNO_VERTS_IN_POLIGON]; // вершин
float xs[MNO_VERTS_IN_POLIGON]; // текстурные координаты
float ys[MNO_VERTS_IN_POLIGON]; // вершин
float br[MNO_VERTS_IN_POLIGON]; // яркости в вершинах
char *texture; // указатель на текстуру
} face;
void __memset(void *where,int what,int how_much);
#pragma aux __memset parm [edi] [eax] [ecx]=
"cld"
"rep stosd"
modify exact[edi ecx];
// ******** ( заполняются при инициализации ) *******************
int win_size_x,win_size_y; // размер видео-буфера
char *vid_buf; // указатель на видео-буфер
char color_tab[256*32]; // таблица перекодировки цвет+якость=цвет
int *z_buf; // указатель на Z-буфер
double l_k; // равняется 0x7fffffff*(l-0.1), где l - расстояние до экрана
// **************** Входные параметры ****************************
face *p; // указатель на структуру с входными параметрами мэппера
//***************** Рабочие переменные *******************
int ypi[MNO_VERTS_IN_POLIGON];
int zt_l[MNO_SCAN_LINES],zt_r[MNO_SCAN_LINES];
int xpi_l[MNO_SCAN_LINES],xpi_r[MNO_SCAN_LINES];
int xs_l[MNO_SCAN_LINES],ys_l[MNO_SCAN_LINES];
int xs_r[MNO_SCAN_LINES],ys_r[MNO_SCAN_LINES];
int br_l[MNO_SCAN_LINES],br_r[MNO_SCAN_LINES];
//******************** Линейный мэппинг **********************************
void draw_spans(int line,int height) // прорисовка отрезков
{ // line - начальная строка
// height - колво строк
int xsc,ysc,brc,ztc; // текущие значения
int xs_add,ys_add,br_add,zt_add; // приращения
int x1,x2,len;
int offs,offs2;
offs=line*win_size_x;
m1: x1=xpi_l[line];
x2=xpi_r[line];
if ((x1>=win_size_x) || (x2<0)) goto m2;
ztc=zt_l[line];
xsc=xs_l[line];
ysc=ys_l[line];
brc=br_l[line];
len=(x2-x1);
if (len)
{
zt_add=(zt_r[line]-ztc)/len;
xs_add=(xs_r[line]-xsc)/len;
ys_add=(ys_r[line]-ysc)/len;
br_add=(br_r[line]-brc)/len;
}
len++;
if (x2>=win_size_x) len-=x2-win_size_x+1;
if (x1<0)
{
len+=x1;
ztc-=zt_add*x1;
xsc-=xs_add*x1;
ysc-=ys_add*x1;
brc-=br_add*x1;
x1=0;
}
offs2=offs+x1;
do
{
if (ztc>z_buf[offs2])
{
z_buf[offs2]=ztc;
vid_buf[offs2]=color_tab[((brc>>16)<<8)+*(p->texture+(xsc>>16)+((ysc>>16)<<8))];
}
offs2++;
ztc+=zt_add;
xsc+=xs_add;
ysc+=ys_add;
brc+=br_add;
} while(--len);
m2: offs+=win_size_x;
line++;
if (--height) goto m1;
}
void scan_edge(int n1,int n2) // сканирование ребра
{ // x1,x2 - номера вершин начала и конца ребра
int q,y,t;
int xpc,xsc,ysc,brc,ztc; // текущие значения
int xp_add,xs_add,ys_add,br_add,zt_add; // приращения
if (ypi[n1]>ypi[n2])
{
t=n1;
n1=n2;
n2=t;
}
y=ypi[n1];
q=ypi[n2]-y;
ztc=l_k/p->zt[n1];
zt_add=(l_k/p->zt[n2]-ztc)/q;
xpc=p->xp[n1]*65536;
xsc=p->xs[n1]*65536;
ysc=p->ys[n1]*65536;
brc=p->br[n1]*65536;
xp_add=((int)(p->xp[n2]*65536)-xpc)/q;
xs_add=((int)(p->xs[n2]*65536)-xsc)/q;
ys_add=((int)(p->ys[n2]*65536)-ysc)/q;
br_add=((int)(p->br[n2]*65536)-brc)/q;
q++;
do
{
if (y<0) goto m1;
if (y>=win_size_y) return;
if ((xpc>>16) < xpi_l[y])
{
xpi_l[y]=xpc>>16;
zt_l[y]=ztc;
xs_l[y]=xsc;
ys_l[y]=ysc;
br_l[y]=brc;
}
if ((xpc>>16) > xpi_r[y])
{
xpi_r[y]=xpc>>16;
zt_r[y]=ztc;
xs_r[y]=xsc;
ys_r[y]=ysc;
br_r[y]=brc;
}
m1: ztc+=zt_add;
xpc+=xp_add;
xsc+=xs_add;
ysc+=ys_add;
brc+=br_add;
y++;
} while(--q);
}
void mapper() // собственно, мэппер
{
int q;
int ymin,ymax;
q=p->verts-1;
do
{
ypi[q]=p->yp[q];
} while(--q>=0);
ymin=ymax=ypi[0];
q=p->verts-1;
do
{
if (ypi[q]<ymin) ymin=ypi[q];
else
if (ypi[q]>ymax) ymax=ypi[q];
} while(--q);
if ((ymin==ymax)||(ymin>=win_size_y)||(ymax<0)) return;
if (ymin<0) ymin=0;
if (ymax>=win_size_y) ymax=win_size_y-1;
__memset(xpi_l+ymin,2100000000,ymax-ymin+1);
__memset(xpi_r+ymin,-2100000000,ymax-ymin+1);
q=p->verts-1;
if (ypi[q]!=ypi[0]) scan_edge(q,0);
q--;
do if (ypi[q]!=ypi[q+1]) scan_edge(q,q+1); while (--q>=0);
draw_spans(ymin,ymax-ymin+1);
}
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
>> source: gamma.pas <<
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
uses Crt;
var Scr: array[0..199,0..319] of Byte absolute $A000:$0000;
const Gamma:Real=1.8;
procedure SetPALColor(R,G,B:Real{0..1});
{с учетом гаммы}
procedure Out1(V:Real);
begin
if V<1E-3 then Port[$3C9]:=0
else if V>1 then Port[$3C9]:=63
else Port[$3C9]:=Round(63*Exp(Ln(V)/Gamma));
end; {Out1}
begin {SetPalColor}
Out1(R);
Out1(G);
Out1(B);
end; {SetPalColor}
procedure SetPalette;
var N : Integer;
begin
Port[$3C8] := 128;
for N:=0 to 127 do
SetPalColor(N/127,N/127,N/127);
end; {of SetPalette}
var X,Y: Integer;
begin
DirectVideo:=False;
asm mov ax,$13; int $10; end;
for Y:=0 to 39 do begin
for X:=0 to 99 do begin
Scr[20+Y*2,159-X]:=128;
Scr[21+Y*2,159-X]:=254;
Scr[20+Y*2,160+X]:=191;
Scr[21+Y*2,160+X]:=191;
end;
end;
for Y:=0 to 79 do begin
for X:=0 to 49 do begin
Scr[100+Y,159-X*2]:=191;
Scr[100+Y,158-X*2]:=191;
Scr[100+Y,161+X*2]:=128;
Scr[100+Y,160+X*2]:=254;
end;
end;
for Y:=0 to 79 do begin
for X:=0 to 99 do begin
Scr[60+Y,109+X]:=128+((X+Y) and 1)*126;
end;
end;
for Y:=20 to 59 do begin
for X:=25 to 74 do begin
Scr[60+Y,109+X]:=191;
end;
end;
Write(' <+> <-> to adjust');
repeat
SetPalette;
Write(#13' '#13,'Gamma = ',Gamma:1:3);
case ReadKey of
'+': Gamma:=Gamma*1.01;
'-': Gamma:=Gamma/1.01;
#27:Break;
end;
until False;
asm mov ax,$03; int $10; end;
end.
Секция 2 из 2 - Предыдущая - Следующая
|