Grafika

Žádný počítačový jazyk si nemůže z důvodů přenositelnosti dovolit přímo do své definice zahrnout grafické operace, které jsou silne závyslé od HW a navíc dochází v této oblasti k masivnímu rozvoji technologií a žádné všeobecně uznávané grafické rozhraní neexistuje. Proto pokud nějaký program chce používat operace s grafikou musí použít nějakou grafickou knihovnu. Z hlediska programování je to jakakoli knihovna a proto s implementací nebývají žádné problémy. Ukážeme použití nejrozšířenejší nízkoůrovňové knihovny OpenGL ve fortranu. Není to ovšm jediná použitelná knihovna.

PgPlot

Nejsofistikovanější fortranovskou knihovnou je PgPlot [http://www.astro.caltech.edu/~tjp/pgplot/] od Tima Pearsona která umí všechny možné grafické operace včetne zobrazování obrázků, kreslení grafů a podobně. Všetně rozsáhlého manuálu je volně k dispozici.

DISLIN

Knihovna DISLIN [http://www.linmpi.mpg.de/dislin/] je o poznání jednodužší ale zase se může hodit pro jednoduché projekty. Volně k dispozici je ovšem jen verze pro f90.

OpenGL+GLUT

Obě uvedené knihovny jsou určené spíše pro neinteraktivní práci, vykreslují ale neumožňují modifikovat data a podobně. Naproti tomu OpenGL je nízkourovňová knihovna umožňující programovat jakékoli grafické aplikace bez omezení. Existují porty pro všechny možné jazyky. OpenGL i ostatní grafické knihovny pracuje na poněkud jiném principu, než jsme při obyčejném programování zvykli. Obyčejně všechny programy vykonávají příkazy od začátku do konce a pak skončí, maximálně se občas uživatele na něco zeptají. Ovšem tenhle model ovládání programů je zcela nepoužitelný v případě, ze máme aplikaci s menu a podobně, kde je běh programu různě měněn v závislosti například na uživatelově rozhodnutí.

Proto OpenGL pracuje jinak. Při startu se nejprve vytvoří procedury, které obhospodařují jednotlivé akce uživatele případě dalších zařízení. Na začátku se též obyčejně vykreslí do paměti grafické karty obrázek a pokud chce uživatel například myší otáčet obrázek pak jen grafická karta přepočítává základní objekt tak ja by jej viděl uživatel a data pak posílá na obrazovku. Program pak je vlastně sada příkazů pro HW procesor.

Ukázka, kterou uvedeme bude vykreslovat torus (pneumatiku) podle toho v jaké poloze je zrovna ukazatel myši na obrazovce. Dále bude ukazovat jeho souřadnice. Levé tlačítko myši ukáže jednduché, menu například umožňující ukončit program. Zajímavé je experimentovat s polohou a barvou světelných zdrojů které torus osvětlují.


! vzorova implmentace GL+GLUT

Module callback

use opengl_gl
use opengl_glu
use opengl_glut


implicit none

real :: px,py

contains 

subroutine init

use opengl_gl
use opengl_glu
use opengl_glut
character(len=20) :: out = "ahoj svete"
integer(glint) :: i,image_data(3*40*40)
contains 

subroutine init

use opengl_gl
use opengl_glu
use opengl_glut
character(len=20) :: out = "ahoj svete"
integer(glint) :: i,image_data(3*40*40)

call glclearcolor(0.0, 0.0, 1.0, 1.0) 
call glmatrixmode(GL_PROJECTION)
call glloadidentity()
call gltranslatef(-0.1,0.1,0.0)
call glrotatef(30.0,0.0,0.0,10.0)

call glnewlist(1,GL_COMPILE)
call glutsolidtorus(0.275_gldouble,0.45_gldouble,50,55)
call glendlist

call glLightfv(GL_LIGHT0, GL_DIFFUSE, (/1.0, 1.0, 0.0, 0.0 /))
call glLightfv(GL_LIGHT0, GL_POSITION, (/0.0, 0.0, 10.0, 10.0 /) )
call glEnable(GL_LIGHT0)
call glEnable(GL_LIGHTING)

call glMaterialfv(GL_FRONT, GL_AMBIENT, (/0.4, 0.4, 0.4, 0.0/))

end subroutine init

subroutine display

use opengl_gl
use opengl_glu
use opengl_glut

integer :: i
character(len=80) :: data

call glclear(GL_COLOR_BUFFER_BIT) 

write(data,*) px,py
call glrasterpos2f(-0.1,0.95)
do i = 1, len_trim(data)
   call glutbitmapcharacter(GLUT_BITMAP_TIMES_ROMAN_24,ichar(data(i:i)))
enddo

call glrotatef(px/100.0,00.0,10.0,0.0)
call glcalllist(1)
call glutswapbuffers
call glflush

end subroutine display

subroutine mouse(x,y)
integer(kind=glcint) , intent(in out) :: x,y
integer :: i

write(*,*) x,y
px = x+200!abs(x-150.0)+10.0
py = y+200!abs(y-150.0)+10.0

call display

end subroutine mouse

subroutine mainmenu(value)

integer, intent(inout) :: value

!write(*,*) "value = ",value

if( value == 1 ) then
   write(*,*) "help"
endif
if( value == 666 ) stop

end subroutine mainmenu

end Module callback

program zk1

use opengl_gl
use opengl_glu
use opengl_glut
use callback

integer(kind=glcint) :: iwin, imenu
integer(kind=glenum) :: itemp


call glutinit()
call glutinitdisplaymode(GLUT_DOUBLE + GLUT_RGB)

iwin = glutcreatewindow("zk1")

! menu
imenu = glutcreatemenu(mainmenu)
call glutaddmenuentry("Help",1)
call glutaddmenuentry("Quit",666)
call glutattachmenu(GLUT_RIGHT_BUTTON)

call init
call glutmotionfunc(mouse)
call glutdisplayfunc(display)

call glutmainloop()

end program zk1

Jak to celé funguje? Nejprve je třeba knihovnu i window system initializovat. To lze demonstrovat na jednoduchem programu:


Program InitGL

use opengl_gl
use opengl_glu
use opengl_glut

integer(kind=glcint) :: iwin, imenu
integer(kind=glenum) :: itemp


call glutinit()
call glutinitdisplaymode(GLUT_RGB)

iwin = glutcreatewindow("initgl")

call glutmainloop()

end program initgl

Tento program zinitializuje knihovnu v RGB modu a vytvoří prázdné okno se jménem initgl a spusti hlavní tzv. zpracovávač událostí. Ten je, zhruba řečeno, představován nekonečným cyklem ve kterém se testují stavy jednotlivých zařízení (klavesnice, myš) a v případě, že se například uživatel zmáčne klávesu tak vyvolá asociovanou funkci (viz dále). V tomto případě ovšem žádné nejsou a tak program nebude reagovat na nic a bude třeba jej nasilím sejmout.

Ovládání je realizováno pomocí tzv. callbacků což jsou funkce, které můžeme svázat s nejakou uživatelovou akcí. Přiřazení funkcí se děje pomocí glutmotionfunc(), glutdisplayfunc() funkcí které zaregistrují danou funci jako callback. Můžeme tak ošetřit jednotlivé akce uživatele. Například v subroutinw mouse kterým gl předává jako parametry polohu ukazatele myši v aktualním okně vypisujeme tyto souřadnice na stadradni vystup. Na základě těchto souřadnic pak můžeme otáčet s objektem.

Vlastní nadefinování a vytvoření objektu je v proceduře init, kde se v paměti GL vyrobí objekt a projekce ze které se uživatel dívá. Dále pak poloha světelného zdroje na osvětlení scény.

Většina počítačů ma nějakou podporu GL v sobě, většinou je to knihovna messa která umožňuje fungovat jako GL knihovna i na strojích kde GL neni k dispozici, dochází ovšem k podstatnému zpomalení. Fortran interface lze stahout z [http://math.nist.gov/f90gl/], kde jsou zdrojáky které po přeložení vytvoří jak fortranovsky interface tak moduly s daty tak aby šlo normálně používat OpenGL v F90. Nakonec uvádíme Makefile potřebný pro přeložení ukázkových programů.


OGLLIBDIR = -L$(HOME)/f90gl/lib 
#-L/usr/lib

# the directory containing the X11 libraries
X11LIBDIR = -L/usr/X11R6/lib/

# the fortran 90 libraries for OpenGL, including GLUT, GLU and OpenGL
F90GLUTLIB = -lf90glut -lf90GLU -lf90GL -lglut -lGLU -lGL

# the X11 libraries
X11LIB =   -lXt -lXmu -lXi -lXext -lX11 -lm

# the f90 compiler flag for specifying the location of MOD files
MODS = $(HOME)/f90gl/include/GL

# fortran 90 compiler and compiler flags
F90 = ifc
F90FLAGS = 

# fortran 90 compiler flag for fixed source form
FFIXED = -FI

#----------- end of user configuration parameters ------------

all: glkinds.o interf.o intrfglt.o intrfglu.o fwrapglt.o fwrap.o fwrapglu.o \
	zk zk1

zk: zk.f90
	$(F90) $(F90FLAGS) -o zk zk.f90 \
	$(OGLLIBDIR) $(F90GLUTLIB) $(X11LIBDIR) $(X11LIB)

zk1: zk1.f90
	$(F90) $(F90FLAGS) -o zk1 zk1.f90 \
	$(OGLLIBDIR) $(F90GLUTLIB) $(X11LIBDIR) $(X11LIB)

fwrapglt.o: 
	$(F90) $(F90FLAGS) -c $(MODS)/fwrapglt.f90 \
	$(OGLLIBDIR) $(F90GLUTLIB) $(X11LIBDIR) $(X11LIB)

fwrap.o: 
	$(F90) $(F90FLAGS) -c $(MODS)/fwrap.f90 \
	$(OGLLIBDIR) $(F90GLUTLIB) $(X11LIBDIR) $(X11LIB)

fwrapglu.o: 
	$(F90) $(F90FLAGS) -c $(MODS)/fwrapglu.f90 \
	$(OGLLIBDIR) $(F90GLUTLIB) $(X11LIBDIR) $(X11LIB)

glkinds.o: 
	$(F90) $(F90FLAGS) -c $(MODS)/glkinds.f90 \
	$(OGLLIBDIR) $(F90GLUTLIB) $(X11LIBDIR) $(X11LIB)

interf.o: 
	$(F90) $(F90FLAGS) -c $(MODS)/interf.f90 \
	$(OGLLIBDIR) $(F90GLUTLIB) $(X11LIBDIR) $(X11LIB)

intrfglt.o: 
	$(F90) $(F90FLAGS) -c $(MODS)/intrfglt.f90 \
	$(OGLLIBDIR) $(F90GLUTLIB) $(X11LIBDIR) $(X11LIB)

intrfglu.o: 
	$(F90) $(F90FLAGS) -c $(MODS)/intrfglu.f90 \
	$(OGLLIBDIR) $(F90GLUTLIB) $(X11LIBDIR) $(X11LIB)

clean:
	rm -f *.o *.d work.pc*