Hlavní navigace

Jazyk VRML 97: dokončení

17. 1. 2008
Doba čtení: 12 minut

Sdílet

V dnešní části našeho seriálu o grafických formátech a metaformátech dokončíme část věnovanou deklarativnímu jazyku VRML 97. Budeme se věnovat především způsobu použití senzorů a spoluprací VRML prohlížečů se skripty napsanými v některém podporovaném skriptovacím jazyce, typicky JavaScriptu.

Obsah

1. Senzory ve VRML
2. Standardní senzory
3. Ukázka využití dotykového senzoru
4. Senzory generující událost při posunu objektů
5. Ukázka použití senzoru pro posun objektu ve scéně
6. Spolupráce prohlížečů VRML se skriptovacími jazyky
7. Literatura a odkazy na Internetu
8. Obsah následující části tohoto seriálu

1. Senzory ve VRML

V předchozí části tohoto seriálu jsme si pověděli základní informace o takzvaných senzorech, zejména o jejich velkém významu při tvorbě plně interaktivních trojrozměrných scén, včetně jednoduchých her. Ve specifikaci VRML 2.0 resp. VRML 97 byla (oproti starší normě VRML) zavedena důležitá novinka – téměř každý uzel (node), tj. jeden geometrický objekt či skupina objektů, může přijímat či naopak generovat takzvané události (events).

Přijímací objekt, který je typicky ovládán pomocí skriptu, tj. programu napsaného například v hojně podporovaném JavaScriptu (samozřejmě je možné, že VRML prohlížeč dokáže přes své aplikační rozhraní použít i další jazyky – Javu, Python, Lua apod.), se nedozví pouze to, že nějaká událost nastala, ale i další informace o vzniklé události, například souřadnice posouvaného objektu nebo čas vzniku události. V tomto ohledu se tedy události v pojetí VRML do značné míry přibližují událostem používaným při programování běžného grafického uživatelského rozhraní (GUI).

Události jsou generovány takzvanými senzory, což jsou neviditelné „sledovací“ objekty umístěné do trojrozměrné scény, které by měly být svázány s některým viditelným objektem. Svázání (binding) dvou a více objektů většinou znamená, že jsou tyto objekty umístěny do stejného uzlu, například typu Transformation nebo (a to možná v praxi častěji) uzlu typu Group. To, které objekty ve skupině jsou mezi sebou propojeny, tj. posílají si události, je specifikováno příkazem ROUTE nebo externím skriptem. Tímto způsobem je dokonce možné propojovat i objekty ležící v rozdílných skupinách. Mezi základní senzory, které podle specifikace VRML 97 (2.0) mohou být ve scénách použity, patří standardní senzory (standard sensors) a poté senzory reagující na posun objektů po nějaké trajektorii či jejich rotaci, která bývá určitým způsobem omezena (dragging sensors). Všechny dostupné senzory jsou vypsány v následující tabulce:

Název senzoru Typ senzoru Popsán v kapitole
CylinderSensor dragging sensor čtvrté
PlaneSensor dragging sensor čtvrté
ProximitySensor standard sensor druhé
SphereSensor dragging sensor čtvrté
TimeSensor standard sensor druhé
TouchSensor standard sensor druhé
VisibilitySensor standard sensor druhé

2. Standardní senzory

Z tabulky uvedené v předchozí kapitole je patrné, že mezi standardní senzory patří ProximitySensor, TimeSensor, TouchSensor a VisibilitySensor. Pojďme si nyní jejich vlastnosti podrobněji popsat. Senzor typu TimeSensor představuje klasický časovač, který generuje (vysílá) události v zadaném časovém intervalu. Čas začátku a konce generování událostí je zadán pomocí Unixového času, tj. počtu sekund uběhlých od půlnoci dne 1.1.1970 (o tomto způsobu časování jsme se již zmiňovali při popisu uzlů se zdroji zvuku i uzlů specifikujících animované textury).

