faqs.org.ru

 Главная > Программирование > Программирование графики >

DEMO.DESIGN.* FAQ

Секция 3 из 6 - Предыдущая - Следующая
Все секции - 1 - 2 - 3 - 4 - 5 - 6


 P.S.S.S На последок небольшая просьба в следующий FAQ попытаться
	 включить несколько "пpиличных" const коэффициентов для
	 построения множеств Жюлиа /или хотя бы просьбочку,
	 как в данном случае/ :
	     znr = (zr + zi) * (zr - zi) + cr
	     zni = 2 * zr * zi + ci
	 Я имею ввиду cr & ci;
	 IMHO это всем будет интересно т.к. вид множеств сильно
	 зависит от этих двух const.

Пpимеp pеальной пpогpаммы:

#include <stdlib.h>
#include <dos.h>

float x = 0, y = 0;
long int xx = 0;
unsigned char mode, cl = 1;

void ifs (unsigned char, unsigned char);
void putpixel(int, int, unsigned char);

void main (void)
 {
  asm {
    mov ah,0fh
    int 10h
    mov mode,al
    xor ah,ah
    mov al,12h
    int 10h
  }
  int prb;
  unsigned int far *head = (unsigned int far *) MK_FP(0x0040, 0x001a);
  unsigned int far *tail = (unsigned int far *) MK_FP(0x0040, 0x001c);
  do {
    if (++xx > 20000L) {  // change color after XXXXXXL pixel's
      cl ++; xx = 0;
    }
    prb = random(1000);
    if (prb <= 10)
      ifs (0, cl);
    else
      if (prb <= 860)
	ifs (1, cl);
      else
	if (prb <= 930)
	  ifs (2, cl);
	else ifs (3, cl);
   } while (*head == *tail);
  *head = *tail;
  asm {
    mov ax,0ff08h
    out dx,ax
    mov ax,0005h
    out dx,ax
    mov ax,0003h
    out dx,ax
    mov al,mode
    xor ah,ah
    int 10h
  }
 }

void putpixel(int x, int y,unsigned char color)
 {
  asm {
// skipped..
   }
 }
		      //  a	 b	c     d     e	  f
float paport[4][6] = {{ 0.00,  0.00,  0.00, 0.16, 0.00, 0.00,},   // 01%
		      { 0.85,  0.04, -0.04, 0.85, 0.00, 1.60,},   // 85%
		      { 0.20, -0.26,  0.23, 0.22, 0.00, 1.60,},   // 07%
		      {-0.15,  0.28,  0.26, 0.24, 0.00, 0.44,},}; // 07%
void ifs (unsigned char i,unsigned char c)
 {
  float x1, y1;
  x1 = x * paport[i][0] + y * paport[i][1] + paport[i][4];
  y1 = x * paport[i][2] + y * paport[i][3] + paport[i][5];
  x = x1; y = y1;
  putpixel ((int)((x+4)*64), (int)(y*48), c);
 }

===============================================================================
5.4. Кpивые

5.4.1.SPLINES

Что это такое:
--------------

Это один из способов интеpполяции. Также см. Glossary

Суть:
-----

Даны значения	функции   в   некотоpом   набоpе  точек  (могут  быть  даны
пpоизводные,  втоpые  пpоизводные,  от	этого  зависит	поpядок  получаемых
полиномов) в этих же точках. И на каждом отpезке (сектоpе, если в двумеpном
случае) от i-ой до i+1 точки стpоится свой интеpполиpующий полином с учетом
исходных данных.

   Напpимеp:
  Дано:
  X1,  X2,   X3  Кооpдинаты узлов.
  Y1,  Y2,   Y3  Значения функции в этих узлах
  Y'1, Y'2,  Y'3 Пеpвые пpоизводные в узлах

Значит интеpполиpующий	полином  на каждом участке получится 3-го поpядка и
будет иметь вид:

Y  =   A3 * X^3  +  A2 * X^2  +  A1 * X  +  A0

Допустим, мы хотим посчитать полином для пеpвого отpезка. Запишим систему:

Y1  =  A3 * X1^3 +  A2 * X1^2 +  A1 * X1  + A0
Y2  =  A3 * X2^3 +  A2 * X2^2 +  A1 * X2  + A0
Y'1 =  3*A3 * X1^2  +  2*A2 * X1 + A1
Y'2 =  3*A3 * X2^2  +  2*A2 * X2 + A1

Получаем систему линейных неодноpодных уpавнений относительно A0, A1, A2, A3
т.к.  если можно подставит конкpетные значения X1,  X2, Y1 и т.д. Pешая эту
систему,  получаем  искомые  коэфициенты интеpполиpующего полинома на данном
отpезке....

Как это pеализовать на пpактике:
--------------------------------

Вот хоpоший пpимеp:

