ГЛАВА 12
Общий алгоритм удаления невидимых линий
Как и в предыдущих главах, мы полагаем, что объекты создаются программой "SCENE3", но теперь мы требуем, чтобы NOV вершин, принадлежащих изображаемым объектам, хранились в массивах X, Y, Z. Их перспективная проекция на визуальную плоскость хранится в массивах V и W. NOF граней хранятся в виде списка принадлежащих им вершин в массиве F (допускаются грани, содержащие не более шести вершин), в массиве H хранится число вершин каждой грани.
Мы считаем, что все объекты замкнуты. Не требуется выпуклость тел, однако мы предполагаем, что все их грани выпуклы, и они обходятся против часовой стрелки. При выполнении этих условий наблюдатель ни при каких условиях не может увидеть внутреннюю сторону грани, что означает, что при проекции на визуальную плоскость мы видим те грани, которые при проецировании сохраняют направление обхода вершин (против часовой стрелки). Строго говоря, это означает, что мы не можем рисовать плоские объекты. Если в какой-либо композиции требуется нарисовать такой объект, то каждую его грань следует записать дважды - один раз грань обходится по часовой стрелке, а другой раз - против, поэтому вне зависимости от положения глаза наблюдателя он видит один и только один из экземпляров каждой грани.
Мы также полагаем, что любой из отрезков, из которых состоит рисунок, получается в результате пересечения двух смежных граней: если требуется изобразить отдельную линию, то её следует представить, как вырожденную грань, ограниченную двумя отрезками. Эти ограничения введены для ускорения алгоритма удаления невидимых линий. Это очень важно, так как мы теперь приближаемся к предельным возможностям SPECTRUMa. Даже для такого простого рисунка, как два куба на рис. 40, требуется около пяти минут машинного времени, а для более сложных изображений ещё больше. SPECTRUM не был рассчитан на решение таких сложных задач, и то, что нам удается это сделать, следует считать показателем высокого качества работы проектировщиков SPECTRUMa.
Тем не менее, мы считаем, что важно изучить общий алгоритм удаления невидимых линий, это способствует повышению квалификации, так как читатель, желающий специализироваться в области машинной графики, обязан иметь представление о том, какие проблемы возникают при изображении трёхмерных объектов с невидимыми линиями. В программе, приведенной на листинге 12.1, реализован такой алгоритм удаления невидимых линий, который даёт отличные результаты на более мощных компьютерах. Если вы имеете доступ к более мощным компьютерам, то вам будет чрезвычайно полезно реализовать эту программу на таком компьютере.
Для того чтобы нарисовать изображение сцены, записанной в памяти в наблюдаемом положении, каждая линия, принадлежащая какому-либо из изображаемых объектов, должна сравниваться с каждой гранью. В силу принятых выше ограничений мы производим эти сравнения только для видимых граней, то есть тех, которые при проецировании сохраняют ориентацию против часовой стрелки.
Предположим, что типичная прямая Г3' в наблюдаемом положении соединяет две точки (X1',Y1',Z1') с (X2',Y2',Z2'), тогда координаты точки, лежащей на этой прямой, есть
(1-#)*(X',Y1',Z1')+#*(X2',Y2',Z2')
Предположим, что две концевые точки проецируются в точки (X1,Y1) и (X2,Y2) визуальной плоскости. Тогда Г3 проецируется на визуальную плоскость как прямая Г2, а координаты точек, лежащих на этой прямой есть
(1-MU)*(X1,Y1)+MU*(X2,Y2)
Обратите внимание, что ниоткуда не вытекает, что точка (1^)*(Xr,Yr,Z1')+ Ф*(X2',Y2',Z2') проецируется в точку (ЬФ^^Д^+Ф*^^), это означает, что Ф не равно MU.
Пусть грань общего положения ОМЕГА1 проецируется на область ОМЕГА2, лежащую в визуальной плоскости. Предположим, что H вершин проекции грани есть (XI,YI), где 1<=I<=H. Пусть 1-ое ребро ОМЕГА2 пересекает Г2 в точке (1- Л^^Д^Л!*^^!^^!)). Если ЛК0 или Ш>1, то это означает, что Г2 пересекает продолжение I-го ребра в точке, лежащей за пределами области ОМЕГА2: если 0<=Ш<=1, тогда Г2 пересекает область ОМЕГА2 в точке, лежащей на I-ом ребре. Так как проекция выпуклой грани выпукла, то число точек пересечения либо равно нулю, или равно двум (возможно совпадающим точкам).
Нам требуется рассмотреть только случай несовпадающих точек: предположим, что соответствующие этим точкам значения параметра MU есть MU(MIN) и MU(MAX), где MU(MIN)<MU(MAX). Тогда точки пересечения с Г2 есть
(1-MU(MIN))*(X1,Y1)+ MU(MIN)*(X2,Y2) и (1-MU(MAX))*(X1,Y1)+ MU(MAX)*(X2,Y2).
Теперь необходимо установить, является ли отрезок, заключенный между этими точками, видимым или нет. Это проверяется нахождением середины этого отрезка - точки
(X(MID),Y(MID))=(1-MU(MID))*(X1,Y1)+MU(MID)*(X2,Y2),
где
MU(MID)=(MU(MIN)+MU(MAX))/2.
Затем мы находим точку (X,Y,Z), лежащую на Г3 такую, что перспективной проекцией этой точки будет (X(MIN),Y(MIN)). Отрезок линии между точками, со значениями параметра MU, равными MU(MIN) и MU(MAX) является невидимым, если и только если точка (X,Y,Z) и глаз наблюдателя расположены по разные стороны плоскости, в которой лежит ОМЕГА3. Уравнения этой плоскости можно найти с помощью методов главы 1, а затем применить для проверки приведенного выше условия функциональное представление этой плоскости.
Обратите внимание, что X*PPD/Z=X(MID), Y*PPD/Z=Y(MID) и точка (X,Y,Z) лежит на Г3. Поэтому для некоторого значения параметра Ф:
Х=(1-Ф)*Х1'+Ф*Х2', Y=(1-#)*Y1'+#*Y2' иZ=(1-#)*Z1'+#*Z2';
так как
X(MID)=((X1,+#*(X2,-X1,))*PPD)/(Z1,+#*(Z2,-Z1')) и Y(MID)=((Y1,+#*(Y2,-Y1,))*PPD)/(Z1,+#*(Z2,-Z1')),
то отсюда следует, что
#=(X(MID)*Z1,-X1,*PPD)/((X2,-X1,)*PPD-X(MID)*(Z2,-Z1,))= (Y(MID)*Z1,-Y1,*PPD)/((Y2,-Y1,)*PPD-Y(MID)*(Z2,-Z1,))
Это позволяет нам вычислить Ф, а следовательно и точку (X,Y,Z), которая в свою очередь используется для проверки того, видима ли часть отрезка Г2 или нет.
В алгоритме, реализованном в программе "HIDDEN" на листинге 12.1, каждая линия, принадлежащая какому-либо объекту, сравнивается со всеми видимыми гранями (теми, проекции которых обходятся против часовой стрелки). Обратите внимание, что мы считаем, что все объекты непрозрачны, то есть невозможно увидеть внутреннюю поверхность ни одной из граней, т.е. увидеть грань, проекция которой обходится по часовой стрелке. Информация о ребрах содержится в информации о гранях, причём каждое ребро встречается дважды, один раз оно проходит от точки IV1 к точке IV2. Для того чтобы избежать повторений, мы рассматриваем только такие ребра, у которых IV1<IV2. Мы сопоставляем ребра только с видимыми гранями, так в силу наложенных на объекты ограничений, если ребро заслонено от наблюдателя невидимой гранью, то оно должно быть отделено от наблюдателя так же и видимой гранью.
Предположим, что на некотором этапе процедуры сравнения для прямой Г2, соединяющей вершины K-ой грани IV1 и IV2, мы нашли NRL видимых подотрезков данного отрезка; мы полагаем, что NRL<50. Значения параметра MU, соответствующего концевым точкам M-того видимого отрезка, заносятся в массив L как L(1,M) и L(2,M).
В начале процедуры мы полагаем NRL=1, L(1,1)=0 и L(2,1)=1. что означает, что мы считаем прямую видимой. Если на этой стадии обнаруживается новый невидимый отрезок, определяемый значениями MU(MIN) и MU(MAX) (переменные МАХ и MIN), то эти значения заносятся в массив L, и NRL соответствующим образом изменяет своё значение.
Когда прямая сравнивается со всеми видимыми гранями, то в результате у нас NRL видимых отрезков, которые можно нанести на экран. Если в какой-нибудь момент NRL принимает значение 0, то это означает, что весь отрезок является невидимым, а следовательно можно прекратить процедуру сравнения для этого отрезка.
Пример 12.1
Теперь мы можем удалить невидимые линии на рис. 37; результат показан на рис. 40. Значение входных параметров: HORIZ=9, VERT=6, глаз наблюдателя расположен в точке (15,10,15), взгляд направлен в (0,0,0).
Рис. 40.
LISTING 12.1
7000 REM HIDDEN/GENERAL HIDDEN LINE ALGORITM
REM IN - NOV,NOL,PPD,R(4,4),X(NOV),Y(NOV),Z(NOV),V(NOV),W(NOV),H(NOF)
7001 F(6,NOF)
7010
7019 DEGENERATE
7020 7030 7040 7050 7060 7070 7080 7090 7100
7109
7110 7120 7130 7140 7150 7160 7170
7179
7180 7190
LET X1=V(I1) LET X2=V(I2) LET X3=V(I3) LET DY1=Y2-Y1 LET DY2=Y3-Y2
LET Y1=W(I1) LET Y2=W(I2) LET Y3=W(I3)
DIM L(2,50): DIM G(NOF): LET EPS=0.000001
REM CHECK ON I,TH PROJECTED FACET-ANTICLOCKWISE SET G(I)=1, CLOCKWISE OR SET G(I)=0. FOR I=1 TO NOF LET I1=F(1,I): LET I2=F(2,I): LET I3=F(3,I): LET DX1=X2-X1: LET DX2=X3-X2: LET G(I)=0
IF DX1*DY2-DX2*DY1>0 AND H(I)>2 THEN LET G(I)=1 NEXT I
REM FIND J,TH LINE ON THE EDGE OF I,TH FACET. FOR I=1 TO NOF LET HH=H(I): LET IV1=F(HH,I) LET X1=V(IV1): LET Y1=W(IV1) FOR J=1 TO HH LET IV2=F(J,I)
LET X2=V(IV2): LET Y2=W(IV2) IF IV1>IV2 THEN GO TO 8160 REM INITIALISE VARIABLES. LET NRL=1: LET L(1,1)=0: LET L(2,1)=1 LET CA=X2-X1: LET CB=Y1-Y2
7200 LET CC=-X1*CB-Y1*CA
7209 REM COMPARE THIS LINE WITH THE K'TH FACET.
7210 FOR K=1 TO NOF
7220 IF G(K)=0 THEN GO TO 8040
7230 IF K=1 THEN GO TO 8040
7240 LET in=H(K)
7249 REM LOOP TO FIND TWO POINTS OF INTERSECTION OF PROJECTED LINE WITH PROJECTED FACET. THESE POINTS SPECIFIED BY MU VALUES MIN AND MAX.
7250 LET MAX=-1: LET MIN=2 7260 LET JV1=F(in,K)
7270 LET VX1=V(JV1): LET WY1=W(JV1)
7280 LET S1=SGN(CA*WY1+CB*VX1+CC)
7290 FOR M=1 TO in
7300 LET JV2=F(M,K)
7310 LET VX2=V(JV2): LET WY2=W(JV2)
7320 LET S2=SGN(CA*WY2+CB*VX2+CC)
7330 IF S1=S2 THEN GO TO 7500
7340 LET XE=VX1-VX2: LET YE=WY1-WY2
7350 LET XF=VX1-X1: LET YF=WY1-Y1
7360 LET DISC=CA*YE+CB*XE
7369 REM IF LINE IS PARALLEL TO A LINE ON THE FACET THEN EXIT FACET LOOP.
7370 IF ABS DISC>EPS THEN GO TO 7440 7380 IF ABS CA>EPS THEN GO TO 7410 7390 IF ABS XF<EPS THEN GO TO 8040 7400 GO TO 7500
7410 LET LAMBDA=XF/CA
7420 IF ABS (XF+LAMBDA*CB)<EPS THEN GO TO 8040
7430 GO TO 7500
7440 LET LAMBDA=(CA*YF+CB*XF)/DISC
7449 REM IF LINE MISSES K'TH FACET THEN GO TO NEXT FACET.
7450 IF LAMBDA<-EPS THEN GO TO 7500 7460 IF LAMBDA>1+EPS THEN GO TO 7500 7470 LET MU=(YE*XF-XE*YF)/DISC
7479 REM A TRUE INTERSECTION SO UPDATE MAX AND MIN.
7480 IF MAX<MU THEN LET MAX=MU 7490 IF MIN>MU THEN LET MIN=MU 7500 LET S1=S2
7510 LET VX1=VX2: LET WY1=WY2
7520 NEXT M
7529 REM CHECK IF INTERSECTIONS LIE BETWEEN SPECIFIED END POINTS OF LINE.
7530 IF MIN>1 THEN GO TO 8040 7540 IF MAX<0 THEN GO TO 8040 7550 IF MAX>1 THEN LET MAX=1 7560 IF MIN<0 THEN LET MIN=0
7570 IF MAX-MIN<EPS THEN GO TO 8040
7579 REM CALCULATE XMID AND YMID
7580 LET MID=(MAX+MIN)*0.5: LET MUD=1-MID 7590 LET XMID=MUD*X1+MID*X2
7600 LET YMID=MUD*Y1+MID*Y2
7610 LET DENOM=PPD*(X(IV2)-X(IV1))-XMID*(Z(IV2)-Z(IV1))
7620 IF ABS DENOM<EPS THEN GO TO 7650
7629 REM CALCULATE PHI AND HENCE XHAT,YHAT,ZHAT.
7630 LET PHI=(XMID*Z(IV1)-PPD*X(IV1))/DENOM 7640 GO TO 7670
7650 LET DENOM=PPD*(Y(IV2)-Y(IV1))-YMID*(Z(IV2)-Z(IV1))
7660 LET PHI=(YMID*Z(IV1)-PPD*Y(IV1))/DENOM
7670 LET ZHAT=(1-PHI)*Z(IV1)+PHI*Z(IV2)
7680 LET FACT=ZHAT/PPD
7690 LET XHAT=XMID*FACT: LET YHAT=YMID*FACT
7699 REM CALCULATE COEFFICIENTS OF PLANE CONTAINING FACET - A,B,C,D.
7700 LET JV1=F(1,K): LET JV2=F(2,K): LET JV3=F(3,K) 7710 LET DX1=X(JV1)-X(JV2)
7720 LET DX3=X(JV3)-X(JV2)
7730 LET DY1=Y(JV1)-Y(JV2)
7740 LET DY3=Y(JV3)-Y(JV2)
7750 LET DZ1=Z(JV1)-Z(JV2) 7760 LET DZ3=Z(JV3)-Z(JV2)
7770 LET A=DY1*DZ3-DY3*DZ1
7780 LET B=DZ1*DX3-DZ3*DX1
7790 LET C=DX1*DY3-DX3*DY1
7800 LET D=A*X(JV1)+B*Y(JV1)+C*Z(JV1)
7810 LET S1=A*XHAT+B*YHAT+C*ZHAT-D
7819 REM IF FACET HIDES PART OF LINE THEN CHANGE THE L ARRAY.
7820 IF ABS S1 <EPS THEN GO TO 8040
7830 IF ABS (SGN S1+SGN D)<2 THEN GO TO 8040
7840 LET MORE=NRL
7850 FOR M=1 TO NRL
7860 LET R1=L(1,M): LET R2=L(2,M)
7870 IF (R1>MAX) OR (R2<MIN) THEN GO TO 7960
7880 IF (R1>=MIN) AND (R2<=MAX) THEN GO TO 7950
7890 IF (R1<MIN) AND (R2>MAX) THEN GO TO 7920
7900 IF (R1<MIN) THEN GO TO 7940
7910 LET L(1,M)=MAX: GO TO 7960
7920 LET MORE=MORE+1
7930 LET L(1,MORE)=MAX: LET L(2,MORE)=R2 7940 LET L(2,M)=MIN: GO TO 7960 7950 LET L(1,M)=-1 7960 NEXT M
7969 REM TIDY UP THE L ARRAY.
7970 LET NRL=0
7980 FOR M=1 TO MORE
7990 IF L(1,M)<=EPS THEN GO TO 8020
8000 LET NRL=NRL+1
8010 LET L(1,NRL)=L(1,M): LET L(2,NRL)=L(2,M) 8020 NEXT M
8030 IF NRL=0 THEN GO TO 8160 8040 NEXT K
8049 REM DRAW VISIBLE PARTS OF LINE.
8050 FOR K=1 TO NRL
8060 LET R1=L(1,K): LET R2=1-R1
8070 LET XP1=X1*R2+X2*R1
8080 LET YP1=Y1*R2+Y2*R1
8090 LET R1=L(2,K): LET R2=1-R1
8100 LET XP2=X1*R2+X2*R1
8110 LET YP2=Y1*R2+Y2*R1
8120 IF (ABS(XP1-XP2)<EPS) AND (ABS(YP1-YP2)<EPS) THEN GO TO 8150 8130 LET XPT=XP1: LET YPT=YP1: GO SUB MOVETO 8140 LET XPT=XP2: LET YPT=YP2: GO SUB LINETO 8150 NEXT K
8160 LET IV1=IV2: LET X1=X2: LET Y1=Y2 8170 NEXT J 8180 NEXT I 8190 RETURN
Мы используем программу "HIDDEN", приведенную на листинге 12.1, "LIB1", "LIB3" и подпрограммы "SCENE3" и "CUBE", приведенные на листинге 12.2. В этой последней версии подпрограммы "CUBE" мы привлекли все методы построения объектов, использующие операции с массивами. Мы умышленно используем куб в наших программах, так как это чрезвычайно простой объект, на котором удается легко продемонстрировать общие идеи трёхмерной графики. Теперь настало время усложнить сами объекты: если вы понимаете ограничения, накладываемые алгоритмами на объекты, то все идеи, изложенные ранее, остаются в силе. Пользователи с объёмом памяти 16К обнаружат, что столь сложные программы не помещаются в памяти. В этом случае программы следует разбить на независимые сегменты, а информация об объектах и массивы должны записываться на пленку.
LISTING 12.2
6000 REM SCENES/EXAMPLES
6010 DIM X(16): DIM Y(16): DIM Z(16)
6020 DIM V(16): DIM W(16): DIM H(12): DIM F(4,12)
6030 DIM A(4,4): DIM B(4,4): DIM R(4,4): DIM Q(4,4)
6040 LET CUBE=6500: LET HIDDEN=7000
6050 LET PPD=3*VERT: LET NOV=0: LET NOF=0
6059 REM PUT FIRST CUBE IN OBSERVED POSITION-(ACTUAL-SETUP).
6060 GO SUB IDR3: GO SUB LOOK3 6070 GO SUB CUBE
6079 REM COPY ACTUAL TO OBSERVED MATRIX INTO Q.
6080 FOR I=1 TO 4: FOR J=1 TO 4
6090 LET Q(I,J)=R(I,J)
6100 NEXT J: NEXT I: GO SUB IDR3
6109 REM CALCULATE SETUP TO ACTUAL MATRIX.
6110 LET TX=3: LET TY=1.5: LET TZ=2: GO SUB TRAN3: GO SUB MULT3
6119 REM RECOVER ACTUAL TO OBSERVED MATRIX.
6120 FOR I=1 TO 4: FOR J=1 TO 4 6130 LET A(I,J)=Q(I,J)
6140 NEXT J: NEXT I
6149 REM CALCULATE SETUP TO OBSERVED MATRIX.
6150 GO SUB MULT3
6159 REM PLACE SECOND CUBE AND DRAW HIDDEN LINE VIEW OF SCENE.
6160 GO SUB CUBE 6170 GO SUB HIDDEN 6180 RETURN
6500 REM CUBE/VERTICES AND FACETS (STORED)
6501 REM IN - PPD,NOV,VOF,R(4,4),(NOV),Y(NOV),Z(NOV),V(NOV),W(NOV),H(NOF), F(4,NOF)
6502 REM OUT - NOV,NOF,X(NOV),Y(NOV),Z(NOV),V(NOV),W(NOV),H(NOF),F(4,NOF) 6510 DATA 1,1,1,1,1,-1,1,-1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1 6520 DATA 1,2,3,4,5,8,7,6,1,5,6,2,2,6,7,3,3,7,8,4,4,8,5,1
6530 RESTORE CUBE
6539 REM EXTEND DATA BASE OF VERTICES IN OBSERVED POSITION.
6540 LET NV=NOV 6550 FOR I=1 TO 8
6560 READ XX,YY,ZZ: LET NOV=NOV+1
6570 LET X(NOV)=XX*R(1,1)+YY*R(1,2)+ZZ*R(1,3)+R(1,4) 6580 LET Y(NOV)=XX*R(2,1)+YY*R(2,2)+ZZ*R(2,3)+R(2,4) 6590 LET Z(NOV)=XX*R(3,1)+YY*R(3,2)+ZZ*R(3,3)+R(3,4)
6599 REM PERSPECTIVE TRANSFORM.
6600 LET V(NOV)=PPD*X(NOV)/Z(NOV) 6610 LET W(NOV)=PPD*Y(NOV)/Z(NOV) 6620 NEXT I
6629 REM READ A*ND EXTEND DATA BASE OF FACETS.
6630 FOR I=1 TO 6
6640 READ F1,F2,F3,F4: LET NOF=NOF+1 6650 LET H(NOF)=4
6660 LET F(1,NOF)=F1+NV: LET F(2,NOF)=F2+NV 6670 LET F(3,NOF)=F3+NV: LET F(4,NOF)=F4+NV 6680 NEXT I 6690 RETURN LISTING 12.3
6500 REM ICOSA/HEDRON
6501 REM IN AND OUT-SAME AS CUBE ABOVE.
6510 DATA 0,1,T,T,0,1,1,T,0,0,-1,T,T,0,-1,-1,T,0,0,1,-T,-T,0,1,1,-T,0,0,-1,-T, -T,0,-1,-1,-T,0
6520 DATA 1,3,2,1,2,4,1,4,8,1,8,6,1,6,3,2,3,5,2,9,4,4,12,8,8,11,6,3,6,7,2,5,9, 4,9,12,8,12,11,6,11,7,3,7,5,5,10,9,
6530 RESTORE ICOSA: LET T=(1+SQR 5)/2
6539 REM EXTEND DATA BASE OF VERTICES IN OBSERVED POSITION.
6540 LET NV=NOV 6550 FOR I=1 TO 12
6560 READ XX,YY,ZZ: LET NOV=NOV+1
6570 LET X(NOV)=XX*R(1,1)+YY*R(1,2)+ZZ*R(1,3)+R(1,4) 6580 LET Y(NOV)=XX*R(2,1)+YY*R(2,2)+ZZ*R(2,3)+R(2,4) 6590 LET Z(NOV)=XX*R(3,1)+YY*R(3,2)+ZZ*R(3,3)+R(3,4)
6599 REM PERSPECTIVE TRANSFORM.
6600 LET V(NOV)=X(NOV)*PPD/Z(NOV) 6610 LET W(NOV)=Y(NOV)*PPD/Z(NOV) 6620 NEXT I
6629 REM READ AND EXTEND DATA BASE OF FACETS.
6630 FOR I=1 TO 20
6640 READ F1,F2,F3: LET NOF=NOF+1 6650 LET H(NOF)=3
6660 LET F(1,NOF)=F1+NV: LET F(2,NOF)=F2+NV: LET F(3,NOF)=F3+NV 6670 NEXT I
6680 RETURN LISTING 12.4
6700 REM CUBOCT/AHEDRON
6701 REM IN AND OUT - SAME AS CUBE ABOVE.
6710 DATA 0,1,1,1,0,1,1,1,0,0,-1,1,1,0,-1,-1,1,0,0,1,-1,-1,0,1,1,-1,0,0,-1,-1,-1,0,-1,-1,-1,0
6720 DATA 1,2,4,8,1,6,7,3,2,3,5,9,4,9,10,12,5,7,11,10,6,8,12,11
6730 DATA 1,3,2,1,8,6,2,9,4,3,7,5,4,12,8,5,10,9,6,11,7,10,11,12
6740 RESTORE CUBOST
6750 LET NV=NOV
6760 FOR I=1 TO 12
6770 READ XX,YY,ZZ: LET NOV=NOV+1
6780 LET X(NOV)=XX*R(1,1)+YY*R(1,2)+ZZ*R(1,3)+R(1,4)
6790 LET Y(NOV)=XX*R(2,1)+YY*R(2,2)+ZZ*R(2,3)+R(2,4)
6800 LET Z(NOV)=XX*R(3,1)+YY*R(3,2)+ZZ*R(3,3)+R(3,4)
6810 LET V(NOV)=X(NOV)*PPD/Z(NOV)
6820 LET W(NOV)=Y(NOV)*PPD/Z(NOV)
6830 NEXT I
6840 FOR I=1 TO 6
6850 READ F1,F2,F3,F4: LET NOF=NOF+1 6860 LET H(NOF)=4
6870 LET F(1,NOF)=F1+NV: LET F(2,NOF)=F2+NV: LET F(3,NOF)=F3+NV: LET F(4,NOF)=F4+NV
6880 NEXT I 6890 FOR I=1 TO 8
6900 READ F1,F2,F3: LET NOF=NOF+1 6910 LET H(NOF)=3
6920 LET F(1,NOF)=F1+NV: LET F(2,NOF)=F2+NV: LET F(3,NOF)=F3+NV 6930 NEXT I 6940 RETURN Упражнение 12.1
Постройте композицию, состоящую из кубов, тетраэдров, пирамид, октаэдров, икосаэдров, кубических октаэдров. Для того, чтобы облегчить вашу задачу, на листингах 12.3 и 12.4 приведены конструирующие программы для кубооктаэдра и икосаэдра. Напишите конструирующую программу для октаэдра, и, пожалуй, ещё более сложную программу для ромбического додекаэдра. LISTING 12.5
6000 REM SCENES/TWO STARS HIDDEN LINES REMOVED 6010 DIM X(22): DIM Y(22): DIM Z(22) 6020 DIM V(22): DIM W(22): DIM H(36): DIM F(3,36) 6030 DIM A(4,4): DIM В(4,4): DIM R(4,4): DIM Q(4,4) 6040 LET STAR1=6500: LET STAR2=6700: LET HIDDEN=7000 6050 LET PPD=3*VERT: LET NOV=0: LET NOF=0
6059 REM PLACE FIRST STAR.
6060 GO SUB IDR3: GO SUB LOOK3 6070 LET A=6: GO SUB STAR1 6080 FOR I=1 TO 4: FOR J=1 TO 4 6090 LET Q(I,J)=R(I,J)
6100 NEXT J: NEXT I: GO SUB IDR3
6109 REM PLACE SECOND STAR.
6110 LET TX=5: LET TY=5: LET TZ=5: GO SUB TRAN3: GO SUB MULT3 6120 FOR I=1 TO 4: FOR J=1 TO 4
6130 LET A(I,J)=Q(I,J)
6140 NEXT J: NEXT I
6150 GO SUB MULT3
6160 LET A=4: GO SUB STAR2
6170 GO SUB HIDDEN
6180 RETURN
LISTING 12.6
6500 REM STAR1
6501 REM IN AND OUT - SAME AS CUBE ABOVE.
6509 REM STAR BASED ON A CUBE.
6510 DATA 1,1,1,1,1,-1,1,-1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,A,0,0,-A,0,0,0,A,0,0,-A,0,0,0,A,0,0,-A
6520 DATA
1,2,9,2,3,9,3,4,9,4,1,9,6,5,10,5,8,10,8,7,10,7,6,10,2,1,11,1,5,11,5,6,11,6,2,11,4,3,1 2,3,7,12,7,8,12,8,4,12,1,4,13,4,8,13,8,5,13,5,1,13,3,2,14,2,6,14,6,7,14,7,3,14
6530 |
RESTORE STAR1 |
|
|
6540 |
LET NV=NOV |
|
|
6550 |
FOR I=1 TO 14 |
|
|
6560 |
READ XX,YY,ZZ: LET NOV=NOV+1 |
|
|
6570 |
LET X(NOV)=XX*R(1,1)+YY*R(1,2)+ZZ*R(1,3)+R(1 |
,4) |
|
6580 |
LET Y(NOV)=XX*R(2,1)+YY*R(2,2)+ZZ*R(2,3)+R(2 |
,4) |
|
6590 |
LET Z(NOV)=XX*R(3,1)+YY*R(3,2)+ZZ*R(3,3)+R(3 |
,4) |
|
6600 |
LET V(NOV)=PPD*X(NOV)/Z(NOV) |
|
|
6610 |
LET W(NOV)=PPD*Y(NOV)/Z(NOV) |
|
|
6620 |
NEXT I |
|
|
6630 |
FOR I=1 TO 24 |
|
|
6640 |
READ F1,F2,F3: LET NOF=NOF+1 |
|
|
6650 |
LET H(NOF)=3 |
|
|
6660 |
LET F(1,NOF)=F1+NV: LET F(2,NOF)=F2+NV: LET |
F(3,NOF)= |
F3+NV |
6670 |
NEXT I |
|
|
6680 |
RETURN |
|
|
LISTING 12.7 |
|
|
6700 |
REM STAR2 |
|
|
6701 |
REM IN AND OUT - SAME AS CUBE ABOVE. |
|
|
6709 |
REM STAR BASED ON A TETRAHDRON. |
|
|
6710 |
DATA 1,1,1,1,-1,-1,-1,1,-1,-1,-1,1,-A,-A,-A, |
-A,A,A,A, |
A,A,A,A, A |
6720 |
DATA 2,1,8,3,2,8,1,3,8,1,2,7,4,1,7,2,4,7,2,3 |
,5,4,2,5, |
3,4,5,3,1,6,4,3,6,1, |
6730 |
RESTORE STAR2 |
|
|
6740 |
LET NV=NOV |
|
|
6750 |
FOR I=1 TO 8 |
|
|
6760 |
READ XX,YY,ZZ: LET NOV=NOV+1 |
|
|
6770 |
LET X(NOV)=XX*R(1,1)+YY*R(1,2)+ZZ*R(1,3)+R(1 |
,4) |
|
6780 |
LET Y(NOV)=XX*R(2,1)+YY*R(2,2)+ZZ*R(2,3)+R(2 |
,4) |
|
6790 |
LET Z(NOV)=XX*R(3,1)+YY*R(3,2)+ZZ*R(3,3)+R(3 |
,4) |
|
6800 |
LET V(NOV)=PPD*X(NOV)/Z(NOV) |
|
|
6810 |
LET W(NOV)=PPD*Y(NOV)/Z(NOV) |
|
|
6820 |
NEXT I |
|
|
6830 |
FOR I=1 TO 12 |
|
|
6840 |
READ F1,F2,F3: LET NOF=NOF+1 |
|
|
6850 |
LET H(NOF)=3 |
|
|
6860 |
LET F(1,NOF)=F1+NV: LET F(2,NOF)=F2+NV: LET |
F(3,NOF)= |
F3+NV |
6870 |
NEXT I |
|
|
6880 |
RETURN |
|
|
Пример 12.2 |
|
|
Теперь, когда мы убедились, что программы, удаляющие невидимые линии, работают чрезвычайно долго, нам следует произвести ряд экспериментов с этими программами. Для генерации изображения двух кубов, показанных на рис. 40, требуется пять минут. Это означает, что мы весьма ограничены в выборе объектов для наших рисун-ков. Тем не менее полезно попытаться создать изображение сложных объектов с помощью нашей программы, и если у вас есть доступ к более мощному компьютеру, то вы можете проверить, что наш алгоритм пригоден и для более мощных компьютеров, причем рисунки создаются значительно быстрее.
На листинге 12.5 приведена программа "SCENE3", а на листингах 12.6 и 12.7 приведены программы, генерирующие два звездообразных трёхмерных объекта (при генерации каждого из которых следует задать параметр A, определяющий длину лучей). Обе эти программы при построении используют формы куба и октаэдра. Изображение двух звёзд (кстати, очень эффектное) может быть получено, если задать следующие значения параметров: HORIZ=48, VERT=32, точка, в которой расположен глаз - (35,20,25), а взгляд направлен в точку (0,0,0).
Упражнение 12.2
Программа на листинге 10.1 проверяет, что порядок обхода вершин треугольной грани - против часовой стрелки. Эта программа была написана для выпуклых тел, содержащих начало координат. Переделайте программу с учётом общего случая. Это означает, что задаются две точки, лежащие по разные стороны от плоскости, содержащей одну из граней объекта. В одной из этих точек находится глаз наблюдателя, а другая точка, не обязательно являющаяся началом координат, - внутренняя точка тела. Используйте эту программу для проверки объектов программы 12.6 и 12.7.
Напишите программы, создающие объекты на основе форм октаэдра, кубического октаэдра, икосаэдра или додекаэдра. Всегда проверяйте правильность обхода вершин граней создаваемых вами объектов. Звездчатые тела можно строить на основе тел вращения, кроме того, необязательно ограничиваться только симметричными телами. Для несимметричного случая вам потребуется модификация программы 10.1. Если соблюдены все условия, налагаемые на изображаемые объекты, то программа 12.1 начертит любой из них.
Упражнение 12.3
Введите дополнительную информацию о создаваемых объектах. Наряду с информацией о вершинах и гранях, введите ещё один массив L, значения элементов L(1,1) и L(2,1) которого есть индексы двух граней, которые содержат
I-ое ребро, 1<=I<=NOL. После того измените алгоритм удаления невидимых линий так, чтобы в нём не рассматривались прямые, являющиеся пересечением двух невидимых граней, и не сравнивалась грань с прямыми, лежащими на этой грани.
Теперь вы понимаете, что мы приблизились к пределу возможностей SPECTRUMa. Если вы хотите продолжить изучение машинной графики, то вам следует использовать для этой цели более мощные компьютеры. Вам так же потребуется изучить способы организации структур данных, более подходящие для машинной графики, чем массивы. Например, вся сцена может храниться в виде связного списка указателей, каждый из которых в свою очередь указывает на связный список информации об объекте. Каждая грань может быть представлена в виде связного списка. На первый взгляд, кажется, что эта конструкция слишком громоздка, но на самом деле она чрезвычайно облегчает программирование таких алгоритмов, как алгоритм удаления невидимых линий. При такой структуре данных легко извлечь отношение между объектами и гранями.
После того, как вы усвоите эти идеи, вы можете перейти к изучению более сложных графических алгоритмов, таких, как алгоритм раскраски, мультипликации и затенения объектов.
В следующей главе мы рассмотрим более сложные методы символьной графики и введём один метод мультипликации трёхмерной графики.
Список программ
1. "LIB 1", "LIB3", листинг 12.1 ("HIDDEN") и 12.2 ("SCENES" и "CUBE"). Входные данные: HORIZ, VERT, (EX,EY,EZ), (XD,DY,DZ). Введите, например, следующие значения:
а) 9,6, (15,10,5), (0,0,0);
б) 9,6, (10,10,-10), (0,1,0).
2. "LIB 1", "LIB3", листинги 12.1 ("HIDDEN") и 12.2 ("SCENES"), загрузите в память программы 12.3 ("ICOSA") и 12.4 ("CUBOCT") и измените подпрограмму "SCENE3" следующим образом:
6010 DIM X(24): DIM Y(24): DIM 7(24)
6020 DIM V(24): DIM W(24): DIM P(34): DIM H(34)
6040 LET CUBOCT=6700: LET ICOSA=6500: LET HIDDEN=7000
6070 GO SUB ICOSA
6160 GO SUB CUBOCT
Входные данные: HORIZ, VERT, (EX,EY,EZ), (DX,DY,DZ). Введите, например,
а) 9, 6, (15,10,5), (0,0,0);
б) 9, 6, (-10,10,10), (0,1,0).
3. "LIBr, "LIB3", листинг 12.1 ("HIDDEN"), 12.5 ("TWOSTARS"), и 12.6 ("STAR1"), и 12.7 ("STAR2"). Входные данные: HORIZ, VERT, (EX,EY,EZ), (DX,DY,DZ). Попробуйте ввести, например,
а) 60, 40, (10,20,30), (0,0,0);
б) 90, 60, (-10-,20,-10), (0,1,0).