Kromě toho je možné pomocí dalších atributů zadat počet tiků časovače za sekundu (frekvenci) a také to, zda se má generování událostí opakovat i po dosažení konce časového intervalu. Ve skutečnosti je totiž implicitně konec časového intervalu nastavený na nulu, a pro mnoho aplikací by bylo obtížné či nemožné určit, kdy se má časovač vypnout. Syntaxe uzlu s TimeSensorem spolu s uvedením implicitních hodnot je následující:

TimeSensor {
    cycleInterval 1.0    # čas mezi jednotlivými tiky zadaný v sekundách
    enabled       TRUE   # povolení či zákaz generování událostí
                         # z tohoto senzoru
    loop          FALSE  # povolení či zákaz generování událostí
                         # po dosažení koncového času
    startTime     0      # počátek časového intervalu zadaný
                         # ve formátu Unixového času
    stopTime      0      # konec časového intervalu zadaný
                         # ve formátu Unixového času
} 

Senzor nazvaný příhodně TouchSensor je velmi užitečný, protože se při jeho použití generuje událost tehdy, pokud je kurzor myši nebo jiného 2D či 3D polohovacího zařízení (3D myš, spaceball, rukavice pro virtuální realitu) umístěn nad některým objektem patřícím do stejné skupiny, ve které se tento senzor nachází (tj. senzor i objekt leží ve stejném uzlu). Pomocí tohoto senzoru se dají vytvářet například vypínače ovládající světelné zdroje, řídící animace (například animované textury) či přehrávání zvukových souborů. Syntaxe zápisu tohoto senzoru je velmi jednoduchá:

TouchSensor {
    enabled       TRUE   # povolení či zákaz generování událostí
                         # z tohoto senzoru
} 

Senzor pojmenovaný VisibilitySensor vytváří resp. generuje událost tehdy, pokud jsou objekty (které jsou opět umístěné ve stejném nadřazeném uzlu) viditelné z pozice pozorovatele (kamery). Viditelnost či neviditelnost skupiny objektů je v obecných případech popsána dosti vágně a také se poměrně složitě zjišťuje (v OpenGL by například k tomuto účelu bylo možné použít speciální neviditelné buffery).

Z tohoto důvodu se ve VRML ve snaze o co největší urychlení a zjednodušení celého výpočtu uzavírají skupiny objektů do takzvaného obalového tělesa a následně se testuje viditelnost tohoto – po geometrické stránce velmi jednoduchého – tělesa, nikoli tedy viditelnost jednotlivých objektů. Tento senzor se používá například při optimalizaci vykreslování celé scény, mnohdy se několik senzorů tohoto typu zkombinuje, čímž vznikne složitější obalové těleso. Syntaxe zápisu tohoto senzoru je opět snadno pochopitelná, přibyly pouze dva atributy, pomocí kterých je možné nastavit střed a velikost kvádru, který představuje obalové těleso:

VisibilitySensor {
    enabled       TRUE   # povolení či zákaz generování událostí
                         # z tohoto senzoru
    center        0 0 0  # střed kvádru představujícího obalové těleso
    size          0 0 0  # velikost kvádru, tj. jeho šířka, výška a hloubka
} 

Podobný význam jako výše popsaný senzor VisibilitySensor má i ProximitySen­sor, který generuje událost tehdy, pokud se uživatel pohybuje v předdefinovaném kvádru, vstoupil do tohoto kvádru nebo z něj naopak vystoupil. Pohybem je myšlen buď skutečný pohyb libovolným směrem nebo otáčení uživatele na místě. Použití tohoto senzoru je různé: tvorba teleportů, spouštění či naopak zastavování animací při průchodu částí prostoru, tvorba automaticky zapínaných či vypínaných světel atd.