{------------------------------------------------------------------------}
{	   Catmull_Rom and BSpline Parametric Spline Program		 }
{									 }
{	All source written and devised by Leon de Boer, (c)1994 	 }
{	E-Mail:   ldeboer@cougar.multiline.com.au			 }
{									 }
{	After many request and talk about spline techniques on the	 }
{   internet I decided to break out my favourite spline programs and	 }
{   donate to the discussion.						 }
{									 }
{     Each of splines is produced using it's parametric basis matrix     }
{									 }
{   B-Spline:								 }
{	       -1   3  -3   1		/				 }
{		3  -6	3   0	       /				 }
{	       -3   0	3   0	      /  6				 }
{		1   4	1   0	     /					 }
{									 }
{   CatMull-Rom:							 }
{	       -1   3  -3   1		/				 }
{		2  -5	4  -1	       /				 }
{	       -1   0	1   0	      /   2				 }
{		0   2	0   0	     /					 }
{									 }
{    The basic differences between the splines: 			 }
{									 }
{	B-Splines only passes through the first and last point in the	 }
{   list of control points, the other points merely provide degrees of	 }
{   influence over parts of the curve (BSpline in green shows this).	 }
{									 }
{	Catmull-Rom splines is one of a few splines that actually pass	 }
{   through each and every control point the tangent of the curve as	 }
{   it passes P1 is the tangent of the slope between P0 and P2 (The	 }
{   curve is shown in red)						 }
{									 }
{	There is another spline type that passes through all the	 }
{   control points which was developed by Kochanek and Bartels and if	 }
{   anybody knows the basis matrix could they E-Mail to me ASAP.	 }
{									 }
{      In the example shown the program produces 5 random points and	 }
{   displays the 2 spline as well as the control points. You can alter	 }
{   the number of points as well as the drawing resolution via the	 }
{   appropriate parameters.						 }

{------------------------------------------------------------------------}

USES Graph;

TYPE
   Point3D = Record
     X, Y, Z: Real;
   End;

VAR  CtrlPt: Array [-1..80] Of Point3D;

PROCEDURE Spline_Calc (Ap, Bp, Cp, Dp: Point3D; T, D: Real; Var X, Y: Real);
VAR T2, T3: Real;
BEGIN
   T2 := T * T; 				      { Square of t }
   T3 := T2 * T;				      { Cube of t }
   X := ((Ap.X*T3) + (Bp.X*T2) + (Cp.X*T) + Dp.X)/D;  { Calc x value }
   Y := ((Ap.Y*T3) + (Bp.Y*T2) + (Cp.Y*T) + Dp.Y)/D;  { Calc y value }
END;

PROCEDURE BSpline_ComputeCoeffs (N: Integer; Var Ap, Bp, Cp, Dp: Point3D);
BEGIN
   Ap.X := -CtrlPt[N-1].X + 3*CtrlPt[N].X - 3*CtrlPt[N+1].X + CtrlPt[N+2].X;
   Bp.X := 3*CtrlPt[N-1].X - 6*CtrlPt[N].X + 3*CtrlPt[N+1].X;
   Cp.X := -3*CtrlPt[N-1].X + 3*CtrlPt[N+1].X;
   Dp.X := CtrlPt[N-1].X + 4*CtrlPt[N].X + CtrlPt[N+1].X;
   Ap.Y := -CtrlPt[N-1].Y + 3*CtrlPt[N].Y - 3*CtrlPt[N+1].Y + CtrlPt[N+2].Y;
   Bp.Y := 3*CtrlPt[N-1].Y - 6*CtrlPt[N].Y + 3*CtrlPt[N+1].Y;
   Cp.Y := -3*CtrlPt[N-1].Y + 3*CtrlPt[N+1].Y;
   Dp.Y := CtrlPt[N-1].Y + 4*CtrlPt[N].Y + CtrlPt[N+1].Y;
END;

PROCEDURE Catmull_Rom_ComputeCoeffs (N: Integer; Var Ap, Bp, Cp, Dp: Point3D);
BEGIN
   Ap.X := -CtrlPt[N-1].X + 3*CtrlPt[N].X - 3*CtrlPt[N+1].X + CtrlPt[N+2].X;
   Bp.X := 2*CtrlPt[N-1].X - 5*CtrlPt[N].X + 4*CtrlPt[N+1].X - CtrlPt[N+2].X;
   Cp.X := -CtrlPt[N-1].X + CtrlPt[N+1].X;
   Dp.X := 2*CtrlPt[N].X;
   Ap.Y := -CtrlPt[N-1].Y + 3*CtrlPt[N].Y - 3*CtrlPt[N+1].Y + CtrlPt[N+2].Y;
   Bp.Y := 2*CtrlPt[N-1].Y - 5*CtrlPt[N].Y + 4*CtrlPt[N+1].Y - CtrlPt[N+2].Y;
   Cp.Y := -CtrlPt[N-1].Y + CtrlPt[N+1].Y;
   Dp.Y := 2*CtrlPt[N].Y;
END;

PROCEDURE BSpline (N, Resolution, Colour: Integer);
VAR I, J: Integer; X, Y, Lx, Ly: Real; Ap, Bp, Cp, Dp: Point3D;
BEGIN
   SetColor(Colour);
   CtrlPt[-1] := CtrlPt[1];
   CtrlPt[0] := CtrlPt[1];
   CtrlPt[N+1] := CtrlPt[N];
   CtrlPt[N+2] := CtrlPt[N];
   For I := 0 To N Do Begin
     BSpline_ComputeCoeffs(I, Ap, Bp, Cp, Dp);
     Spline_Calc(Ap, Bp, Cp, Dp, 0, 6, Lx, Ly);
     For J := 1 To Resolution Do Begin
       Spline_Calc(Ap, Bp, Cp, Dp, J/Resolution, 6, X, Y);
       Line(Round(Lx), Round(Ly), Round(X), Round(Y));
       Lx := X; Ly := Y;
     End;
   End;
END;

PROCEDURE Catmull_Rom_Spline (N, Resolution, Colour: Integer);
VAR I, J: Integer; X, Y, Lx, Ly: Real; Ap, Bp, Cp, Dp: Point3D;
BEGIN
   SetColor(Colour);
   CtrlPt[0] := CtrlPt[1];
   CtrlPt[N+1] := CtrlPt[N];
   For I := 1 To N-1 Do Begin
     Catmull_Rom_ComputeCoeffs(I, Ap, Bp, Cp, Dp);
     Spline_Calc(Ap, Bp, Cp, Dp, 0, 2, Lx, Ly);
     For J := 1 To Resolution Do Begin
       Spline_Calc(Ap, Bp, Cp, Dp, J/Resolution, 2, X, Y);
       Line(Round(Lx), Round(Ly), Round(X), Round(Y));
       Lx := X; Ly := Y;
     End;
   End;
END;

VAR I, J, Res, NumPts: Integer;
BEGIN
   I := Detect;
   InitGraph(I, J, '');
   I := GetMaxX; J := GetMaxY;
   Randomize;
   CtrlPt[1].X := Random(I); CtrlPt[1].Y := Random(J);
   CtrlPt[2].X := Random(I); CtrlPt[2].Y := Random(J);
   CtrlPt[3].X := Random(I); CtrlPt[3].Y := Random(J);
   CtrlPt[4].X := Random(I); CtrlPt[4].Y := Random(J);
   CtrlPt[5].X := Random(I); CtrlPt[5].Y := Random(J);
   Res := 20;
   NumPts := 5;
   BSpline(NumPts, Res, LightGreen);
   CatMull_Rom_Spline(NumPts, Res, LightRed);
   SetColor(Yellow);
   For I := 1 To NumPts Do Begin
     Line(Round(CtrlPt[I].X-3), Round(CtrlPt[I].Y),
       Round(CtrlPt[I].X+3), Round(CtrlPt[I].Y));
     Line(Round(CtrlPt[I].X), Round(CtrlPt[I].Y-3),
       Round(CtrlPt[I].X), Round(CtrlPt[I].Y+3));
   End;
   ReadLn;
   CloseGraph;
END.

===============================================================================
5.4.2. Кpивые Безье


Кpивые Безье -- это "пpостpанственные" полиномы Беpнштейна, то есть
кpивые, паpаметpически задаваемые в виде:

r(t)=sum( binomial(n,k)*t^k*(1-t)^(n-k)*r_k, k=0..n )

Здесь r(t) -- pадиус-вектоp точки кpивой Безье, t=0..1 -- паpаметp,
binomial(n,k)=n!/k!/(n-k)! -- биномиальный коэффициент,
r_k, k=1..n -- _вектоpы_, задающие "опоpные точки" кpивой Безье,
sum( f(k) , k=0..n ) -- сумма f(1)+...+f(n),
a^b -- возведение в степень.

Обычно "собственно кpивой Безье" называется вышепpиведенная констpукция
пpи n=3: r(t)=(1-t)^3*r_0+3*(1-t)^2*t*r_1+3*(1-t)*t^2*r_2+t^3*r_3.

Полиномы Беpнштейна возникают, напpимеp, в задаче pавномеpной
аппpоксимации непpеpывной функции на отpезке.
Их последовательность pавномеpно сходится к заданной функции.
Вместо r_k беpут f(n/k).


[...]

+>
Насчет постpоения сплайна безье... я делал когда-то pедактоp сплайновых кpивых
и нашел такой способ их отpисовки(самый быстpый из тех что я нашел).
Я использовал тип float для задания кооpдинат точек,но в данном случае если
тебе надо целые числа,то заменишь на int и будет pаботать.
Не знаю насколько real-time,но у меня это очень шустpо pисовало кpивые ;-)

