• No se han encontrado resultados

A popular component of many demo programs and even a few games is voxel spacing. Using this effect, the observer moves over an imaginary landscape with hills and valleys.

Although this is a very general description, many methods exist for displaying this effect. In fact, there is likely to be as many algorithms as programmers who have already tried this effect. The procedure we show here has no complex three-dimensional images, and relies instead on simple two-dimensional relationships. Basically, the program takes a map and tips it on its side so the observer views it at an angle from above. Altitude can now be represented by vertical lines of varying lengths.

The map, which depicts the high and low points of the landscape, is a simple 320 x 200 image with transitions as smooth as possible. Color 0 represents the lowest point and Color 255 the highest.

In practice, two-dimensional projections depend on a fairly basic principle. As we know, objects farther away appear smaller than objects closer to us. So when viewing a landscape through a frame (i.e., your computer monitor) you'll find that more distant objects can fit in the frame than closer objects because the distant objects are smaller. What you see will always be similar to the following illustration.

Example of a landscape drawn with voxel spacing

The rectangular frame shows the entire existing landscape from a bird's eye view. A trapezoid displays its visible portion. All you need to do now is display this trapezoid on a rectangular screen section. This rectangle must stretch the front horizontally so objects in this area appear larger. Any coordinates refer to a projection on the lower screen-half of Mode X.

The trapezoidal projection is formed by steadily decreasing the number of pixels that fit onto one line. So, the ratio of landscape size to screen-pixel size is continually decremented. The initial value is a 1:1 diagram, with 80 pixels to a line (this resolution is used for speed). As the image is drawn the ratio becomes smaller and smaller, until perhaps a 1:2 diagram is reached and only 40 pixels fit on the frontmost line.

In line-by-line display you must also remember the distance between lines always becomes smaller as you move backward. Therefore, you should continually increase the line spacing when drawing from top to bottom.

The only thing missing now is the altitude information for the image. As we mentioned, this involves taking the color for each pixel and arranging a corresponding number of pixels one on top of the other. The greater the color-value, the higher the pixel grows into a vertical line. This vertical structure also eliminates gaps arising from the ever increasing line-spacing toward the front.

To make the scenery more realistic, you'll need to add bodies of water. Simply take all colors below a certain value and set them to this value. This creates surfaces without a vertical structure.

The easiest way to generate the landscape is to use a fractal generator that can generate plasma-clouds. FRACTINT is used for this purpose. Simply generate a plasma-field of resolution 320 x 200 and then load the palette LANDSCAP.MAP (by pressing CL). The completed image can be saved with qS. The program VOXEL.PAS redraws the scene in a loop in response

to mouse-movement (control is through mouse):

PC

PCunderground

You can find VOXEL.PAS on the companion CD-ROM

{$G+}

Uses Crt,Gif,ModeXLib;

Var x,y:Integer; {Coordinates of the trapezoid}

Procedure Draw_Voxel;external; {$l voxel.obj}

Begin

asm mov ax,0; int 33h End; {reset mouse driver} Init_ModeX; {enable Mode X} LoadGif(‘landsc3’); {load landscape}

x:=195; {define start coordinate} y:=130;

Repeat

ClrX($0f); {clear screen} Draw_Voxel; {draw landscape}

Switch; {activate completed video page} WaitRetrace; {wait for retrace}

asm

mov ax,000bh {Function 0bh: read relative coordinates} int 33h sar cx,2 {Division by 2} sar dx,2 add x,cx add y,dx End; If x < 0 Then x:=0; If x > 130 Then x:=130; If y < 0 Then y:=0; If y > 130 Then y:=130; Until KeyPressed; {until key} TextMode(3);

End.

The central procedure is Draw_Voxel, found in Assembler module VOXEL.ASM: data segment

extrn vscreen:dword ;pointer to landscape data extrn x,y: word ;coordinates of trapezoid extrn vpage:word ;current video page data ends

code segment

assume cs:code,ds:data

;variables with fractional part (lower 8 bits): offst dd 0 ;current offset step dd 0 ;pixel size

row_start dd 0 ;beginning of current row row_step dd 0 ;distance from next row

r_count dw 0 ;counter for depth

shrink dw 0 ;correction on lower screen border row dd 0 ;current screen row number

vpage_cs dw 0 ;video page in the code segment