Důvod, který vedl k tomu, že se pro tento účel používá kvádr, je stejný, jako v případě senzoru VisibilitySensor – z geometrického hlediska se jedná o velmi jednoduché těleso, takže test, zda se uživatel nachází uvnitř či vně kvádru, je možné provádět velmi rychle, a to i při aplikaci lineární transformace. Geometrie kvádru je definovaná pozicí jeho středu a velikostí všech tří stran, tj. šířkou, výškou a hloubkou. Syntaxe zápisu tohoto senzoru je následující:

ProximitySensor {
    enabled       TRUE   # povolení či zákaz generování událostí
                         # z tohoto senzoru
    center        0 0 0  # střed kvádru představujícího vymezený prostor
    size          0 0 0  # velikost kvádru, tj. jeho šířka, výška a hloubka
} 

3. Ukázka využití dotykového senzoru

Ukažme si nyní, jakým způsobem je možné využít dotykový senzor. V trojrozměrné scéně popsané níže uvedeným zdrojovým kódem je vytvořeno těleso ve tvaru koule, ke kterému je připojen dotykový senzor. Pokud uživatel nějakým způsobem toto těleso vybere (typicky myší nebo VR rukavicí), vyvolá se událost, která spustí přehrávání zvuku. Vazba mezi zdrojem události, tj. senzorem a uzlem popisujícím zvuk, je provedena pomocí příkazu ROUTE, ve kterém je předepsáno, co se má při vzniku události stát – v tomto případě se změní začátek přehrávání zvuku, tj. zvuk se začne (znovu) přehrávat:

#VRML V2.0 utf8
# verze 2.0 vyzaduje kodovani UTF-8

# globalni informace o 3D scene
WorldInfo {
    title "3D scena s dotykovym senzorem"
    info ["Autor: Pavel Tisnovsky, 2008"
         "sireno pod licenci GPL "]
}

# nastaveni svetelneho zdroje
pointlight {
    color     1.0 1.0 1.0 # barva svetla
    intensity 1.0         # intenzita bodoveho svetla
    location  0.0 4.0 1.0 # pozice svetelneho zdroje
}

# nastaveni cerneho pozadi sceny
background {
    skycolor [0.0 0.0 0.0]
}

# skupina objektu s telesem a zvukovym zdrojem
Group {
    children [
        # senzor reagujici na dotyk
        DEF objTouchSensor TouchSensor { }
        # zvukovy soubor
        Sound {
            source DEF objAudioClip AudioClip {
                loop FALSE
                url "audio_clip.mid"
            }
        }
        Shape {
            # vzhled objektu - negeometricke informace
            appearance Appearance {
                # nastaveni sede barvy objektu
                # s odlesky
                material Material {
                    diffuseColor  0.7 0.7 0.7
                    specularColor 1.0 1.0 1.0
                }
            }
            # tvar objektu - geometricke informace
            geometry Sphere {
            }
        }
        # propojeni udalosti se zacatkem prehravani
        ROUTE objTouchSensor.touchTime TO objAudioClip.set_startTime
    ]
}

# ---------------------------------------------------------
# finito
# --------------------------------------------------------- 

4. Senzory generující událost při posunu objektů

Druhou skupinu senzorů tvoří senzory, které reagují na posun (drag) či rotaci objektů, tj. generují událost ve chvíli, kdy uživatel nějakým způsobem mění transformační matici objektů, které leží ve stejné skupině, v jaké se dragging sensor nachází. Od standardních senzorů se tedy dragging senzory odlišují především v tom, že uživatel může ve specifikovaném směru (tj. po ploše, plášti nebo základně válce či povrchu koule) měnit polohy či orientaci objektů a tím pádem i jejich vzájemné postavení. Opět se zde nabízí možnost vytváření jednoduchých her, komplikovanějších ovládacích prvků apod.