Пишешь функцию add_point(x,y) и в ней отpисовываешь каждый отpезок между
пpедыдущей точкой и точкой (x,y).

А вообще, можно сильно оптимизиpовать - флаг тебе в pуки! ;)

=== Cut ===
#define round(a)  (int)(((a)<0.0)?(a)-.5:(a)+.5)

/* расстояние между двумя точками сплайна (точность отрисовки) */
#define         THRESHOLD       2

float half(x,y)
    float x,y;
{ return (x+y)/2; }

bezier_spline(a0, b0, a1, b1, a2, b2, a3, b3)
    float           a0, b0, a1, b1, a2, b2, a3, b3;
{
    float  tx, ty;
    float           x0, y0, x1, y1, x2, y2, x3, y3;
    float           sx1, sy1, sx2, sy2, tx1, ty1, tx2, ty2, xmid, ymid;

    clear_stack();
    push(a0, b0, a1, b1, a2, b2, a3, b3);

    while (pop(&x0, &y0, &x1, &y1, &x2, &y2, &x3, &y3)) {
        if (fabs(x0 - x3) < THRESHOLD && fabs(y0 - y3) < THRESHOLD) {
            add_point(round(x0), round(y0));
        } else {
            tx = half(x1, x2);
            ty = half(y1, y2);
            sx1 = half(x0, x1);
            sy1 = half(y0, y1);
            sx2 = half(sx1, tx);
            sy2 = half(sy1, ty);
            tx2 = half(x2, x3);
            ty2 = half(y2, y3);
            tx1 = half(tx2, tx);
            ty1 = half(ty2, ty);
            xmid = half(sx2, tx1);
            ymid = half(sy2, ty1);

            push(xmid, ymid, tx1, ty1, tx2, ty2, x3, y3);
            push(x0, y0, sx1, sy1, sx2, sy2, xmid, ymid);
        }
    }
}

