Changes between Initial Version and Version 1 of opengl-intro


Ignore:
Timestamp:
Feb 10, 2009, 7:25:08 PM (15 years ago)
Author:
Leon Kos
Comment:

Računalniška grafika v TeX obliki

Legend:

Unmodified
Added
Removed
Modified
  • opengl-intro

    v1 v1  
     1= Računalniška grafika =
     2
     3Leon Kos
     4
     5
     6  Predstavljeni so bistveni prijemi pri programiranju
     7  računalniške grafike z grafično knjižnico OpenGL. Ta
     8  dokument naj bi bil osnova za vaje pri predmetih RPK, OPK, PK in
     9  izdelavo seminarjev s tega področja. Ker študenti FS
     10  pridobijo znanje programiranja v jeziku Fortran, so primeri podani za
     11  ta jezik. To pa ne pomeni, da je teorija omejena le na ta jezik, saj
     12  brez bistvenih popravkov kode lahko pišemo tudi v jeziku C in C++.
     13
     14
     15= Uvod =
     16
     17Za modeliranje posebnih modelov običajno ni možno uporabiti
     18splošno namenskih grafičnih orodij. To se pokaže predvsem
     19pri vizualizaciji inženirskih preračunov. Rezultati modeliranja
     20običajno niso le funkcije ampak kompleksni objekti kot so grafi,
     21vodi, hierarhične strukture, animacije gibanja, mehanizmi,
     22kontrola poti, volumski modeli posebnih oblik, ...
     23
     24Skozi razvoj računalnikov so se uvajali različni standardi za
     25grafiko. Od začetnikov kot je GKS in njegovega naslednika PHIGS je
     26ostal le še spomin. To pa predvsem zaradi zahtevnosti
     27implementacije in zaprtosti kode. Kot edini odprti standard obstaja
     28le OpenGL, ki ga je najprej uvedel SGI na svojih grafično podprtih
     29delovnih postajah. Poleg OpenGL obstaja tudi Microsoftov Direct3D, ki
     30pa je omejen na PC računalnike z Windows in ni tako enostaven za
     31uporabo kot OpenGL, ki se je zaradi svoje odprtosti in zmogljivosti
     32uveljavil na vseh operacijskih sistemih in strojnih platformah.
     33
     34Jezik OpenGL je konstruiran kot strojno-neodvisen vmesnik med
     35programsko kodo in grafičnim pospeševalnikom. Strojna
     36neodvisnost jezika OpenGL poneni tudi to, da v specifikaciji jezika ni
     37podpore za nadzor okenskega sistema in dogodkov (\emph{events}) pri
     38interaktivnem programiranju. Za tak nadzor so za vsak operacijski
     39sistem izdelani vmesniki, ki povezujejo OpenGL stroj z okenskim
     40sistemom.
     41
     42Zaradi specifičnosti različnih okenskih sistemov (Windows,
     43Xwindow, MacOS, BeOS) je potrebno za vsak sistem uporabiti posebne
     44prijeme pri klicanje OpenGL ukazov. Da bi vseeno lahko posali
     45programe, s sicer omejeno funkcionalnostjo uporabniškega vmesnika,
     46se je izdelala knjižnica GLUT (GL UTility), ki vse razlike
     47med operacijskimi sistemi kompenzira in vpeljuje skupen način
     48manipuliranja z dogodki (\emph{events}). S knjižnico GLUT je tako
     49mogoče pisati prenosljive programe, ki so enostavni za
     50programiranje in dovolj zmogljivi za nezahtevne uporabniške
     51vmesnike.
     52
     53
     54= Enostavni OpenGL program =
     55
     56Osnovni jezik OpenGL je podan v knjižnici GL. Bolj zahtevni
     57primitivi se gradijo z knjižnico GLU (GL Utility) v kateri so
     58podprogrami, ki uporabljajo rutine GL. Rutine GLU vsebujejo več
     59GL ukazov, ki pa so splošno uporabni in so bili zato
     60standardizirani.
     61
     62\subsection{Dogodki}
     63Vsi okenski vmesniki delujejo na principu dogodkov (\emph{events}). To
     64so signali okenskega sistema, ki se pošiljajo programu. Naš
     65program je tako v celoti odgovoren za vsebino okna. Okenski sistem mu
     66le dodeli področje (okno) katero vsebino mora popolnoma
     67nadzorovati. Poleg dodeljenega področja pa okenski sistem
     68pošilja še sporočila našemu programu. Najbolj pogosta
     69sporočila so:
     70\begin{description}
     71\item[display] Prosim obnovi (nariši) vsebino okna. Več
     72  možnih primerov je, da se to zgodi. Lahko je drugo okno odkrilo
     73  del našega okna, okno se je premaknilo na omizju ali pa se je
     74  ponovno prikazalo po tem ko je bilo ikonizirano. Prestrezanje tega
     75  dogodek je obvezno saj mora prav vsak program poskrbeti, da se
     76  vsebina okna obnovi.
     77\item[reshape] Velikost/oblika okna se je spremenila. Poračunaj
     78  vsebino okna za novo velikost. Ta dogodek se zgodi, kadar
     79  uporabnik z miško spremeni velikost okna.
     80\item[keyboard] Pritisnjena je bila tipka na tipkovnici.
     81\item[mouse] Stanje gumbov na miški se je spremenilo. Uporabnik je
     82  pritisnil ali sprostil enega od gumbov.
     83\item[motion] Uporabnik premika miško ob pritisnjenem gumbu.
     84\item[timer] Program zahteva sporočilo po preteku po določenega
     85  časa, da bo popravil vsebino okna. Primerno je za časovne
     86  simulacije.
     87\end{description}
     88Seveda poleg naštetih dogodkov obstajajo še drugi dogodki, za
     89katere lahko skrbi naš program. Ni pa potrebno da naš program
     90skrbi za vse naštete dogodke. Običajno mora program povedati
     91okenskem sistemu, za katere dogodke bo skrbel in za te dogodke mu bo
     92sistem tudi pošiljal sporočila.
     93
     94\subsection{GLUT}
     95Za abstrakcijo dogodkov
     96okenskega sistema v našem primeru skrbi knjižnica GLUT. Primer
     97minimalnega programa, ki nariše črto, je naslednji:
     98{\scriptsize\begin{verbatim}
     99subroutine display
     100include 'GL/fgl.h'
     101implicit none
     102call fglClear(GL_COLOR_BUFFER_BIT)
     103call fglColor3f(1.0, 0.4, 1.0)
     104call fglBegin(GL_LINES)
     105call fglVertex2f(0.1,0.1)
     106call fglVertex3f(0.8,0.8,1.0)
     107call fglEnd
     108call fglFlush
     109end
     110
     111program crta
     112external display
     113include 'GL/fglut.h'
     114call fglutInit
     115call fglutInitDisplayMode(GLUT_SINGLE + GLUT_RGB)
     116call fglutCreateWindow('Fortran GLUT program')
     117call fglutDisplayFunc(display)
     118call fglutMainLoop
     119end
     120\end{verbatim}
     121}
     122   
     123Fortranski program je sestavljen iz dveh delov: glavnega programa
     124\texttt{crta} in podprograma display. Z ukazom \texttt{fglutInit}
     125inicializiramo GLUT knjižnico podprogramov. Sledi zahteva po vrsti
     126okna. S konstantama GLUT\_SINGLE in GLUT\_RGB povemo, da želimo
     127okno z eno ravnino tribarvnega RGB prostora.  spremenljivka
     128\texttt{window} hrani številko okna, ki jo naredi
     129\texttt{fglutCreateWindow} in hkrati pove OS, kakšen naj bo napis
     130na oknu. Okenskemu sistemu moramo še dopovedati, katere dogodke bo
     131program prestrezal. Za podani primer je to le prikaz vsebine okna. S
     132klicem podprograma \texttt{fglutDisplayFunc} prijavimo OS, da naj
     133pošilja sporočila za izris, knjižnici GLUT pa s tem
     134dopovemo, da ob zahtevi za ponovni izris pokliče podprogram
     135\texttt{display}.
     136
     137Zadnji klic v glavnem programu je vedno \texttt{fglutMainLoop}. Ta
     138podprogram se konča le takrat se konča celoten program. Kot že
     139ime podprograma govori, je to glavna zanka programa, v kateri GLUT
     140sistematično sprejema sporočila in kliče naše
     141podprograme za dogodke. V podanem primeru je to le en tip dogodkov, ki
     142je tudi najbolj uporaben - \texttt{display}. Ostale tipe dogodkov pa
     143\texttt{fglutMainLoop} obdeluje na privzeti način, ki je lahko
     144preprosto ignoriranje sporočila ali pa klicanje vgrajenega
     145podprograma, ki obdela sporočilo. Izgled glavnega programa je tako
     146običajno zelo podoben za vse tipe GLUT programov v katerem si
     147sledijo ukazi v naslednjem zaporedju:
     148\begin{enumerate}
     149\item Vključi definicije konstant \texttt{GLUT} z ukazom
     150  \texttt{include 'GL/glut.h'}
     151\item Dopovej Fortranu, da so imena, kot je npr. \texttt{display},
     152  podprogrami in ne spremenljivke. To se naredi z ukazom \texttt{external}
     153\item Inicializiraj GLUT
     154\item Nastavi parametre okna (položaj, velikost, tip, bitne
     155  ravnine, pomnilnik)
     156\item Naredi okno in ga poimenuj
     157\item Prijavi podprograme, ki jih bo program izvajal ob
     158  dogodkih. Obvezno prestrezanje je le za \emph{display}. Ostali so poljubni.
     159\item Nastavi lastnosti OpenGL stroja. To so običajno ukazi
     160  \texttt{fglEnable} ali pa kakšna nastavitev luči, materialov
     161  in obnašanja GL stroja. V tem področju se običajno
     162  nastavi tudi ostale spremenljivke, ki niso neposredno vezane na
     163  OpenGL, ampak na samo delovanje programa, ki poleg prikaza dela
     164  še kaj drugega.
     165\item Zadnji je klic \texttt{fglutMainLoop}, iz katerega se program
     166  vrne, ko zapremo okno. Ob tem glavni program konča.
     167\end{enumerate}
     168
     169
     170\subsection{Izris v jeziku OpenGL}
     171V podprogramu \texttt{display} se morajo torej nahajati ukazi, ki
     172nekaj narišejo v okno. To so ukazi v jeziku OpenGL ali krajše
     173v jeziku GL. Vsi podprogrami ali funkcije v GL imajo predpono pri
     174imenu \emph{gl} oziroma za Fortran \emph{fgl}. Te predpone so potrebne
     175zaradi možnega prekrivanja z imeni v drugih knjižnicah in
     176jezikih. Za razumevanje jezika se lahko funkcije razlaga brez teh
     177predpon, saj je OpenGL koncipiran tako, da so tipi argumentov za vse
     178jezike podobni. Za posamezen jezik se predpostavi prefiks (za Fortran
     179je to \texttt{fgl}, za C pa \texttt{gl})
     180
     181Podprogram \texttt{display} torej skrbi za izris vsebine okna. Z
     182ukazom \emph{Clear} se briše celotno področje okna.  Kaj
     183konkretno se briše povemo z argumentom. V našem primeru je to
     184\texttt{GL\_COLOR\_BUFFER\_BIT}, kar pomeni brisanje vseh točk v
     185barvnem pomnilniku.
     186
     187Z ukazom \emph{Color} se nastavi trenutna barva grafičnih
     188gradnikov, ki se bodo izrisovali v nadaljnih ukazih. Kot argument se
     189podaja barva v RGB komponentah. Običajno imajo GL ukazi na koncu
     190imena tudi oznako tipa argumentov, ki jih je potrebno podati pri klicu
     191podprograma. To je običaj za skoraj vse jezike. Tako imamo za
     192Fortran in za C pri podprogramih, kot končnico imena še oznako o
     193številu argumentov in tip argumentov. Podprogram \emph{fglColor3f}
     194torej pomeni, da zahteva podprogram tri argumente tipa \texttt{float},
     195kar je ekvivalentno v fortranu tipu \emph{real} ali \texttt{real*4}.
     196Najbolj uporabljane GL ukaze lahko torej podamo z različnimi tipi
     197argumentov za isti ukaz. Izbor tipa argumentov je odvisen od
     198programerja in njegovih zahtev. Tako so argumenti za isto funkcijo
     199izbrani glede na priročnost. Za podani primer imamo ukaz
     200\emph{Vertex} v dveh oblikah in isti tip argumentov. \texttt{Vertex2f}
     201pomeni, da podajamo kordinate vozlišča z dvema argumentoma.
     202Tipi argumentov (končnic) so naslednji:
     203\begin{description}
     204\item[f] V jeziku C float in real ali real*4 za Fortran
     205\item[d] double za C in real*8 za Fortran
     206\item[i] integer
     207\item[s] short integer v C-ju ali integer*2 za F
     208\end{description}
     209Namesto fiksiranega števila argumentov obstajajo tudi funkcije, ki
     210imajo podan argument v obliki vektorja. Za to se uporabi končnica
     211\texttt{v}. Nekaj primerov končnic:
     212\begin{description}
     213\item[3f] Sledijo trije argumenti realnih števil
     214\item[3i] Sledijo trije argumenti celih števil
     215\item[3fv] Sledi vektor treh realnih števil
     216\end{description}
     217
     218Ker je GL v osnovi 3D pomeni, da so argumenti če sta podani le dve
     219koordinati $x$ in $y$ v ravnini $z=0$. Torej je to ekvivalentno klicu
     220podprograma \emph{Vertex(x, y, 0)}. V našem primeru imamo
     221tudi nastavitev vozlišča z ukazom
     222\texttt{Vertex3f(0.8,0.8,1.0)}, ki podaja vse tri koordinate v
     223prostoru. Koordinata $z=1$ je torej podana, vendar je zaradi
     224privzetega začetnega ortografskega pogleda v prostor ravnine (x,y)
     225koordinata $z$ po globini neopazna. če pa bi bila projekcija
     226perspekvivna in ne ortogonalna, bi opazili tudi vpliv
     227koordinate $z$. Raznolikost tipov argumentov se pokaže ravno pri
     228podajanju vozlišč, saj obstajajo naslednjji podprogrami:
     229\emph{glVertex2d, glVertex2f, glVertex2i, glVertex2s, glVertex3d,
     230  glVertex3f, glVertex3i, glVertex3s, glVertex4d, glVertex4f,
     231  glVertex4i, glVertex4s, glVertex2dv, glVertex2fv, glVertex2iv,
     232  glVertex2sv, glVertex3dv, glVertex3fv, glVertex3iv, glVertex3sv,
     233  glVertex4dv, glVertex4fv, glVertex4iv, glVertex4sv}. In vse to za
     234en sam ukaz.
     235
     236Namen velikega števila istih podprogramov za isto funkcijo je
     237opustitev pretvarjanja tipov in s tem pisanje bolj razumljive
     238in hitrejše kode. V jeziku C++ ali Java, ki pa sam dodaja ustrezne
     239argumente k imenom funkcij, pa bi lahko obstajal le en podprogram (npr.
     240\emph{glVertex}), jezik pa bi sam dodal ustrezne končnice in s tem
     241klical ustrezno funkcijo.
     242
     243Izris grafičnih elementov risbe se v GL podaja med ukazoma
     244\emph{glBegin} in \emph{glEnd}.  Predmet risanja podamo kot argument v
     245ukazu \texttt{Begin}. Na splošno velja, da funkcije brez končnic
     246zahtevajo en sam argument s konstanto, ki je podana v \emph{header}
     247datoteki s končnico \texttt{.h} in se vključuje za začetek
     248programske enote s stavkom \texttt{include 'GL/fgl.h'}. Za fortran je
     249programska enota vsak podprogram, zato moramo pri vsakem
     250podprogramu, ki uporablja te konstante, na začetku dopisati
     251še vključevanje teh konstant. Za C je modul \emph{.c}
     252datoteka, in ni potrebno vključevanje definicij konstant za vsak
     253podprogram, kot je to nujno za Fortran.
     254
     255Vse te GL konstante, ki so napisane v \emph{fgl.h} in \emph{fglu.h}
     256imajo standardno predpono \texttt{GL\_} in je za vse jezike
     257enoznačen. Ker pa Fortran ne zahteva deklaracje spremenljivk in
     258ima implicitno definirane tipe je prav možno, da se zatipkamo pri
     259imenu konstante, kar za Fortran pomeni realno številko z
     260vrednostjo 0.0. Da se izognemo takim težavam, se priporoča
     261ukaz \emph{implicit none}, s katerim izključimo predpostavljene
     262tipe in moramo za vsako spremenljivko povedati, kakšnega tipa je.
     263žal pa F77 ne omogoča prototipov tako, da je še vedno
     264potrebna pazljivost, kakšne tipe podajamo kot argumente
     265podprogramom. Posebno to velja za podprograme \emph{GLU}, ki
     266običajno nimajo tako razvejanih možnosti argumentov, kot
     267knjižnica \emph{GL}.
     268
     269Zadnji ukaz \texttt{glFlush} dopove GL stroju naj vse te ukaze, ki jih
     270je sprejel do sedaj, spravi iz svojih internih pomnilnikov v okno
     271okenskega sistema. Ker imamo v našem primeru le enostaven izris,
     272smo se odločili le za en slikovni pomnilnik
     273(\texttt{GLUT\_SINGLE}), ki je primeren le za statične slike. Za
     274aplikacije pri katerih se vsebina zaslona pogosto spreminja, je
     275primerneje uporabiti okno z dvema grafičnima pomnilnikoma
     276\texttt{GLUT\_DOUBLE}. Prednost slednjega je v tem, da v en pomnilnik
     277rišemo, drugega pa prikazujemo. Rišemo v ravnino, ki je v ozadju.
     278Ob koncu risanja pa le zamenjamo ravnini. Ker pa je to odvisno od
     279sistema se ukaz za zamenjavo risalnih ravnin imenuje
     280\texttt{glutSwapBuffers}. Prednost takega načina se pokaže pri
     281animacijah.
     282
     283= Geometrijski primitivi =
     284Uporabiti je mogoče le enostavne primitive.  To pa predvsem zaradi
     285zahtevane hitrosti in enostavosti izdelave strojnega pospeševanja.
     286Ločimo tri vrste teh enostavnih primitivov:
     287\begin{itemize}
     288\item točke ali pike
     289\item črte
     290\item ploskvice konveksnega tipa
     291\end{itemize}
     292Bolj zahtevne predstavitve izvedemo z kombiniranjem teh primitivov. Tako
     293krivulje različnih tipov aproksimiramo z lomljenkami, površine
     294pa s ploskvicami. Za najbolj razširjene kompleksne tipe se že
     295nahajajo podprogrami v knjižnici \emph{GLU}. Obstajajo tudi
     296možnosti sestavljenih enostavnih gradnikov za črte in
     297ploskvice. Za črte poznamo tako naslednje možnosti:
     298\begin{description}
     299\item[GL\_LINES] Pari vozlišč podajajo posamezne segmente
     300\item[GL\_LINE\_STRIP] Zaporedje povezanih vozlišč podaja
     301  lomljenko
     302\item[GL\_LINE\_LOOP] Lomljenka se zaključi tako, da poveže
     303  prvo in zadnje vozlišče.
     304\end{description}
     305Konstante so podane kot argument za podprogram \emph{Begin}. Za
     306ploskve se podaja več točk. Najenostavnejše ploskve so
     307trikotniki. Možni so še ravninski štirikotniki in konveksni
     308ravninski mnogokotniki. Enostavne elemente lahko podajamo tudi v
     309pasovih in trikotnike v pahljačah:
     310\begin{description}
     311\item[GL\_TRIANGLES] Tri vozlišča za en trikotnik
     312\item[GL\_TRIANGLE\_STRIP] Pas trikotnikov. Tri vozlišča za
     313  prvi in nato vsako nadaljnje vozlišče k prejšnjemo
     314  trikotniku doda nov trikotnik.
     315\item[GL\_TRIANGLE\_FAN] Pahljača: vsako dodatno vozlišče
     316  naredi dodaten trikotnik v smislu dežnika.
     317\item[GL\_QUADS] Ravninski štirikotnik se podaja s štirimi
     318  vozlišči.
     319\item[GL\_QUAD\_STRIP] Dodatni štirikotniki gradijo pas z
     320  dodajanjem parov vozlišč.
     321\item[GL\_POLYGON] En sam konveksni mogokotnik poljubnega števila
     322  vozlišč.
     323\end{description}
     324Za nesestavljeni tipe gradnikov lahko med \emph{Begin} in \emph{End}
     325podamo tudi več vozlišč. Tip gradnika se pri pri tem
     326avtomatsko ponovi. Med \emph{Begin} in \emph{End} se lahko uporabljajo
     327še ukazi za barvo \emph{glColor} in normale \emph{glNormal}. Ti
     328ukazi nastavljajo trenutno stanje, ki velja za vsa naslednja
     329vozlišča. Primer podprograma za prikaz enega trikotnika v
     330ravnini je naslednji:
     331{\scriptsize
     332\begin{verbatim}
     333subroutine display
     334implicit none
     335include 'GL/fgl.h'
     336call fglClear(GL_COLOR_BUFFER_BIT)
     337call fglBegin(GL_TRIANGLES)
     338call fglColor3f(1.0, 0.0, 0.0)
     339call fglVertex2f(-1.0, -1.0)
     340call fglColor3f(0.0, 1.0, 0.0)
     341call fglVertex2f(0.0, 1.0)
     342call fglColor3f(0.0, 0.0, 1.0)
     343call fglVertex2f(1.0, 0.0)
     344call fglEnd
     345call fglFlush
     346end
     347\end{verbatim}
     348}
     349Pred vsako točko je podana še trenutna barva. Izrisani
     350trikotnik tako ni enotne barve ampak se njegova notranjost preliva iz
     351ene skrajne barve v drugo. Rezultat prikazuje slika \ref{fig:color-triangle}.
     352\begin{figure}[htbp]
     353  \centering
     354  \includegraphics[height=1.5in]{color-triangle}
     355  \caption{Trikotnik s podanimi barvami v vozliščih}
     356  \label{fig:color-triangle}
     357\end{figure}
     358Uporaba različnih barv v vozliščih  mogoče ni posebno
     359uporabna. Za podajanje normal pa je običajno potrebno, da so
     360normale v vozliščih različne. Različne normale v
     361vozliščih nastajajo povsod tam kjer imajo ploskvice skupen rob,
     362za katerega želimo, da ima gladek prehod. To pa je povsod tam, kjer
     363aproksimiramo ,,gladko`` površino z osnovnimi gradniki.  Slika
     364\ref{fig:normala} kaže splošno postavitev treh točk v
     365prostoru.
     366\begin{figure}[htbp]
     367  \centering
     368  \includegraphics{normal0}
     369  \caption{Normala}
     370  \label{fig:normala}
     371\end{figure}
     372Normalo za trikotnik
     373z vozlišči $\vec{r}_0, \vec{r}_1, \vec{r}_2$ izračunamo z
     374vektorskim produktom
     375$$ \vec{n} = \frac{  (\vec{r}_1-\vec{r}_0)\times (\vec{r}_2-\vec{r}_0) }
     376    {|(\vec{r}_1-\vec{r}_0)\times (\vec{r}_2-\vec{r}_0)|}
     377$$
     378   
     379Imenovalec zgornje enačbe je dolžina vektorja $\vec{n}$.
     380Normala je pravokotna na razliko vektorjev, ki podajajo
     381vozlišče gradnika.
     382
     383
     384= Geometrijske transformacije =
     385Osnova vseh grafičnih knjižnic so tudi osnovne geometrijske
     386transformacije, kot so: 
     387\begin{description}
     388\item[Translate(x, y, z)] Premik v smeri vektorja
     389\item[Rotate(fi, x, y, z)]  Rotacija za \emph{fi} stopinj okoli osi
     390  podane z  (x, y, z)
     391\item[Scale(x, y, z)] Skaliranje po posameznih oseh
     392\end{description}
     393Ukazi za transformacije se ne smejo pojavljati med \emph{Begin/End},
     394saj bi to pomenilo, da se transformacija spreminja med izrisom.
     395Geometrijske transformacije nam pomagajo pri modeliranju, saj lahko
     396podajamo vozlišča gradnikov v nekem poljubnem koordinatnem
     397sistemu. To je lahko svetovni koordinatni sistem ali lokalni
     398koordinatni sistem. Za primer izberimo izris krivulje $y(x)=\sin(x)$ v
     399jeziku GL. Kot smo že opazili, je prednastavljeno okno v GLUT
     400obliki kvadrata, velikosti (-1,-1) do (1,1). Vsega skupaj torej dve
     401enoti. V zaslonskih koordinatah je prednastavljena velikost okna
     402$300\times 300$ pikslov. Za nas je pomembno, da sinus narišemo v
     403mejah od -1 do 1. Vzemimo primer, ko predvidimo število točk.
     404Podprogram za izris je naslednji:
     405
     406{\scriptsize\begin{verbatim}
     407subroutine display
     408include 'GL/fgl.h'
     409call fglClear(GL_COLOR_BUFFER_BIT)
     410call fglBegin(GL_LINE_STRIP)
     411do i=0,10
     412  y = sin((i-5)/5.0*3.14)
     413  call fglVertex2f((i-5)/5.0, y/3.14)
     414end do
     415call fglEnd
     416call fglFlush
     417end
     418\end{verbatim}}
     419Da smo spravili naših 11 točk lomljenke v okvir -1, 1 je bilo
     420potrebno premakniti koordinatni sistem osi $x$ za 5, ga nato še
     421skalirati tako, da smo iz območja [0,10] dobili območje [-3.14,
     4223.14]. čeprav smo za izračun koordinate y potrebovali na osi x
     423območje [-3.14, 3.14] pa je potrebna os $x$ za izris v območju
     424[-1,1]. Zato pri izrisu podajamo os $x$ tako, da ponovno
     425poračunavamo območje [0,10] v območje [-1,1], tako da \texttt{i}-ju
     426odštejemo 5 in delimo z 5. Lahko bi tudi delili s 5 in odšteli
     4271. Nekoliko bi poenostavili stvari, če bi imeli vsaj en kordinatni
     428sistem že takoj uporaben. Recimo os $x$. Zanka se nekoliko
     429poenostavi, še vedno pa je potrebno vse koordinate pomanjšati
     430za 3.14 oziroma poskalirati.
     431{\scriptsize\begin{verbatim}
     432do x=-3.14, 3.14, 0.6
     433y = sin(x)
     434call fglVertex2f(x/3.14, y/3.14)
     435end do
     436\end{verbatim}}
     437Bolj razumljivo bi bilo risati kar v lokalnem koordinatnem sistemu in
     438prednastaviti pomanjšavo modela. Za pomanjšavo uporabimo
     439ukaz za skaliranje, ki posamezne koordinate množi s konstanto 1/3.14,
     440preden se izriše. Podprogram za izris je naslednji:
     441{\scriptsize\begin{verbatim}
     442subroutine display
     443include 'GL/fgl.h'
     444call fglClear(GL_COLOR_BUFFER_BIT)
     445call fglScalef(1/3.14, 1/3.14, 1.0)
     446call fglBegin(GL_LINE_STRIP)
     447do x=-3.14, 3.14, 0.6
     448y = sin(x)
     449call fglVertex2f(x, y)
     450end do
     451call fglEnd
     452call fglFlush
     453end
     454\end{verbatim}}
     455Prednost takega načina razmišljanja se pokaže, že ko
     456želimo pod sinusom narisati še krivuljo kosinusa. Seveda ni
     457možno obeh krivulj risati z \emph{GL\_LINE\_STRIP} v isti
     458zanki. Zato se odločimo za ponovno risanje v lokalnem
     459koordinatnem sistemu in prednastavimo pomik navzdol za 1.5 enote.
     460{\scriptsize\begin{verbatim}
     461subroutine display
     462include 'GL/fgl.h'
     463call fglClear(GL_COLOR_BUFFER_BIT)
     464call fglScalef(1/3.14, 1/3.14, 1.0)
     465call fglBegin(GL_LINE_STRIP)
     466do x=-3.14, 3.14, 0.6
     467y = sin(x)
     468call fglVertex2f(x, y)
     469end do
     470call fglEnd
     471call fglTranslatef(0.0, -1.5, 0.0)
     472call fglBegin(GL_LINE_STRIP)
     473do x=-3.14, 3.14, 0.6
     474y = cos(x)
     475call fglVertex2f(x, y)
     476end do
     477call fglEnd
     478call fglFlush
     479end
     480\end{verbatim}}
     481Podani program za kosinus ne nastavlja ponovno skaliranja, saj je ukaz
     482že pred tem nastavil pomanjšavo. Translacija za -1.5 se izvede
     483v koordinatnem sistemu kosinusa. Splošen napotek za razumevanje
     484transformacije vsakega vozlišča je, da se za podano koordinato
     485upoštevajo transformacije, kot so napisane od spodaj na
     486vzgor. Transformacija, ki se izvede zadnja je torej napisana na prvem
     487mestu v programu. Tak način transformiranja točk nam
     488omogoča enostavnejše modeliranje. Koordinata $y$ kosinusa se
     489izračuna tako, da se pred izrisom najprej vsaki točki $y$
     490prišteje translacija -1.5 in potem se še izvede skaliranje
     491tako, da se ta vmesna točka pomnoži še z 1/3.14.
     492
     493
     494\subsection{Nadzor transformacijske matrike}
     495OpenGL pa za izračun koordinat ne hrani vse zgodovine posameznih
     496transformacij za nazaj, saj bi bilo to računsko potratno. Vse te
     497transformacije, ki jih v poljubnem zaporedju navajamo v programu,
     498popravljajo transformacijsko matriko. OpenGL ima le dve aktivni
     499transformacijski matriki, ki jih uporablja za poračun koordinat.
     500Prva matrika je modelna, druga pa je projekcijska. Mi bomo
     501uporabljali le modelno transformacijo in upoštevali, da
     502projekcijska matrika omogoča prikaz ravnine $(x,y)$ v področju
     503[-1,1]. Modelna matrika je tudi stalno aktivna, če se ne
     504izbere projekcijsko.
     505
     506Modelna matrika se ob vsakem klicu transformacijskega podprograma
     507popravi. Začetna oblika modelne matrike je enotska. Vsak klic
     508podprograma \emph{Translate}, \emph{Scale} in \emph{Rotate} pa matriko
     509popravi tako, da so upoštevane vse prejšnje transformacije in
     510nad njimi še novo podana transformacija. Matrika je torej stalna,
     511kar se kaže tudi v napaki prejšnjega programa za izris sinusa
     512in kosinusa, ki je pri vsakem ponovnem izrisu trikrat manjši. To
     513lahko preverimo tako, da okno prekrijemo s kakim drugim oknom in ga
     514potem ponovno odkrijemo. Kot že omenjeno, je na začetku
     515programa matrika enotska. S podprogramom \emph{fglLoadIdentity} na
     516začetku bi lahko to tudi zagotovili ob vsakem izrisu.
     517
     518Za bolj zahtevne transformacije je potrebno matriko začasno
     519shraniti in obnoviti. OpenGL ima v ta namen poseben pomnilnik v obliki
     520sklada, v katerega lahko shranjujemo trenutno
     521transformacijsko matriko. V pomnilniku oblike LIFO (\emph{Last In,
     522  First Out}) je prostora je za najmanj 32 matrik. Pomnilnik si lahko
     523predstavljamo kot hladilnik, v katerega shranjujemo matrike. Matriko, ki
     524jo želimo shraniti, potisnemo na začetek in to tako, da
     525vse ostale matrike potisnemo malo naprej na polici. Ko nekaj želimo
     526iz hladilnika, je to lahko le zadnja matrika. če
     527želimo predzadnjo, moramo poprej vzeti zadnjo. Za shranitev
     528trenutne matrike se uporabi \textbf{glPushMatrix}, za ponastavitev iz
     529sklada pa uporabimo \textbf{glPopMatrix}. Izkaže se, da je za
     530modeliranje taka oblika pomnilnika povsem primerna.
     531
     532Za primer vzemimo primer kocke sestavljene iz šestih ploskev. Za
     533izris kvadrata obstaja že krajša funkcija \texttt{glRectf(x1,
     534  y1, x2, y2)} za ravnino $z=0$. če želimo imeti kvadrat v
     535poljubni ravnini, pa uporabimo transformacije.
     536{\scriptsize
     537\begin{verbatim}
     538subroutine kvadrat(i)
     539real r(6), g(6), b(6)
     540data r /1,0,0,1,1,1/, g /0,1,0,1,0,0/
     541data b /0,0,1,0,1,1/
     542call fglPushMatrix
     543call fglColor3f(r(i), g(i), b(i))
     544call fglTranslatef(0.0, 0.0, 1.0)
     545call fglRectf(-1.0, -1.0, 1.0, 1.0)
     546call fglPopMatrix
     547end
     548
     549subroutine display
     550implicit none
     551include 'GL/fgl.h'
     552call fglClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT)
     553call fglPushMatrix
     554call fglRotatef(30.0, 1.0, 0.0, 0.0)
     555call fglRotatef(30.0, 0.0, 1.0, 0.0)
     556call fglScalef(0.5, 0.5, 0.5)
     557call kvadrat(1)
     558call fglRotatef(90.0, 0.0, 1.0, 0.0)
     559call kvadrat(2)
     560call fglRotatef(90.0, 0.0, 1.0, 0.0)
     561call kvadrat(3)
     562call fglRotatef(90.0, 0.0, 1.0, 0.0)
     563call kvadrat(4)
     564call fglRotatef(90.0, 1.0, 0.0, 0.0)
     565call kvadrat(5)
     566call fglRotatef(180.0, 1.0, 0.0, 0.0)
     567call kvadrat(6)
     568call fglPopMatrix
     569call fglFlush
     570end
     571
     572program kocka
     573external display
     574include 'GL/fglut.h'
     575include 'GL/fgl.h'
     576call fglutinit
     577call fglutInitDisplayMode(GLUT_SINGLE+GLUT_DEPTH)
     578call fglutCreateWindow('Fortran GLUT program')
     579call fglutDisplayFunc(display)
     580call fglEnable(GL_DEPTH_TEST)
     581call fglutmainloop
     582end
     583\end{verbatim}
     584} Podprogram \emph{kvadrat} je narejen tako, da riše transliran
     585kvadrat v ravnini $z=1$. To lahko razumemo kot nov primitiv, saj par
     586ukazov Push/Pop ne popravlja transformacije ob klicu podprograma.
     587šest stranic se riše z rotacijo osnovne stranice okoli osi y
     588in x. Modelna matrika se shani z začetnim ukazom \emph{Push} in
     589potem ponovno obnovi z ukazom \emph{Pop}.
     590
     591
     592\subsection{Globinski pomilnik}
     593Da se ploskve v prostoru pravilno izrisujejo tudi takrat, ko
     594rišemo ploskvice za drugimi, je potrebno uporabiti globinski
     595pomnilnik ali \emph{z-buffer}. To pa mora omogočati že sam
     596okenski sistem, zato je potrebno tak način prikaza zahtevati že
     597pri \texttt{fglutInitDisplayMode} in kasneje še dopovedati GL
     598stroju, da poleg barve točk na zaslonu shranjuje še koordinato
     599$z$ v svoj pomnilnik. S tem pomnilnikom GL ob rasterizaciji lika za
     600vsako točko ugotovi, če je že kakšna točka po
     601 globini pred njim in jo zato ne riše. Z ukazom
     602\texttt{fglEnable(GL\_DEPTH\_TEST)} se zahteva izračunavanje
     603globine, ki jo je potrebno tako kot barvo pred vsakim začetkom
     604risanja pobrisati z ukazom \texttt{fglClear}.
     605
     606\begin{figure}[htbp]
     607  \centering
     608  \includegraphics{half-cube}
     609  \caption{Kocka brez spodnjega in zgornjega pokrova (kvadrat(5) in kvadrat(6)}
     610  \label{fig:half-cube}
     611\end{figure}
     612
     613če rišemo zaprte modele, potem notranjosti ni možno
     614videti. Primer odprtega modela kaže slika \ref{fig:half-cube}. V
     615takih primerih se ob uporabi prostorskega pomnilnika običajno kar
     616polovica ploskvic modela prekrije v celoti in kasneje na zaslonu ni
     617vidna. Skupna značilnost vseh teh ploskvic, ki se prekrijejo je,
     618da imajo normalo površine negativno ($n_z < 0$). Da se izogemo
     619nepotrebni rasterizaciji teh ploskvic, vključimo
     620\texttt{GL\_CULL\_FACE}. Da pa bo izločanje delovalo, mora imeti
     621GL podatek za normalo površine, ki jo je potrebno podati pred
     622podatki v vozliščih. Za pravilno delovanje globinskega
     623pomnilnika je potrebna tudi nastavitev projekcijske matrike kot je to
     624opisano v \S\ref{sec:viewing}.
     625
     626\subsection{Animacija}
     627\label{sec:animate}
     628Imejmo primer animacije vozil na avtocesti. Predstavljeno bo
     629cestišče v eno smer z dvema pasovoma, voznim in prehitevalnim.
     630Vozila imajo začetni položaj in hitrost. Opazujemo
     631vozišče dolžine 500 metrov. Hitrost vozila med vožnjo se ne
     632spreminja. Spreminja se le položaj vozil (x, y) na
     633cestišču, ki jih izriše  podprogram \texttt{vozilo}.
     634
     635{\scriptsize\begin{verbatim}
     636subroutine display
     637implicit none
     638include 'GL/fgl.h'
     639common /vozila/ y(5), v(5)
     640real y, v, pas
     641integer i
     642data y /0,50,120,170,200/
     643data v /50,30,45,31,33/
     644call fglClear(GL_COLOR_BUFFER_BIT)
     645call fglPushMatrix
     646call fglRotatef(-45.0, 0.0, 0.0, 1.0)
     647call fglTranslatef(0.0, -1.0, 0.0)
     648call fglScalef(0.004, 0.004, 0.004)
     649call fglColor3f(0.0, 0.0, 0.0)
     650call fglRectf(-4.0, 0.0, 4.0, 500.0)
     651call fglTranslatef(0.0, -50.0, 0.0)
     652do i=1,5
     653   if (i.ne.5 .and. y(i+1)-y(i).lt.10.0) then
     654     pas=-2.0
     655   else
     656      pas = 2.0
     657   end if
     658   call vozilo(y(i), pas)
     659end do
     660call fglPopMatrix
     661call fglutSwapBuffers
     662end
     663
     664subroutine vozilo(y, pas)
     665call fglPushMatrix
     666call fglColor3f(1.0, 1.0, 1.0)
     667call fglTranslatef(pas, y, 0.5)
     668call fglRectf(-2.0, 0.0, 2.0, 6.0)
     669call fglPopMatrix
     670end
     671
     672subroutine ura(n)
     673common /vozila/ y(5), v(5)
     674real y, v, dt
     675dt = 0.1
     676do i=1,5
     677  y(i)=y(i)+v(i)*dt
     678end do
     679call fglutPostRedisplay
     680call fglutTimerfunc(100, ura, 0)
     681end
     682
     683program Mad Max
     684external display
     685external ura
     686include 'GL/fglut.h'
     687integer window
     688call fglutInit
     689call fglutInitDisplayMode(GLUT_RGB+GLUT_DOUBLE)
     690call fglutCreateWindow('Avtocesta')
     691call fglClearColor(0.0, 0.5, 0.0, 0.0)
     692call fglutDisplayFunc(display)
     693call fglutTimerFunc(100, ura, 0)
     694call fglutMainLoop
     695end
     696\end{verbatim}
     697}
     698
     699Pas predstavlja odmik v smeri $x$ od sredine cestišča. Vse
     700enote so v metrih. Vozilo je zaradi sorazmerja narisano
     701nekoliko večje. Za animacije je primernejša uporaba dvojnega
     702pomnilnika \texttt{GLUT\_DOUBLE}. S tem se izognemo težavam izrisa,
     703saj v trenutku, ko se zgornja plast izrisuje, nemoteno rišemo v
     704spodnjo plast. Ko je spodnja plast izdelana z ukazom
     705\emph{fglutSwapBuffers}, zamenjamo trenutni prikaz.
     706
     707Za animacijo, pri kateri je zahtevano točno časovno zaporedje je
     708primerno uporabiti uro (\emph{timer}), ki program opozori, da je
     709pretekel predpisani čas in da je potrebno izračunati nov
     710položaj vozil. V našem primeru je podana spremeba vsakih 100~ms
     711in zato nov položaj v smeri $y$ linearno narašča za $v(i)
     712dt$, kjer je hitrost podana v metrih na sekundo. Izbor 0.1s za premik
     713pomeni 1/0.1=10 posnetkov na sekundo, kar je spodnja meja pri
     714animacijah. Po poračunu novih položajev pošljemo
     715sporočilo \emph{fglutPostRedisplay}, da se na novo izriše
     716scena. Lahko bi tudi neposredno klicali \texttt{display}, vendar bi
     717bilo potem potrebno zagotoviti še kompenzacijo hitrosti, saj
     718že v podprogramu ura izgubimo nekaj časa pri izrečunu
     719novih položajev. Prostorski pomnilnik v tem primeru ni potreben,
     720saj je zagotovljeno, da se izrisi prekrijejo v pravilnem vrstnem redu.
     721
     722\subsection{Transformacije pogleda}
     723\label{sec:viewing}
     724Za zahtevnejše načine gledanja na model je potrebno nastaviti
     725projekcijo modela iz svetovnih koordinat v normalizirane oz. zaslonske
     726koordinate. V praksi obstajata dva načina projekcije: ortografska in
     727perspektivna. V tehniški predstavitvah se uporablja predvsem
     728paralelna oz. ortografska projekcija. Le v primeru animacije, kjer
     729želimo poudariti bližino in oddaljenost določenih
     730objektov, se uporablja tudi perspektivna projekcija.
     731\begin{figure}[htbp]
     732  \centering
     733  \includegraphics[width=3in]{viewing}
     734  \caption{Zaporedje pretvorbe koordinat vozlišč}
     735  \label{fig:viewing}
     736\end{figure}
     737
     738OpenGL ločuje projekcijsko matriko in modelno matriko zato,
     739da ni potrebno nastavljati projekcije pri vsakem izrisu.  Slika
     740\ref{fig:viewing} kaže zaporedje transformacij iz svetovnih
     741koordinat v zaslonske. Pri risanju modela običajno začnemo z
     742enotsko \emph{ModelView} matriko.
     743
     744Najpreprostejši način prikaza, kot je bil prikazan tudi v
     745dosedanjih primerih je, da stlačimo naš model s
     746transformacijami v privzete normalizirane koordinate [-1, 1]. Pri tem
     747načinu sta tako modelna kot projekcijska matrika enotski. Modelna
     748matrika je enotska le na začetku vsakega risanja, projekcijska pa
     749je konstantna ves čas. Pri takem načinu ni potrebno
     750preklapljati med trenutno aktivnima projekcijama. In če se
     751zadovoljimo s takim načinom, potem zadostuje tudi
     752privzeta zaslonska transformacija pri spremembi velikosti okna, ki je
     753pri sistemu GLUT le enovrstičen ukaz:
     754
     755{\scriptsize\texttt{call fglViewport (0, 0, width, height)}}
     756
     757Nekoliko zahtevnejša je sorazmerna sprememba,
     758ki ne bo anamorfično popravljala velikosti okna:
     759{\scriptsize\begin{verbatim}
     760subroutine  reshape (w, h)
     761integer w, h
     762implicit none
     763include 'GL/fgl.h'
     764common /viewport/ width, height
     765integer width, height
     766real*8 left, right, bottom, top, znear, zfar
     767width = w
     768height = h
     769if (w .ge. h) then
     770        left = -width/(1.0*height)
     771        right = width/(1.0*height)
     772        bottom = -1.0
     773        top = 1.0
     774else
     775        left = -1.0
     776        right = 1.0
     777        bottom = -height/(1.0*width)
     778        top = height/(1.0*width)
     779end if
     780znear = -1.0
     781zfar = 1.0
     782call fglViewport (0, 0, width, height)
     783call fglMatrixMode (GL_PROJECTION)
     784call fglLoadIdentity
     785call fglOrtho(left, right, bottom, top, znear, zfar)
     786call fglMatrixMode(GL_MODELVIEW)
     787end
     788\end{verbatim}
     789}
     790   
     791Predstavljeni podprogram se priporoča v uporabo za vse programe,
     792ki pripravljajo model v velikosti [-1,~1] za \emph{ModelView}. če
     793bi želeli dodati modelno transformacijo v \emph{reshape}, potem za
     794zadnjo vrstico dopišemo še modelno transformacijo in nato v
     795programu za izris pred začetkom le obnovimo stanje modelne
     796matrike. Primer animacije \ref{sec:animate} bi tako imel namesto
     797nastavitve modelne transformacije slednje v podprogramu \emph{reshape}.
     798Začetna nastavitev modelne matrike pred začetkom izrisa v
     799podprogramu \emph{display} pa bi bila:
     800{\scriptsize\begin{verbatim}
     801call fglClear(GL_COLOR_BUFFER_BIT)
     802call fglPushMatrix
     803... izris
     804call fglPopMatrix
     805\end{verbatim}
     806}
     807Takoj za brisanjem zaslona z ukazom \emph{Push} shranimo modelno
     808matriko in jo ob koncu ponovno nastavimo na začetno vrednost.
     809V podprogramu \emph{reshape}, pa modelno matriko popravljamo:
     810{\scriptsize\begin{verbatim}
     811call fglMatrixMode(GL_MODELVIEW)
     812call fglLoadIdentity
     813call fglRotatef(-45.0, 0.0, 0.0, 1.0)
     814call fglTranslatef(0.0, -1.0, 0.0)
     815call fglScalef(0.004, 0.004, 0.004)
     816\end{verbatim}
     817}
     818Tak pristop nekoliko jasneje predstavi program, saj so vse enote, s
     819katerimi manipuliramo, v programu za izris v svetovnih oz. modelnih
     820koordinatah. V spošnem se priporoča nastavitev projekcije za
     821vse modele, ki uporabljajo izris ploskev. To pa zaradi tega, ker je
     822privzeta projekcijska matrika enotska. Poglejmo to na primeru
     823paralelne projekcije \emph{glOrtho(l,r,b,,n,f)}:
     824$$
     825   PM = \left[
     826     \begin{array}{cccc}
     827       \frac{2}{r-l} &      0        &       0        & \frac{r+l}{l-r} \cr
     828               0     & \frac{2}{t-b} &       0        & \frac{t+b}{t-b} \cr
     829               0     &      0        & \frac{2}{f-n}  & \frac{f+n}{f-n} \cr
     830               0     &      0        &       0        &      1          \cr
     831     \end{array}
     832     \right]\quad .
     833$$
     834Za primer normalizacijskega prostora v obsegu [-1,1] je tako matrika paralelne
     835projekcije
     836$$
     837   PM(-1, 1, -1, 1, -1, 1) = \left[
     838     \begin{array}{cccc}
     839       1 &   0   &   0   & 0 \cr
     840       0 &   1   &   0   & 0 \cr
     841       0 &   0   &  -1   & 0 \cr
     842       0 &   0   &   0   & 1 \cr
     843     \end{array}
     844     \right]\quad ,
     845$$
     846kar se razlikuje od enotske prav v koordinati $z$. če bi
     847projekcijsko matriko ohranili enotsko, potem bi to pomenilo, da objekt
     848gledamo kot zrcalno sliko zadnje strani. Nastavitev projekcijske matrike
     849je torej obvezna za vse izrise ploskvic po globini, kot tudi za modele
     850z osenčenjem. Zaradi tega je tudi program za izris kocke
     851nelogično postavil v ospredje modro stranico in ne rdečo.
     852
     853= Osvetlitev =
     854Do sedaj predstavljeni primeri so uporabljali le sintetične barve.
     855To pomeni, da se barva vsake ploskvice ne spreminja v odvisnosti od
     856položaja v prostoru. Tak način prikaza je uporaben le za
     857omejen nabor prostorskih modelov. Neprimeren je že za vse modele,
     858ki imajo površine sestavljene iz primitivov in te površine
     859niso ravninske. Za primer kocke (slika \ref{fig:half-cube}) je bilo
     860potrebno za vsako stranico nastaviti svojo barvo, da smo lahko dobili
     861vtis prostora. če bi kocko risali le z eno barvo, potem bi dobili
     862na zaslon le obris.
     863
     864Za bolj realističen izris je potrebno vključiti računanje
     865osvetlitve.  žal osvetlitev zajema veliko parametrov, ki jih je
     866potrebno nastaviti preden lahko karkoli dobimo na zaslonu. Tako je
     867potrebno nastavljati položaj in lastnosti luči, osvetlitveni
     868model in lastnosti površin modelov. Za vsako luč se lahko tako
     869nastavi 10 lastnosti in vsaka površina ima 5 lastnosti materiala.
     870
     871Kot predpogoj za pravilno osvetljen model pa je podana normala v
     872vsakem vozlišču vsake ploskvice. Najpreprostejši način
     873pri uporabi osvetlitve je, da parametre luči ne nastavljamo in da
     874uporabimo nastavljanje lastnosti materiala površine le z ukazom za
     875barvo. S tem vpeljemo veliko  predpostavk, ki pa so za šolsko rabo
     876povsem uporabne. Predpostavljena je le ena luč bele svetlobe s
     877položajem $(0, 0, 1)$ in difuzni odboj svetlobe na
     878površini. Barvo površine podajamo kar z običajnim ukazom
     879za barvo. Program za izris osenčenega modela kocke je tako v
     880minimalni obliki naslednji:
     881
     882{\scriptsize\begin{verbatim}
     883subroutine kvadrat()
     884call fglPushMatrix
     885call fglTranslatef(0.0, 0.0, 1.0)
     886call fglNormal3f(0.0, 0.0, 1.0)
     887call fglRectf(-1.0, -1.0, 1.0, 1.0)
     888call fglPopMatrix
     889end
     890
     891subroutine display
     892implicit none
     893include 'GL/fgl.h'
     894call fglClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT)
     895call fglColor3f(0.7, 0.6, 0.2)
     896call fglPushMatrix
     897call fglScalef(0.5, 0.5, 0.5)
     898call kvadrat
     899call fglRotatef(90.0, 0.0, 1.0, 0.0)
     900call kvadrat
     901call fglRotatef(90.0, 0.0, 1.0, 0.0)
     902call kvadrat
     903call fglRotatef(90.0, 0.0, 1.0, 0.0)
     904call kvadrat
     905call fglRotatef(90.0, 1.0, 0.0, 0.0)
     906call kvadrat
     907call fglRotatef(180.0, 1.0, 0.0, 0.0)
     908call kvadrat
     909call fglPopMatrix
     910call fglFlush
     911end
     912
     913
     914program kocka
     915external display
     916external reshape
     917include 'GL/fglut.h'
     918include 'GL/fgl.h'
     919call fglutinit
     920call fglutinitdisplaymode(GLUT_SINGLE+GLUT_DEPTH)
     921call fglutcreatewindow('Osencena kocka')
     922call fglutDisplayFunc(display)
     923call fglutReshapeFunc(reshape)
     924call fglClearColor(1.0, 1.0, 1.0, 1.0)
     925call fglEnable(GL_LIGHTING)
     926call fglEnable(GL_LIGHT0)
     927call fglEnable(GL_DEPTH_TEST)
     928call fglEnable(GL_COLOR_MATERIAL)
     929call fglutMainLoop
     930end
     931
     932subroutine  reshape (w, h)
     933integer w, h
     934implicit none
     935include 'GL/fgl.h'
     936real*8 l
     937l = 1.0
     938call fglViewport (0, 0, w, h)
     939call fglMatrixMode (GL_PROJECTION)
     940call fglLoadIdentity
     941call fglOrtho(-l, l, -l, l, -l, l)
     942call fglMatrixMode(GL_MODELVIEW)
     943call fglLoadIdentity
     944call fglRotatef(30.0, 1.0, 0.0, 0.0)
     945call fglRotatef(30.0, 0.0, 1.0, 0.0)
     946end
     947\end{verbatim}
     948}
     949
     950Razširjeni primitiv smo poenostavili tako, da ne vsebuje več
     951definicije barve, ampak le geometrijo. Obvezno je bilo potrebno podati
     952izračun normale. Za naš primitiv kvadrata je to $(0,0,1)$.
     953Program za izris v bistvu ni spremenjen, le da je sedaj
     954transformacija modela preseljena v podprogram za nastavitev velikosti
     955okna \texttt{reshape}. V glavnem programu pa je potrebno najprej
     956vključiti računanje osvetlitve \emph{GL\_LIGHTING},
     957prižgati je potrebno luč št 0, ki ima začetni
     958položaj $(0,0,1)$.
     959
     960\begin{figure}[htbp]
     961  \centering
     962  \includegraphics[width=2.5in]{cube-l}
     963  \caption{Osenčen model kocke}
     964  \label{fig:cube-l}
     965\end{figure}
     966
     967Z vključitvijo \emph{GL\_COLOR\_MATERIAL}
     968pa poenostavimo podajanje barve za material površine tako, da vsi
     969klici podprogramov \emph{Color} nastavljajo privzeto difuzno in
     970ambientno barvo površine. Slika \ref{fig:cube-l} prikazuje
     971rezultat upodabljanja z osvetlitvijo.
     972
     973
     974
     975= Tekst =
     976OpenGL sam ne podpira teksta in je zato potrebno uporabiti razne
     977prijeme za izris teksta v prostoru. Možnih je več načinov
     978za risanje besedila:
     979\begin{description}
     980\item[stroke] črke so izrisane s črtami v prostoru modela
     981\item[bitmap] črke so izrisane na zaslon
     982\item[teksture] črke so izrisane rastrsko v prostoru modela
     983\end{description}
     984V šolskih primerih so najbolj uporabni že izdelani fonti v
     985knjižnici GLUT. Možne so naslednje številke fontov:
     986{\small
     987\begin{enumerate}
     988\item GLUT\_STROKE\_ROMAN
     989\item GLUT\_STROKE\_MONO\_ROMAN
     990\item GLUT\_BITMAP\_9\_BY\_15             
     991\item GLUT\_BITMAP\_8\_BY\_13             
     992\item GLUT\_BITMAP\_TIMES\_ROMAN\_10     
     993\item GLUT\_BITMAP\_TIMES\_ROMAN\_24     
     994\item GLUT\_BITMAP\_HELVETICA\_10       
     995\item GLUT\_BITMAP\_HELVETICA\_12       
     996\item GLUT\_BITMAP\_HELVETICA\_18       
     997\end{enumerate}
     998}
     999
     1000Za primer razširimo program za izris osenčene kocke z
     1001besedilom na vsaki stranici. Podprogram \emph{kvadrat} kot argument
     1002vzame besedilo. Začetek izpisa premakne za malenkost višje in
     1003začne v koordinati $x=-0.8$. Ker pa ne želimo, da se besedilo
     1004senči je tu potrebno izklapljanje senčenja takrat, ko
     1005izrisujemo posamezne črke. Ker so črke v vnaprej določeni
     1006velikost, jih je potrebno ustrezno pomanjšati s
     1007skaliranjem. Podprogram \texttt{fglutStrokeCharacter} po vsaki
     1008izrisani črti sam nastavi pomik v smeri x za širino izrisane
     1009črke.
     1010
     1011{\scriptsize\begin{verbatim}
     1012subroutine kvadrat(s)
     1013include 'GL/fgl.h'
     1014character s*(*), c
     1015call fglPushMatrix
     1016call fglTranslatef(0.0, 0.0, 1.0)
     1017call fglNormal3f(0.0, 0.0, 1.0)
     1018call fglRectf(-1.0, -1.0, 1.0, 1.0)
     1019call fglTranslatef(-0.8, 0.0, 0.01)
     1020call fglDisable(GL_LIGHTING)
     1021call fglScalef(0.003, 0.003, 0.003)
     1022call fglColor3f(1.0, 0.0, 0.0)
     1023lenc = len(s)
     1024do i=1,lenc
     1025  c = s(i:i)
     1026  call fglutStrokeCharacter(1, ichar(c))
     1027end do
     1028call fglEnable(GL_LIGHTING)
     1029call fglPopMatrix
     1030end
     1031
     1032subroutine display
     1033implicit none
     1034include 'GL/fgl.h'
     1035real mat(4)
     1036data mat /0.9, 0.6, 0.3, 1.0/
     1037call fglClear(GL_COLOR_BUFFER_BIT)
     1038call fglClear(GL_DEPTH_BUFFER_BIT)
     1039call fglPushMatrix
     1040call fglRotatef(30.0, 1.0, 0.0, 0.0)
     1041call fglRotatef(30.0, 0.0, 1.0, 0.0)
     1042call fglScalef(0.5, 0.5, 0.5)
     1043call fglMaterialfv(GL_FRONT, GL_DIFFUSE, mat)
     1044call kvadrat('Spredaj')
     1045call fglRotatef(90.0, 0.0, 1.0, 0.0)
     1046call kvadrat('Desno')
     1047call fglRotatef(90.0, 0.0, 1.0, 0.0)
     1048call kvadrat('Zadaj')
     1049call fglRotatef(90.0, 0.0, 1.0, 0.0)
     1050call kvadrat('Levo')
     1051call fglRotatef(90.0, 1.0, 0.0, 0.0)
     1052call kvadrat('Spodaj')
     1053call fglRotatef(180.0, 1.0, 0.0, 0.0)
     1054call kvadrat('Zgoraj')
     1055call fglPopMatrix
     1056call fglFlush
     1057end
     1058
     1059subroutine reshape(w, h)
     1060include 'GL/fgl.h'
     1061integer w, h
     1062real*8 l
     1063l = 1
     1064call fglViewPort(0, 0, w, h)
     1065call fglMatrixMode(GL_PROJECTION)
     1066call fglLoadIdentity
     1067call fglOrtho(-l,l,-l,l,-l,l)
     1068call fglMatrixMode(GL_MODELVIEW)
     1069call fglLoadIdentity
     1070end
     1071
     1072program crta
     1073external display
     1074external reshape
     1075include 'GL/fglut.h'
     1076include 'GL/fgl.h'
     1077call fglutinit
     1078call fglutinitdisplaymode(GLUT_SINGLE+GLUT_DEPTH)
     1079call fglutcreatewindow('Fortran GLUT program')
     1080call fglutDisplayFunc(display)
     1081call fglutReshapeFunc(reshape)
     1082call fglEnable(GL_DEPTH_TEST)
     1083call fglEnable(GL_LIGHTING)
     1084call fglEnable(GL_LIGHT0)
     1085call fglClearColor(1.0, 1.0, 1.0, 1.0)
     1086call fglutmainloop
     1087end
     1088\end{verbatim}
     1089}
     1090
     1091\begin{figure}[htbp]
     1092  \centering
     1093  \includegraphics[width=2.5in]{cube-f}
     1094  \caption{Osenčen model kocke z napisi}
     1095  \label{fig:cube-f}
     1096\end{figure}
     1097
     1098Podajanje barve za površino je spremenjeno tako, da se ne uporabi
     1099funkcije \emph{Color} ampak normalno funkcijo za podajanje lastnosti
     1100materialaf \texttt{glMaterialfv}. Rezultat kaže slika
     1101\ref{fig:cube-f}. če bi napisali komentar pred izrisom
     1102štirikotnika, potem bi bilo vidno besedilo tudi za ostale
     1103(skrite) strani.
     1104
     1105Včasih pa raje želimo, da se besedilo na zaslonu ne
     1106izrisuje rotirano in senčeno, temveč da se le pojavi na
     1107določenem položaju v prostoru in potem izriše v zaslonskih
     1108koordinatah. V ta namen uporabimo \emph{bitmap} fonte in naslednji
     1109podprogram za izpis besedila:
     1110{\scriptsize\begin{verbatim}
     1111subroutine output(x,y,z,s)
     1112character s*(*)
     1113call fglRasterPos3f(x,y,z)
     1114lenc = len(s)
     1115do i=1,lenc
     1116  call fglutBitmapCharacter(6, ichar(s(i:i)))
     1117end do
     1118end
     1119\end{verbatim}
     1120}
     1121Primer izrisa z bitmap fonti kaže slika \ref{fig:cube-b}
     1122\begin{figure}[htbp]
     1123  \centering
     1124  \includegraphics[width=2.5in]{cube-b}
     1125  \caption{Osenčen model kocke z \emph{bitmap} napisi}
     1126  \label{fig:cube-b}
     1127\end{figure}
     1128
     1129
     1130= Uporabniški vmesnik =
     1131V nadaljevanju so prikazani primeri programov, ki izkoriščajo
     1132dodatne funkcionalnost knjižnice GLUT za vnos dodatnih podatkov v
     1133program. To je predvsem uporaba tipk in miške.
     1134
     1135\subsection{Rotacija s tipkami}
     1136Rotiramo že vgrajeni geometrijski model čajnika s tipkami \textbf{x,
     1137y, z}. Vsak pritisk na tipko poveča kot rotacije za pet stopinj.
     1138Podatke o trenutni rotaciji prenašamo s poljem \texttt{common}. Ker
     1139izrisujemo žični model, podprogram za \emph{reshape} ni
     1140potreben. Podprogram \emph{keyboard} ob pritisku na tipko dobi tudi
     1141informacijo o zaslonskem položaju miške.
     1142
     1143{\scriptsize\begin{verbatim}
     1144subroutine display
     1145implicit none
     1146include 'GL/fgl.h'
     1147common /rotation/ rx, ry, rz
     1148real rx, ry, rz
     1149call fglClear(GL_COLOR_BUFFER_BIT)
     1150call fglColor3f(0.5, 0.4, 1.0)
     1151call fglPushMatrix
     1152call fglRotatef(rx, 1.0, 0.0, 0.0)
     1153call fglRotatef(ry, 0.0, 1.0, 0.0)
     1154call fglRotatef(rz, 0.0, 0.0, 1.0)
     1155call fglutWireTeapot(dble(r))
     1156call fglPopMatrix
     1157call fglutSwapBuffers
     1158end
     1159
     1160subroutine keyboard(key,x,y)
     1161common /rotation/ rx, ry, rz
     1162integer key,x,y
     1163print *, 'Key ', char(key),  key, ' at', x, y
     1164if (key .eq. ichar('x')) rx = rx + 5.0
     1165if (key .eq. ichar('y')) ry = ry + 5.0
     1166if (key .eq. ichar('z')) rz = rz + 5.0
     1167call fglutpostredisplay
     1168end
     1169
     1170program teapot
     1171external display
     1172external keyboard
     1173include 'GL/fglut.h'
     1174integer window
     1175call fglutInit
     1176call fglutInitDisplayMode(ior(GLUT_DOUBLE,GLUT_RGB))
     1177window = fglutCreateWindow('Use keys x, y, and z')
     1178call fglutDisplayFunc(display)
     1179call fglutKeyboardFunc(keyboard)
     1180call fglutMainLoop
     1181end
     1182\end{verbatim}
     1183}
     1184
     1185
     1186\subsection{Miška in inverzna projekcija}
     1187Za vsak pritisk gumba miške lahko dobimo poleg koordinate tudi
     1188še stanje gumbov. Naslednji primer prikazuje risanje črte v
     1189ravnini (x,y) s tem, da je potrebno zaslonske koordinate pretvoriti
     1190nazaj v modelne.
     1191{\scriptsize\begin{verbatim}
     1192subroutine redraw
     1193implicit none
     1194include 'GL/fgl.h'
     1195common /vertices/ n, vertex(2, 100)
     1196integer n, i
     1197real vertex
     1198call fglClear(GL_COLOR_BUFFER_BIT)
     1199call fglbegin(GL_LINE_STRIP)
     1200do i = 1,n
     1201        call fglVertex2f(vertex(1, i), vertex(2, i))
     1202end do
     1203call fglend
     1204call fglFlush
     1205end
     1206
     1207subroutine mouse (button, state, x, y)
     1208implicit none
     1209include 'GL/fglut.h'
     1210include 'GL/fgl.h'
     1211include 'GL/fglu.h'
     1212common /vertices/ n, vertex(2, 100)
     1213integer n, i
     1214real vertex
     1215integer button, state, x, y
     1216integer viewport(4)
     1217real*8 mvmatrix(16), projmatrix(16)
     1218real*8 wx, wy, wz               ! returned world x, y, z coords
     1219real*8 px, py, pz               ! picked window coortinates
     1220integer status
     1221
     1222if (button .eq. GLUT_LEFT_BUTTON) then
     1223 if (state .eq. GLUT_DOWN) then
     1224        call fglGetIntegerv (GL_VIEWPORT, viewport)
     1225        call fglGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix)
     1226        call fglGetDoublev (GL_PROJECTION_MATRIX, projmatrix)
     1227        note viewport(4) is height of window in pixels
     1228        px = x
     1229        py = viewport(4) -  y - 1
     1230        pz = 0.0
     1231        print *, ' Coordinates at cursor are ', px, py
     1232        status = fgluUnProject (px, py, pz,  mvmatrix,
     1233                    projmatrix,  viewport, wx, wy, wz)
     1234        print *, 'World coords at z=0.0 are ', wx, wy, wz
     1235        n = n + 1
     1236        vertex(1, n) = wx
     1237        vertex(2, n) = wy
     1238        call fglutPostRedisplay
     1239 end if
     1240end if
     1241end
     1242
     1243
     1244program main
     1245external redraw
     1246external mouse
     1247include 'GL/fglut.h'
     1248call fglutinit
     1249call fglutinitdisplaymode(ior(GLUT_SINGLE,GLUT_RGB))
     1250call fglutInitWindowSize (500, 500)
     1251call fglutInitWindowPosition (100, 100)
     1252window = fglutcreatewindow('Click in window')
     1253call fglutdisplayfunc(redraw)
     1254call fglutMouseFunc(mouse)
     1255call fglutmainloop
     1256end
     1257\end{verbatim}
     1258}
     1259
     1260
     1261
     1262\subsection{Kvaternionska rotacija}
     1263Naslednji program prikazuje vrtenje osenčenega čajnika z
     1264miško. V ta namem se uporabi že izdelam podprogram v jeziku C,
     1265ki ga kličemo iz fortrana in nam omogoča kvaternionsko
     1266rotacijo. Za vrtenje enotske krogle je potrebno zaznati tako
     1267začetni pritisk na gumb (podprogram \emph{mouse}) kot vse
     1268naslednje pomike miške (podprogram \emph{motion}).
     1269
     1270
     1271{\scriptsize\begin{verbatim}
     1272subroutine display
     1273implicit none
     1274include 'GL/fgl.h'
     1275common /quaternion/ last(4), cur(4)
     1276real last, cur, m(4,4)
     1277call build_rotmatrix(m, cur)
     1278call fglLoadIdentity
     1279call fglMultMatrixf(m)
     1280call fglclear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT)
     1281call fglutSolidTeapot(dble(0.5))
     1282call fglutSwapBuffers
     1283end
     1284
     1285subroutine motion (x, y)
     1286include 'GL/fglut.h'
     1287include 'GL/fgl.h'
     1288implicit none
     1289integer x, y
     1290common /quaternion/ last(4), cur(4)
     1291common /mousestart/ beginx, beginy
     1292common /viewport/ width, height
     1293integer width, height
     1294integer beginx, beginy
     1295real last, cur
     1296real p1x, p1y, p2x, p2y
     1297p1x = (2.0*beginx - width)/width
     1298p1y = (height - 2.0*beginy)/height
     1299p2x = (2.0 * x - width) / width
     1300p2y = (height - 2.0 * y) / height
     1301call trackball(last,p1x, p1y, p2x, p2y)
     1302call add_quats(last, cur, cur)
     1303beginx = x
     1304beginy = y
     1305call fglutPostRedisplay
     1306end
     1307
     1308subroutine mouse (button, state, x, y)
     1309implicit none
     1310integer button, state, x, y
     1311include 'GL/fglut.h'
     1312include 'GL/fgl.h'
     1313include 'GL/fglu.h'
     1314common /mousestart/ beginx, beginy
     1315integer beginx, beginy
     1316beginx = x
     1317beginy = y     
     1318end
     1319
     1320subroutine reshape(w, h)
     1321include 'GL/fgl.h'
     1322integer w, h
     1323real*8 l
     1324common /viewport/ width, height
     1325integer width, height
     1326width=w
     1327height=h
     1328l = 1
     1329call fglViewPort(0, 0, w, h)
     1330call fglMatrixMode(GL_PROJECTION)
     1331call fglLoadIdentity
     1332call fglOrtho(-l,l,-l,l,-l,l)
     1333call fglMatrixMode(GL_MODELVIEW)
     1334call fglLoadIdentity
     1335end
     1336
     1337program trackballdemo
     1338implicit none
     1339include 'GL/fglut.h'
     1340include 'GL/fgl.h'
     1341include 'GL/fglu.h'
     1342external display
     1343external motion
     1344external mouse
     1345external reshape
     1346integer window
     1347common /quaternion/ last(4), cur(4)
     1348real last, cur
     1349call trackball(cur, 0.0, 0.0, 0.0, 0.0)
     1350call fglutinit
     1351call fglutinitdisplaymode(GLUT_DOUBLE+GLUT_RGB+GLUT_DEPTH)
     1352window = fglutcreatewindow('Use mouse to rotate')
     1353call fglutdisplayfunc(display)
     1354call fglutmousefunc(mouse)
     1355call fglutmotionfunc(motion)
     1356call fglutreshapefunc(reshape)
     1357call fglEnable(GL_LIGHTING)
     1358call fglEnable(GL_LIGHT0)
     1359call fglEnable(GL_DEPTH_TEST)
     1360call fglutmainloop
     1361end
     1362\end{verbatim}
     1363}
     1364Predstavljeni program je sestavljen iz dveh delov. Kodo za rotacijo v
     1365jeziku C \texttt{trackball.c} uporabimo kod zunanje podprograme.
     1366Rezultat osenčenega model, ki je bil obrnjen z miško, prikazuje
     1367slika \ref{fig:teapot}.
     1368\begin{figure}[htbp]
     1369  \centering
     1370  \includegraphics[width=2.1in]{teapot}
     1371  \caption{Osenčen model čajnika}
     1372\label{fig:teapot}
     1373\end{figure}
     1374
     1375= Razvojno okolje =
     1376Za šolske probleme smo pripravili razvojno okolje, ki omogoča
     1377prevajanje kode v jezikih F77, C++ in C za okenski sistem Windows.
     1378Razvojno okolje deluje v načunu ukazne vrstice in nima
     1379priloženega integriranega vmesnika. Vsi ukazi za popravljanje
     1380programov in prevajanje se tako podajajo v ukazni vrstici DOS okna
     1381(\emph{Start-Programs-Command Prompt}).
     1382
     1383Osnova okolja je Borlandov C++ prevajalnik, ki ga lahko dobimo
     1384zastonj. Besedilo dogovora uporabe se nahaja v datoteki
     1385\emph{linence.txt}. Prevajanje v jeziku Fortran pa dosežemo z
     1386pretvorbo fortranske kode v C, nato sledi prevajanje v C-ju in
     1387povezovanje v končni program (\emph{.exe}). Končni program
     1388lahko zaženemo z DOS okna ali z dvoklikom na izvršni program.
     1389Poleg Novega grafičnega okna vsak GLUT program uporablja še
     1390konzolo za morebiten vnos ali izpis z ukazoma \texttt{print *,} ali
     1391\texttt{read *,}
     1392
     1393\subsection{Namestitev}
     1394Namestitev je možna z CD-ROMA ali datoteke
     1395\texttt{bcc-fgl-full.zip}. V slednjem primeru je potrebno paketno
     1396datoteko odpakirati v začasen imenik, nakar sledi namestitev tako
     1397kot iz CD-ROM-a.
     1398
     1399\begin{enumerate}
     1400\item Za namestitev je na disku C potrebnih 60 MB prostora!
     1401\item Dvolkikni na \texttt{install.bat}
     1402\end{enumerate}
     1403
     1404
     1405
     1406\subsection{Dokumentacija}
     1407Po namestitvi se navodila z nahajajo v imeniku
     1408\verb|c:\bcc55\doc|. Priližena so naslednja navodila v obliki PDF:
     1409\begin{description}
     1410\item[redbook-*.pdf] OpenGL Programming Guide
     1411\item[opengl-intro.pdf] Ta dokument
     1412\item[fgl.pdf] OpenGL reference
     1413\item[fglu.pdf] OpenGL Utility reference
     1414\item[fglut.pdf] GLUT
     1415\item[f2c.pdf] Prevajalnik za Fortran
     1416\end{description}
     1417
     1418V datoteki \texttt{} se nahaja ta dokument.  Iskanje
     1419po dokumentaciji za OpenGL ( \texttt{fgl.pdf, fglu.pdf}), izvedemo
     1420tako, da natipkamo npr.  \texttt{fglVertex3f(}
     1421
     1422
     1423\subsection{Prevajanje}
     1424Primeri so v imeniku \verb|c:\bcc55\examples|.
     1425
     1426Pred prevajanjem je potrebno odpreti okno DOS
     1427\emph{Start-Run-command-OK} in nastaviti pot do prevajalnikov z
     1428ukazom
     1429\begin{verbatim}
     1430        PATH=\BCC55\bin;%PATH%
     1431\end{verbatim}
     1432
     1433
     1434Pomik v imenik naredimo z ukazom
     1435\begin{verbatim}
     1436         c:
     1437         cd \bcc55\examples
     1438\end{verbatim}
     1439
     1440
     1441Pot lahko nastavimo tudi za celoten sistem:
     1442\emph{Start-Settings-Control Panel-System-evironment->PATH} in dopišemo
     1443\verb|c:\bcc55\bin;| na začetku ali koncu obstoječe poti.
     1444
     1445Za prevajanje fortranskih datotek ne smemo uporabiti končnice \texttt{.f}
     1446Primer prevajanja začetnega primera \texttt{line.f} za izris črte:
     1447\begin{verbatim}
     1448       f77 line       
     1449\end{verbatim}
     1450Za najzahtevnejši primer čajnika uporabimo hkratno prevajanje
     1451fortranskega in C programa, ki oba med seboj tudi poveže v program
     1452\texttt{tblight.exe}
     1453\begin{verbatim}
     1454       f77 tblight trackball.c       
     1455\end{verbatim}
     1456če imamo več modulov, potem lahko že prevedene podprograme
     1457\texttt{.obj} le povežemo v izvršno kodo. Primer:
     1458\begin{verbatim}
     1459       f77 tblight trackball.obj
     1460\end{verbatim}
     1461
     1462Prevajanje in povezovanje v jeziku C se izvede z klicem prevajalnika
     1463        \emph{bcc32}.  Primer:
     1464\begin{verbatim}
     1465        bcc32 teapot.c       
     1466\end{verbatim}
     1467
     1468
     1469\subsection{Urejanje fortranskih in C programov:}
     1470Na urejanje kode lahko uporabimo DOS-ov urejevalnik EDIT ali Windows
     1471notepad. Oba imata svoje slabosti; EDIT ima težave z daljšimi
     1472imeni datotek, NOTEPAD pa nima prikaza trenutne vrstice in ob prvem
     1473shranjevanju datoteke lepi končnico \texttt{.txt}, tako da moramo
     1474datoteko kasneje preimenovati v \texttt{.f}. Kljub slabostim sta oba
     1475urejevalnika primerna za šolske probleme.
     1476
     1477V trenutnem imeniku DOS okna odtipkamo izbrani ukaz:
     1478\begin{verbatim}
     1479        notepad teapot.f
     1480        edit teapot.f
     1481\end{verbatim}
     1482