V minulosti bylo například navrženo plně 3D grafické uživatelské rozhraní, ve kterém byly standardní prvky GUI, tj. tlačítka, posuvníky, scrollbary a menu nahrazeny svými trojrozměrnými protějšky ovládanými pomocí programových skriptů volaných právě dotykovými senzory a dragging senzory (v určitém ohledu se jednalo o náhradu OpenInventoru za řešení postavené na skriptovacím jazyku). Tyto senzory jsou tří typů, jejichž význam je vysvětlen v následujících odstavcích.

PlaneSensor umožňuje uživatelům pohybovat objekty po rovině x-y, přičemž se jedná o lokální rovinu objektů, nikoli nutně rovinu určenou světovými souřadnicemi. Rozsah pohybu v rovině je možné omezit nastavením atributů maxPosition a minPosition, přičemž platí, že pokud je maximální zadaná pozice menší než minimální pozice, není rozsah pohybu žádným způsobem omezen (samozřejmě v rámci dané roviny). Syntaxe zápisu uzlu s tímto senzorem je následující:

PlaneSensor {
    enabled     TRUE    # povolení či zákaz tohoto senzoru
    offset      0 0 0   # počáteční pozice objektů
    autoOffset  TRUE    # určení, zda se má provádět absolutní posun či posun relativní k offsetu
    maxPosition -1 -1   # maximální hodnota souřadnic X a Y
    minPosition 0 0     # minimální hodnota souřadnic X a Y
} 

Vzhledem k tomu, že rovina obrazovky je obecně odlišná od lokální roviny objektů, je pohyb kurzoru myši na obrazovce automaticky mapován do lokální roviny x-y tak, jak je naznačeno na následujícím obrázku (ve skutečnosti je prováděna inverzní transformace ze souřadnic obrazovky na světové souřadnice a poté na lokální souřadnice):

7201

CylinderSensor se od předchozího typu senzoru odlišuje pouze v tom, že uživateli umožňuje přesun předmětů po povrchu (přesněji řečeno plášti nebo základně) válce spolu s jejich rotací. Jedná se o kombinaci čistého přesunu (PlaneSensor) a čisté rotace (SphereSensor). Volba pohybu po plášti nebo jedné základně je provedena na základě hodnoty úhlu diskAngle, který zásadním způsobem ovlivňuje celkové chování objektu při jeho transformaci na základě pohybu myši (viz následující dva obrázky). Pokud je prostorový úhel mezi bearing vektorem a osou y menší než nastavená hodnota diskAngle, je prováděn posun a rotace po základně válce, v opačném případě po jeho plášti. Syntaxe zápisu tohoto senzoru je podobná jako u předchozího typu senzoru:

CylinderSensor {
    enabled     TRUE    # povolení či zákaz tohoto senzoru
    offset      0       # počáteční pozice objektů
    autoOffset  TRUE    # určení, zda se má provádět absolutní posun či posun relativní k offsetu
    maxAngle    -1      # minimální povolený úhel natočení
    minAngle    0       # maximální povolený úhel natočení
    diskAngle   0.262   # mezní úhel určující způsob pohybu
} 
7202
7203

Posledním typem dragging senzoru je SphereSensor, který, jak již jeho název napovídá, dovoluje myší řízený pohyb koncového bodu řídicího vektoru po povrchu koule, přičemž orientace řídicího vektoru (jehož počátek leží ve středu koule) určuje rotaci příslušného objektu. Ve skutečnosti se tedy jedná o dvojí rotaci, tj. pohybem myši v horizontálním směru se objekt rotuje okolo lokální osy y a naopak pohybem myši ve směru vertikálním dochází k rotaci okolo lokální osy x. I syntaxe uzlu obsahujícího tento senzor se podobá předchozím dvěma typům senzorů, ovšem rozsah rotace se nijak neomezuje. Velmi důležité je u tohoto senzoru nastavení hodnoty autoOffset tak, aby bylo možné rotace sčítat (skládat):

SphereSensor {
    enabled     TRUE    # povolení či zákaz tohoto senzoru
    offset      0 1 0 0 # počáteční pozice objektů
    autoOffset  TRUE    # určení, zda se má provádět absolutní posun či posun relativní k offsetu
} 
7204