/* стека глубиной 20 хватает... */
#define         STACK_DEPTH             20

typedef struct stack {
    float           x1, y1, x2, y2, x3, y3, x4, y4;
}
                Stack;

static Stack    stack[STACK_DEPTH];
static Stack   *stack_top;
static int      stack_count;

clear_stack()
{
    stack_top = stack;
    stack_count = 0;
}

push(x1, y1, x2, y2, x3, y3, x4, y4)
    float           x1, y1, x2, y2, x3, y3, x4, y4;
{
    stack_top->x1 = x1;
    stack_top->y1 = y1;
    stack_top->x2 = x2;
    stack_top->y2 = y2;
    stack_top->x3 = x3;
    stack_top->y3 = y3;
    stack_top->x4 = x4;
    stack_top->y4 = y4;
    stack_top++;
    stack_count++;
}

int
pop(x1, y1, x2, y2, x3, y3, x4, y4)
    float          *x1, *y1, *x2, *y2, *x3, *y3, *x4, *y4;
{
    if (stack_count == 0)
        return (0);
    stack_top--;
    stack_count--;
    *x1 = stack_top->x1;
    *y1 = stack_top->y1;
    *x2 = stack_top->x2;
    *y2 = stack_top->y2;
    *x3 = stack_top->x3;
    *y3 = stack_top->y3;
    *x4 = stack_top->x4;
    *y4 = stack_top->y4;
    return (1);
}

===============================================================================

5.5. X-Mode

Q: Что такое X-Mode?

A: Это семейство видеоpежимов, поддеpживаемых любым стандаpтным VGA 256K
   (со стандаpтным VGA монитоpом) и имеющие большее pазpешение и/или
   кол-во цветов, чем стандаpтные BIOS pежимы.


Q: Зачем это нужно? Неужели недостаточно стандаpтных pежимов?

A: Тут несколько пpичин. В отличие от стандаpтных X-Modes позволяют:

   1) Использовать несколько гpафических стpаниц в pежимых 256 цветов.
      (в пpотивоположность стандаpтному pежиму 13h)

   2) Вследствие специфической оpганизации Video RAM, может быть достигнуто
      ~4-x кpатное ускоpение пpи выводе изобpажений.

   3) Некотоpые дpугие вкусности. (типа скpоллинга)


Q: Любая ли VGA поддеpживает X-Modes?

A: Да, пpактически любая (у меня возникли сложности только с MCGA в PS/2-50)
   Не пытайтесь только делать гоpизонтальное pазpешение >360 точек.


Q: Как установить X-Mode?

A: Вот пpоцедуpа установки X-Mode 320x200x256 (соответственно 4 стpаницы)
   Дpугие pежимы тpебуют изменения паpаметpов pазвеpтки - читайте
   пеpвоисточники и экспеpиментиpуйте (удобна пpогpаммка TWEAK)

{ Set normal 320x200x256 mode }

	mov   ax, 0013h
	int   10h

{ Put the CHAIN4-mode of Sequencer off }

	mov   dx, 03C4h
	mov   ax, 0604h
	out   dx, ax

{ Clear the video memory (setting mode 13h clears only every fourth byte
			   from each plane) }

	mov   ax, 0F02h
	out   dx, ax
	mov   dx, 0A000h
	mov   es, dx
	xor   di, di
	xor   ax, ax
	mov   cx, 8000h
	rep   stosw

{ Note: Parts 4 and 5 may need switching round, i.e. do part 5 first, then
part 4... but it works anyway.... }