.386

public Draw_Voxel Draw_Voxel proc pascal

;shows landscape on current video page

;reads data from vscreen starting from position (x/y) mov ax,vpage ;note number of video page mov vpage_cs,ax

push ds

mov ax,0a000h ;load destination segment mov es,ax

mov ax,320 ;calculate offset in landscape imul y

add ax,x

lds si,vscreen ;take data from vscreen add si,ax ;add offset

shl esi,8 ;convert to fixed point number mov offst,esi ;initial values for pixel ... mov row_start,esi ... and row

mov step,100h ;first scaling factor 1 mov row,100*256 ;begin in screen row 100 mov row_step,14040h ;distance of rows 320,25 mov shrink,0 ;first no correction

mov r_count,160 ;number of rows to calculate

The first part initializes several important variables. We should mention Offst and row_start in particular. Both form a fixed-point value in Bits 8-31 and following the decimal point in Bits 0-7), which gives the offset of the current pixel or current line. After each pixel, Offst is incremented by Step, moving you one step further in the landscape; for example if Step equals 080h (= 0.5), a new pixel is read from the landscape every two steps.

row_start and row_step are similar: row_step contains 14040, or 320.25 in decimal notation. After each line this sets row_start to the beginning of the next line and at the same time takes care of the narrowing toward the front, by starting the next line a little farther to the right.

next_y:

mov eax,row ;get current (screen) row number mov ebx,eax ;store

shr eax,8 ;convert to whole number add eax,50 ;50 pixels down

imul eax,80 ;convert to offset

mov di,ax ;store as destination pointer cmp di,199*80 ;screen border exceeded ? jb normal

mov di,199*80 ;yes, then position on last row mov eax,row ;difference to bottom screen border shr eax,8

sub eax,149

mov shrink,ax ;and note as correction normal:

add di,vpage_cs ;add current video page imul ebx,16500 ;multiply row number by 1,007 shr ebx,14 ;calculate * 16500 / 16384 mov row,ebx ;and store

Here the program calculates an offset before each line. The variable row contains the current line on the screen, which is converted to an offset. This line number should not be confused with row_Offst, which represents the position within the landscape. row represents the position on the screen. Even if the calculated offset lies below the screen, drawing must still occur because the vertical lines of this pixel can still project into the screen. The program handles this special case by again placing the destination pointer di in the screen and shrinking the height of the bar (by the number of lines given in shrink).

Now the increasing space between screen lines comes into play. The current y-coordinate of the screen is found in the variable row, which is multiplied here by a factor of 1.007, so the lines continue to spread out as they move forward.

mov bp,80 ;number of pixels per row next_x:

mov esi,offst ;load current pixel offset shr esi,8 ;convert to whole number xor eax,eax

mov al,[si] ;load dot from landscape mov cx,ax ;store

cmp cx,99 ;color (=height) < 100 ja fill_bar

mov ax,99 ;then set to 99

fill_bar:

shl ax,5 ;vanishing point projection: height * 32 xor dx,dx

push bp

mov bp,r_count ;divides by the distance add bp,50

idiv bp pop bp

sub ax,shrink ;perform correction jbe continue ;if <= 0, don’t even draw

For each pixel, the color at the current offset is read and the water level set to a minimum value of 99. The counter z_count, which gives the distance of each line from the observer, is used to calculate the flight perspective. This process will be discussed in Chapter 7. Finally, the height (stored in al) is decremented by the value shrink, so the bottom screen border can be processed.

push di next_fill:

mov es:[di],cl ;Enter color

sub di,80 ;Address next higher line dec al ;Decrement counter jne next_fill ;Continue ? pop di

weiter:

inc di ;Address next byte on screen mov esi,step ;Get step size

add esi,offst ;Add up mov offst,esi ;and rewrite

dec bp ;Next pixel jne next_x

mov esi,row_step ;Move line-start add esi,row_start

mov row_start,esi

mov offst,esi ;Also reload pixel-offset

dec step ;Decrement scaling factor dec z_count ;Line counter continues jne next_y pop ds ret Draw_Voxel endp code ends end

The loop next_Fill now draws the vertical bar of the given size. The program then calculates the offset of the next pixel and, if a new line is beginning, its offset as well. In addition, the step size Step is decremented after each line to create the expansion in the foreground.