5. Ukázka použití senzoru pro posun objektu ve scéně

V demonstračním příkladu, jehož zdrojový kód je vypsán níže, je ukázáno, jakým způsobem je možné použít senzor PlaneSensor pro posun objektu, konkrétně šedé kuličky. Velikost roviny, po které je kulička posouvána, je omezena čtyřmi krajními body [-1, –1], [-1, 1], [1, –1] a [1, 1]. Propojení senzoru s vlastnostmi objektu (v tomto případě posunem kuličky) je provedena, podobně jako v příkladu předchozím, pomocí příkazu ROUTE.

#VRML V2.0 utf8
# verze 2.0 vyzaduje kodovani UTF-8

# globalni informace o 3D scene
WorldInfo {
    title "3D scena s dragging senzorem"
    info ["Autor: Pavel Tisnovsky, 2008"
         "sireno pod licenci GPL "]
}

# nastaveni svetelneho zdroje
pointlight {
    color     1.0 1.0 1.0 # barva svetla
    intensity 1.0         # intenzita bodoveho svetla
    location  0.0 4.0 1.0 # pozice svetelneho zdroje
}

# nastaveni cerneho pozadi sceny
background {
    skycolor [0.0 0.0 0.0]
}

# skupina objektu s telesem a dragging senzorem
Group {
    children [
        # dragging senzor pro posun objektu po plose
        DEF ts PlaneSensor {
            minPosition -1 -1
            maxPosition 1 1
        }
        DEF tr Transform {
            children Shape {
                # vzhled objektu - negeometricke informace
                appearance Appearance {
                    # nastaveni sede barvy objektu
                    # s odlesky
                    material Material {
                        diffuseColor  0.7 0.7 0.7
                        specularColor 1.0 1.0 1.0
                    }
                }
                # tvar objektu - geometricke informace
                geometry Sphere {
                }
            }
        }
        # propojeni udalosti se zmenou objektu
        ROUTE ts1.translation_changed to tr.set_translation
    ]
}

# ---------------------------------------------------------
# finito
# --------------------------------------------------------- 

6. Spolupráce prohlížečů VRML se skriptovacími jazyky

Některé VRML prohlížeče dokáží spolupracovat s externími programovacími jazyky nebo dokonce některý programovací jazyk (většinou skriptovací) přímo obsahují. Typickým skriptovacím jazykem se v tomto oboru stal JavaScript, přesněji řečeno ECMA Script, i když některé jeho vlastnosti nejsou ideální (například automatické vytváření členů tříd mnohdy vede ke zmatkům). Na dalším příkladu je ukázáno propojení VRML souboru s JavaScriptovým kódem, pomocí kterého je vytvořen objekt ve tvaru toroidu (anuloidu):

#VRML V2.0 utf8
# The VRML 2.0 Sourcebook
# Copyright [1997] By
# Andrea L. Ames, David R. Nadeau, and John L. Moreland
Group {
    children [
    # Donut, initially empty
        Shape {
            appearance Appearance {
                material Material { }
            }
            geometry DEF Donut Extrusion {
                crossSection [ ]
                spine [ ]
                creaseAngle 1.57
                beginCap FALSE
                endCap   FALSE
            }
        },
    # Donut maker
        DEF DonutMaker Script {
            url "donutmkr.js"
            field    SFFloat crossSectionRadius     1.0
            field    SFFloat spineRadius            2.0
            field    SFInt32 crossSectionResolution 16
            field    SFInt32 spineResolution        16
            eventIn  SFFloat set_crossSectionRadius
            eventIn  SFFloat set_spineRadius
            eventOut MFVec2f crossSection_changed
            eventOut MFVec3f spine_changed
        }
    ]
}
ROUTE DonutMaker.crossSection_changed TO Donut.set_crossSection
ROUTE DonutMaker.spine_changed        TO Donut.set_spine