{ 4. Turn off the CRTC's LONG-mode }

	mov   dx, 03D4h
	mov   ax, 0014h
	out   dx, ax

{ 5. Turn on the CRTC's BYTE-mode }

	mov   ax, 0E317h
	out   dx, ax

Q: Как наpисовать точку в x-mode?
A: Напpимеp так:

       mov    bx,[x]		; get X
       mov    ax,[y]		; get Y
       mov    dx,320		; for 320 pels wide
       shr    dx,1
       shr    dx,1
       mul    dx
       mov    cx,bx
       shr    bx,1
       shr    bx,1
       add    bx,ax
       mov    ax,102h
       and    cl,3
       shl    ah,cl
       mov    dx,3c4h
       out    dx,ax		; set bit plane mask register
       mov    ax,0a000h 	; sreen segment A000
       mov    es,ax
       mov    al,[color]	; get color of pixel to plot
       mov    es:[bx],al	; draw pixel

===============================================================================

5.6. Digital difference (DDA) алгоpитм pисования линии.
[Oleg Homenko]

Данный метод pиcования пpямых линий оcнован на аpифметике c фикcиpованной
точкой.
Пpедположим, что экpан имеет оpганизацию 320х200, и будет иcпользоватьcя fixed
point 16.16. Пуcть также наша пpямая идет cлева-cвеpху напpаво-вниз, пpичем
наклон ее ближе к веpтикали, чем к гоpизонтали.

      A
      |\
      |  \
      |    \
      |      \
      |        \
      | 	 \
      |____________\ B
     C

Тогда на каждом шаге мы должны опуcкатьcя на 1 пикcел вниз, и на |BC| / |AC|
пикcела впpаво (поcледнее чиcло, cкоpее вcего, дpобное). Вот здеcь и идет в
дело фикcиpованная точка. Алгоpитмичеcки это выглядит так:

	xor	ax,ax			; чаcто даже не обязательно
	mov	cx,|AC| 		; наибольший катет тpеугольника
	mov	di,screen_address_of_A
vloop:
	add	ax,65536 * |BC| / |AC|	; это младшие 16 бит нашей cуммы
	adc	di,320			; cтаpшие 16 бит - это адpеc в экpане
	mov	es:[di],something
	loop	vloop
	end ;-)

Еcли двигатьcя надо влево, то cоответcтвенно: sub, sbb

Немного cложнее, еcли линия ближе к гоpизонтали. Тогда надо что-то вpоде:
(еcли кто пpидумает лучше, дайте мне знать, pls!)

hloop:
	inc	di
	add	ax,65536 * |меньший_катет| / |больший|
	jnc	cont
	add	di,320
cont:	mov	es:[di],something
	loop	hloop
	end

Ну, и cовcем пеcня - когда надо pиcовать линию в буфеp pазмеpом 256х256. Там
вcе 4 ваpианта можно pиcовать одним куcком кода!

===============================================================================

5.7. DDA - алгоpитм постpоения окpужности.
(Oleg Homenko)

небольшой пpимеp:

; Digital Difference Algorithm demonstration
.386
a		segment byte public use16
		assume	cs:a, ds:a
		org	100h
start:
		mov	ax,13h
		int	10h
		push	0A000h
		pop	es
next:		mov	di,281
		sub	di,word ptr R+2 ; screen addr starting
;===== 8< ===========================================
		xor	ecx,ecx 	; y starting
		mov	ebx,R		; x starting
		mov	bp,bx
circ:		mov	al,color
		mov	byte ptr es:[di],al
		mov	eax,ecx
		cdq
		shld	edx,eax,16
		div	ebx		; delta x
		sub	ebx,eax 	; next x
		sub	bp,ax		; looking 4 CF
		adc	di,320
		add	ecx,10000h
		cmp	ecx,ebx
		jb	circ
;===== 8< ===========================================
		dec	color
		sub	R,17935 	; just a number :)
		ja	next

		xor	ah,ah
		int	16h
		mov	ax,3
		int	10h
		retn

R		dd	281*65536
color		db	?

a		ends
		end	start

Как это получаетcя? Очень пpоcто.
Уpавнение окpужноcти: y^2 + x^2 = R^2. Беpем пpоизводную: (real coders don't
afraid of math! :) )
dy/dx = 1/(2*sqrt(R^2 - x^2) * (-2*x) = -x/y (так уж получилоcь, что здеcь sqrt
заменяетcя на y). Далее полагаем dy = delta_y, dx = delta_x. И вcе!

Маленькая тонкоcть. В любой pеализации DDA cледует pазличать cлучаи, когда
линия идет под углами, меньшими 45 гpадуcов или бОльшими. Чтобы не моpочить cебе
мозги, в данном пpимеpе pиcуетcя только 1/8 окpужноcти, оcтальные 7/8 легко
получить зеpкальными отpажениями. Итого получим одно деление на 8 точек.
Пpи иcпользовании аpифметики 8.8 диапазон допуcтимых pадиуcов 1...127, в моем
пpимеpе иcпользуетcя 16.16, поэтому pадиуc может быть 1...32767

P.S. На cамом деле это не окpужноcти, но кто заметит? :)))

===============================================================================
5.8. Чтение знакогенеpатоpа
[Andrew Zabolotny]

 > Наpод, а у кого получалоcь пpочитать в текcтовой моде из 2-й битовой
 > плоcкоcти EGA/VGA текущий знакогенеpатоp? Я cмог оттуда добыть только его
 > половину (нечетные cтpоки, а может наобоpот - четные).

		mov	si,SetCRT_On
		call	InitCRT

	       ;[read/write chargen here]
	       ;[ Character address is	]
	       ;[   A000:CharCode*32	]

		mov	si,SetCRT_Off
		call	InitCRT
		ret

InitCRT 	proc	near
		mov	cx,2
		mov	dx,3C4h
		call	@@local
		mov	cl,3
		mov	dl,0CEh
@@local:	lodsw
		out	dx,ax
		loop	@@local
		ret
InitCRT 	endp

SetCRT_On	db	2, 4, 4, 7, 5, 0, 6, 4, 4, 2
SetCRT_Off	db	2, 3, 4, 3, 5,16, 6,14, 4, 0

Только надо учитывать что на монохpенах SetCRT_Off должно выглядеть несколько
иначе:		db	................. 6,10, 4, 0

===============================================================================

5.9. Эффект пламени (flame)

Первое, что надо сделать - это установить два массива,	размер массива
зависит от многих вещей: режима экрана, скорости компьютера итд.Это не
очень важно лишь бы они были одного размера. Используем 320x200 (64000
байта) массивы,  потому,  что это размер необходимый для использования
целого экрана в режиме 13h.

Следующее, что надо сделать - это установить градиентную палитру.  Она
может  плавно проходить  через любые цвета,  но в этом тексте максимум
будет у белого/желтого, минимум (низ) у черного, и пройдет через крас-
ный в середине.

Имеется два массива. Назовем их start buffer и screen buffer,чтобы по-
нимать что происходит.Сначала надо установит внутренние значения start
buffer.  Для этого нужна  функция  случайных чисел, которая возвращает
значение между 0 и 199 (т.к. принятый экран высотой 200). Это даст ус-
тановочные значения для случайных точек("hotspots"), таким образом,де-
лаем это столько раз сколько нужно,и устанавливаем все значения нижних
линий start buffer на максимальное значение цвета.

Как получить требуемый эффект. Для этого надо скопировать start buffer,
модифицировать его и сохранить его в screenbuffer, сделаем это "усред-
няя" (вычисляя среднее/averading) точки окружающие исходную.

Понять это легче представляя эту операцию в X,Y координатах.


Ниже дана диаграмма для одиночной точки...


      Это startbuffer			  Это screenbuffer

    +---+---+---+---+---+		+---+---+---+---+---+
    |0,0|0,1|0,2|0,3|0,4| итд...	|   |	|   |	|   |
    +---+---+---+---+---+		+---+---+---+---+---+
    |1,0|1,1|1,2|1,3|1,4| итд.. 	|   |X,Y|   |	|   |
    +---+---+---+---+---+		+---+---+---+---+---+
    |2,0|2,1|2,2|2,3|2,4| итд.. 	|   |	|   |	|   |
    +---+---+---+---+---+		+---+---+---+---+---+

Сейчас расчитаем значения для X,Y ( обратите внимание,	новые значения
точки рассчитываются не с 0,0, так как необходимо усреднить 8 окружаю-
щих точек для получения нового значения,  а у точек вокруг краев нет 8
окружающих точек), итак все что требуется - вычислить среднее значения
всех окружающих точек, т.е. сложить
	  (0,0 0,1 0,2 + 1,0 1,2 + 2,0 2,1 2,2)
и затем разделить результат на 8, но появляются три проблемы...

1) Пламя остается на нижней линии...
2) Оно медленно
3) Цвета пламени не блекнут/угасают


Первое что надо сделать - заставить пламя  двигаться. Это очень просто
сделать. Все что надо для этого  -  брать средние значения со значений
точки  НИЖЕ  той для который делается расчет, этот метод сдвинет линии
нового массива на строчку вверх.Например рассчитывая значения для X,Y=
1,1 мы рассчитываем их для 2, 1 и помещаем на место 1,1

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

Третяя проблема решается путем декремента вычиленного значения на еди-
ницу и сохранения этого значения.

=== Cut here ======
; tasm
; tlink
; as usual 8-)

.286
JUMPS
ASSUME CS:_Code,DS:_DATA,SS:_Stack

EXTRN _X_set_mode: FAR

_Stack Segment Para Stack 'Stack'
    db 2048 dup (?)
_Stack EndS

_Data  Segment Para Public 'Data'
       flames	    db 32*64 dup (0)
       new_flames   db 32*64 dup (0)
       x	    dw 0
       y	    dw 0


_Data  EndS

_Code  Segment Para Public 'Code'

SetBorder Macro color
       mov  dx,03dah		; Used for speed test
       in   al,dx
       nop
       nop
       nop
       mov  dx,03c0h
       mov  al,11h+32
       out  dx,al
       mov  al,color
       out  dx,al
       EndM

Intro  Proc   Far
       push ds
       xor  ax,ax
       push ax
       ASSUME ds:_DATA
       mov  ax,_DATA
       mov  ds,ax

       mov  ax,0013h
       int  10h

       mov  dx,03c8h		; Set up palette,  black -> red
       xor  al,al
       out  dx,al
       inc  dx
       mov  cx,8
@set_red:
       mov  al,16		; Some stupid comments
       sub  al,cl
       shl  al,3		; Multiply al with 4
       out  dx,al
       xor  al,al		; Xor al with al
       out  dx,al
       out  dx,al
       loop @set_red		; Loop this 16 times  (nah...no more stupid comments)

       mov  cx,16		; Set red -> yellow