# ---------------------------------------------------------
# finito
# --------------------------------------------------------- 

Zdrojový kód JavaScriptového programu pro generování tvaru toroidu je vypsán níže:

function initialize( ) {
    generateCrossSection( );
    generateSpine( );
}

function set_crossSectionRadius( csr, ts ) {
    crossSectionRadius = csr;
    generateCrossSection( );
}

function set_spineRadius( sr, ts ) {
    spineRadius = sr;
    generateSpine( );
}

function generateCrossSection( ) {
    angle = 0.0;
    delta = 6.28 / crossSectionResolution;
    for ( i = 0; i <= crossSectionResolution; i++ ) {
        crossSection_changed[i][0] = crossSectionRadius * Math.cos( angle );
        crossSection_changed[i][1] = -crossSectionRadius * Math.sin( angle );
        angle += delta;
    }
}

function generateSpine( ) {
    angle = 0.0;
    delta = 6.28 / spineResolution;
    for ( i = 0; i <= spineResolution; i++ ) {
        spine_changed[i][0] = spineRadius * Math.cos( angle );
        spine_changed[i][1] = 0.0;
        spine_changed[i][2] = -spineRadius * Math.sin( angle );
        angle += delta;
    }
} 

root_podpora

7. Literatura a odkazy na Internetu

  1. The Virtual Reality Modeling Language Specification,
    Version 2.0, ISO/IEC WD 14772
    http://graphcom­p.com/info/spec­s/sgi/vrml/spec/
  2. VRML 97 Specification,
    http://www.web3d­.org/x3d/conten­t/examples/Ba­sic/Vrml97Spe­cification/in­dex.html
  3. VRML Tutorial,
    http://www.lig­hthouse3d.com/vrml/tu­torial/
  4. VRML (Virtual Reality Modeling Language) and X3D,
    http://xml.co­verpages.org/vrml-X3D.html
  5. Onyx Graphics VRML Development,
    http://onyxgrap­hics.com/VRML­.html
  6. Open InventorTM,
    http://oss.sgi­.com/projects/in­ventor/
  7. Root.cz: Seriál Open Inventor,
    /serialy/open-inventor/
  8. Root.cz: Seriál Grafická knihovna OpenGL,
    /serialy/graficka-knihovna-opengl/
  9. Wikipedia: VRML,
    http://en.wiki­pedia.org/wiki/Vrml
  10. Wikipedia: Open Inventor,
    http://en.wiki­pedia.org/wiki/O­pen_Inventor
  11. Wikipedia: Web 3D Consortium,
    http://en.wiki­pedia.org/wiki/Web3D_Con­sortium
  12. Wikipedia: List of vector graphics markup languages,
    http://en.wiki­pedia.org/wiki/Lis­t_of_vector_grap­hics_markup_lan­guages
  13. Gavin Bell, Anthony Parisi, Mark Pesce: The Virtual Reality Modeling Language, Version 1.0 Specification,
    Silicon Graphics Inc., Intervista Software
    26-MAY-95
  14. Dr. Clue's VRML reference V2.0
    (dokument ve formátu WinHelp)
  15. Graef, G.L.: „Graphics Format“,
    Graphics Format
  16. Žára J., Beneš B., Felkel P.: „Moderní počítačová grafika“,
    Computer Press, Praha, 1998, ISBN 80–7226–049–9

8. Obsah následující části tohoto seriálu

Dalším jazykem určeným pro popis prostorových scén, který si v seriálu o grafických formátech a metaformátech popíšeme, je jazyk nazvaný X3D (Extensible 3D). Jedná se o ideového nástupce již popsaného jazyka VRML (Virtual Reality Modeling Language), který původní ideu VRML v několika směrech rozšiřuje i zpřesňuje a především ji přibližuje moderním technologiím, zejména obecnému značkovacímu jazyku XML.

7205

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.