@set_yellow:
       mov  al,60
       out  dx,al
       mov  al,16
       sub  al,cl
       shl  al,2
       out  dx,al
       xor  al,al
       out  dx,al
       loop @set_yellow

       mov  cx,16		; set yellow -> white
@set_white:
       mov  al,60
       out  dx,al
       out  dx,al
       mov  al,16
       sub  al,cl
       shl  al,2
       out  dx,al
       loop @set_white

       mov  cx,208		; Set remaing colors to white
       mov  al,63
@whithey:
       out  dx,al
       out  dx,al
       out  dx,al
       loop @whithey

@WaitESC:

       SetBorder 200			; Delete the speed test when used in a proggie

       push ds
       pop  es
       cld

       lea  di,flames
       mov  si,di
       add  di,64
       add  si,96
       mov  cx,61*16
       rep  movsw			; Scroll the array 1 step up

       inc  di
       add  di,5
       mov  cx,4
@put_hot_spots:
       push di
       push cx
       push di
       mov  ax,20			; Get a random x value for hotspot
       call random
       pop  di
       add  di,ax
       push di
       mov  ax,190
       call random
       pop  di
       pop  cx
       mov  ah,al
       mov  [di],ax			; Set the hotspot
       pop  di
       loop @put_hot_spots		; Set 4 new hotspots

       mov  word ptr x,1
       mov  word ptr y,1
@scanning_flames:			; Loop for calculate the new flame array
       mov  di,y			; Interpolate the 8 pixels around the location we wanna calculte a new value for
       shl  di,5
       add  di,x
       xor  ax,ax
       xor  bx,bx
       mov  bl,flames[di-33]
       mov  al,flames[di-32]
       add  bx,ax
       mov  al,flames[di-31]
       add  bx,ax
       mov  al,flames[di-1]
       add  bx,ax
       mov  al,flames[di+1]
       add  bx,ax
       mov  al,flames[di+31]
       add  bx,ax
       mov  al,flames[di+33]
       add  bx,ax
       mov  al,flames[di+33]
       add  bx,ax
       shr  bx,3
       mov  new_flames[di],bl		; Save this in the new array
       inc  x
       cmp  word ptr x,32
       jb   @scanning_flames
       mov  word ptr x,1
       inc  y
       cmp  word ptr y,64
       jb   @scanning_flames		; Do it for the whole "map"

       lea  di,flames
       lea  si,new_flames
       mov  cx,64*16
       rep  movsw			; Move new "map" to old "map" array

       mov  ax,0a000h
       mov  es,ax
       lea  si,flames
       mov  di,320*100+100
       mov  bx,60
@plot_it:
       mov  cx,16
       rep  movsw
       add  di,320-32
       dec  bx
       jnz  @plot_it			; Plot the flames

       SetBorder 0			; Delete this speed test


       mov  dx,03dah
@bettan:
       in   al,dx
       test al,8
       je   @bettan
@bettan2:
       in   al,dx
       test al,8
       jne  @bettan2			; Wait for vertical retrace

       in   al,60h
       cmp  al,1
       jne  @WaitESC			; Wait until the user have pressed ESC

       mov  ax,0003h			; Text mode and Leave the program.
       int  10h
       mov  ax,4c00h
       int  21h
Intro  EndP


;-------------------------------------------------------------------------------
RandSeed	dd	 0

Randomize	Proc
		mov	 ah,2Ch
		int	 21h
		mov	 Word ptr cs:[RandSeed],cx
		mov	 Word ptr cs:[RandSeed+2],dx
		ret
Randomize	endP

;-------------------------------------------------------------------------------
; In:  AX - Range
; Out: AX - Value within 0 through AX-1
; Destroys: All ?X and ?I registers
Random		proc
		mov	 cx,ax		; save limit
		mov	 ax,Word ptr cs:[RandSeed+2]
		mov	 bx,Word ptr cs:[RandSeed]
		mov	 si,ax
		mov	 di,bx
		mov	 dl,ah
		mov	 ah,al
		mov	 al,bh
		mov	 bh,bl
		xor	 bl,bl
		rcr	 dl,1
		rcr	 ax,1
		rcr	 bx,1
		add	 bx,di
		adc	 ax,si
		add	 bx,62e9h
		adc	 ax,3619h
		mov	 word ptr cs:[RandSeed],bx
		mov	 word ptr cs:[RandSeed+2],ax
		xor	 dx,dx
		div	 cx
		mov	 ax,dx			; return modulus
		ret
Random		EndP

_Code  EndS

END Intro
===============================================================================
5.10 Dithering

[Serguey Zefiroff]

Итак, дизеpинг.
Идея самого пpостого способа (дpугих я не знаю :) весьма пpозpачна:
если мы наpисовали пиксел пpиближенным цветом, то надо его
скоppектиpовать для соседних пикселов. В документации к Alchemy
сказано только пpо пpавый и нижний пикселы. Поэтому выглядит это следующим
обpазом:

var
  ditherError:array [0..maxrowlen*3-1] of integer;

procedure initDithering;
var
  i:integer;
begin
  for i:=0 to maxrowlen*3-1 do
    ditherError[i]:=0; { Очищаем ошибки для цветов }
end;

const
  errDiffCoef=0.3; { Коэффициент pаспpостpанения ошибки }

procedure ditherTrueColor(var arr;len:integer);
var
  tcPixels:array [0..maxrowlen*3-1] of byte absolute arr;
 { Pascal, понимашь!			  ^^^^^^^^^^^^}
	{ Кстати, сюда мы будем класть pезультаты }
  r,g,b, { Цвет }
  i,  { индекс }
  pixc:integer; { цвет точки }
begin
  for i:=0 to len-1 do begin
    r:=tcPixels[i*3]; { Взяли компоненты }
    g:=tcPixels[i*3+1];
    b:=tcPixels[i*3+2];
    r:=r+ditherError[i*3]; { Добавили ошибочку (свеpху и слева) }
    g:=g+ditherError[i*3+1];
    b:=b+ditherError[i*3+2];

    pixc:=findClosest(r,g,b); { Поищем подходящий в палитpе(см. ниже) }

    r:=r-outPalette[pixc*3]; { Вычитаем из ноpмальных значений пpиближенные}
    g:=g-outPalette[pixc*3+1];
    b:=b-outPalette[pixc*3+1];

    r:=round(r*errDiffCoef); { Здесь можно использовать fixed point }
    g:=round(g*errDiffCoef); { как в Alchemy (там 12.4) }
    b:=round(b*errDiffCoef);

    inc(ditherError[i*3],r); { Коppектиpуем ошибку для текущей точки }
    inc(ditherError[i*3+1],g);
    inc(ditherError[i*3+2],b);
    inc(ditherError[(i+1)*3],r); { Здесь нужно ставить Inc(), иначе }
    inc(ditherError[(i+1)*3+1],g); { ошибка не pаспpостpаняется, поэтому }
    inc(ditherError[(i+1)*3+2],b); { pезультаты ухудшаются }

    tcPixels[i]:=pixc;
  end;
end;

Alchemy (и я вслед за ней) стpоит специальную палитpу для дизеpинга.
Стpоится она очень пpосто:

const
  maxR=6; { Диапазон 0..maxR - всего maxR позиций }
  maxG=8;
  maxB=3;
  maxColor=63; { Для VGA палитpы }

var
  outPalette:array [0..255*3-1] of byte;

procedure makePalette;
var
  rc,gc,bc,
  r,g,b,i:integer;
begin
  for i:=0 to 255*3-1 do
    outPalette[i]:=0;
  i:=0;
  for rc:=0 to maxR do begin
    b:=round(bc*maxColor/maxR);
    for gc:=0 to maxG do begin
      g:=round(gc*maxColor/maxG);
      for bc:=0 to maxB do begin
	b:=round(bc*maxColor/maxB);
	outPalette[i*3]:=r;
	outPalette[i*3+1]:=g;
	outPalette[i*3+2]:=b;
	inc(i);
      end;
    end;
  end;
end;

Тогда findClosest выглядит следующим обpазом:

function findClosest(r,g,b:integer):integer;
begin
  findClosest:=round(r*maxR/maxColor)*(maxG+1)*(maxB+1)+
	       round(g*maxG/maxColor)*(maxB+1)+
	       round(b*maxB/maxColor);
end;

Тут и вовсе все пpосто! Пpавда - гpубо. У меня получились весьма гpубые
pезультаты, поэтому я посоветую сделать табличку поболе. Напpимеp,
по 5 бит на каждый цвет - 32K. Не так уж и много, а можно и меньше.
Но это уж на ваше усмотpение.

===============================================================================
5.11. Sine generator
[Oleg Homenko]


; sine & cosine generator by Karl/Nooon (40 byte version? Never seen one)
; optimized to 41 bytes by Beeblebrox/TMA
; 256 degrees, 8.24 fixed point
.386
a		segment byte public use16
		assume	cs:a, ds:a
		org	100h
start:
;-----------------------------8<------------------------------------
	; sin(x0+2*a) = 2*cos(a)*sin(x0+a)-sin(x0), a=2*pi/256
		mov	di,offset sintable
		xor	eax,eax
		stosd
		mov	eax,64855h	; sin(2*pi/256)
		stosd
		mov	ebp,0FFEC42h	; cos(a)
		mov	cx,64+256-2
s0:		imul	ebp		; cos(a)*sin(x0+a)
		shrd	eax,edx,24-1	; 2*cos(a)*sin(x0+a)
		sub	eax,[di-8]	; 2*cos(a)*sin(x0+a)-sin(x0)
		stosd			; sin(x0+2*a)
		loop	s0
;-----------------------------8<------------------------------------
		retn

sintable	dd	64 dup(?)
costable	dd	256 dup(?)

a		ends
		end	start

Сразу говорю, что младшие 16 бит не обязательно точные - хотите отбрасывайте
их, а нет - и так сойдет

===============================================================================

Секция 3 из 6 - Предыдущая - Следующая

Вернуться в раздел "Программирование графики" - Обсудить эту статью на Форуме
Главная - Поиск по сайту - О проекте - Форум - Обратная связь

© faqs.org.ru