Scéna bez světel bývá většinou nudná a plochá. Pomineme-li drátěné modely, tělesa, která mají různě obarvené stěny, a tělesa pokrytá texturami, není u neosvětlených objektů prakticky možné poznat, že jsou trojrozměrné. Aby naše scény napříště nevypadaly ploše, seznámíme se v tomto článku s osvětlovacím modelem v Javě 3D.

Ambientní a difúzní odrazy, odlesky

Java 3D používá pro osvětlování Phongův osvětlovací model, který je pro své rozumné výpočetní nároky zvláště vhodný pro real-time renderovanou grafiku. V tomto modelu se rozlišují tři složky světla odraženého od povrchu tělesa – ambientní (ambient), difúzní (diffuse) a takzvané odlesky (speculars).

Ambientní složka vzniká odrazem „ambientního“ světla, což je světlo přicházející ze všech směrů se stejnou intenzitou. Samotné ambientní světlo nám vzhledem ke své povaze plastické zobrazení objektů ve scéně nezajistí, ale je užitečné ve spojení s dalšími složkami.

Difúzní složka světla představuje odraz světla pocházejícího z určitého daného zdroje nebo alespoň směru. Od povrchu tělesa se odráží do všech směrů stejně. Sama o sobě difúzní složka stačí k zobrazení matných objektů.

Poslední ze složek odraženého světla, odlesky, vytvářejí přesně to, co bychom očekávali podle jejich názvu – odlesky. Odlesky vznikají ze světla, které se odráží podle zákona odrazu (byť s jistým rozptylem, ale o tom bude řeč dále).

Všechny tři zmíněné složky odraženého světla jsou označeny na následujícím obrázku. (Není to tak úplně přesné, protože v jednom bodě se může projevit více složek.)

Jednotlivé složky odraženého světla

Materiál

Jak známo, od různých těles se světlo odráží různě. Od černého tělesa se neodráží vůbec, od bílého naopak zcela. Některé těleso je matné, jiné lesklé. K určení vlastností objektů ve scéně vzhledem k osvětlení se v Javě používají objekty Material, které se pak přidružují k příslušným objektům Appearance, k čemuž slouží metoda setMaterial(Material material) definovaná ve třídě Appearance.

Konstruktor Material(Color3f ambientColor, Color3f emissiveColor, Color3f diffuseColor, Color3f specularColor, float shininess) přebírá jako parametry čtyři objekty typu Color3f a jedno číslo typu float. První z těchto parametrů určuje, jakou část ambientního světla objekt odráží (ve smyslu součinu intenzity jednotlivých barevných složek materiálu a světla). Druhý parametr, emissiveColor, určuje světlo, které objekt sám vyzařuje. Třetí parametr udává, jaká část světla se bude odrážet jako difúzní složka. Čtvrtý parametr udává, jaká část světla se bude odrážet jako odlesk. Poslední parametr shininess určuje, jak moc se odlesková složka světla svým chováním odchýlí od zákona odrazu. Tento parametr nabývá hodnoty z intervalu 1 až 128. Čím menší hodnota parametru, tím větší je úhel, do něhož se světlo rozptyluje, a tedy i velikost odlesku.

Následují obrázky vytvořené osvětlením šedým ambientním světlem a bílým směrovým světlem, nebo zeleným směrovým světlem (druhý). U každého z nich je uveden příslušný objekt Material.

Zobrazovaná koule je matná
Zobrazovaná koule je matná

Toto těleso vůbec neodráží zelenou složku
Toto těleso vůbec neodráží zelenou složku

Zobrazovaná koule je matná. V druhém obrázku se objeví pouze ambientní složka odrazu, protože toto těleso vůbec neodráží zelenou složku.

Appearance app = new Appearance();
Color3f ambient = new Color3f(0.3f, 0.3f, 0.3f);
Color3f emissive = new Color3f(0,0,0);
Color3f diffuse = new Color3f(0.8f, 0.0f, 0);
Color3f specular = new Color3f(0,0,0);
Material material = new Material(ambient, emissive, diffuse, specular, 100);

Difúzní složka i odlesk
Difúzní složka i odlesk

Pod zeleným světlem se těleso jeví rovněž zelené
Pod zeleným světlem se těleso jeví rovněž zelené

Appearance app = new Appearance();
Color3f ambient = new Color3f(0.3f, 0.3f, 0.3f);
Color3f emissive = new Color3f(0,0,0);
Color3f diffuse = new Color3f(0.7f, 0.5f, 0.3f);
Color3f specular = new Color3f(1,1,1);
Material material = new Material(ambient, emissive, diffuse, specular, 128);

Čtyři druhy světel

Java 3D nám poskytuje čtyři třídy pro čtyři typy světel: AmbientLight, DirectionalLight, PointLight a SpotLight.

Třída AmbientLight implementuje funkčnost ambientního světla, o němž jsme si již řekli, že přichází ze všech směrů se stejnou intenzitou. Ambientní světlo nahrazuje ve scéně tu část reálného světla, která obyčejně vzniká odrážením světla od různých předmětů, například nábytku a stěn v místnosti. Konstruktor AmbientLight(Color3f color) očekává pouze jeden parametr typu Color3f, který určuje barvu tohoto světla.

Třída DirectionalLight slouží k vytvoření takzvaného směrového světla, což je světlo, jehož směr a intenzita jsou ve všech bodech scény stejné. Toto světlo odpovídá světlu přicházejícímu z nějakého vzdáleného zdroje, například Slunce. Konstruktoru DirectionalLight(Color3f color, Vector3f direction) se předávají dva parametry, přičemž první určuje barvu světla a druhý určuje jeho směr pomocí vektoru Vector3f.

Další ze světel, která poskytuje Java 3D, je bodové světlo – PointLight. Bodové světlo je charakterizováno svou barvou, pozicí ve scéně a třemi koeficienty, které určují, jak se se vzdáleností mění jeho intenzita. Výsledné zeslabení světla se vypočítá z těchto tří koeficientů podle vzorce 1/(konstantní + lineární*vzdálenost + kvadratický*vzdálenost^2). Konstruktor třídy vypadá následovně: PointLight(Color3f color, Point3f position, Point3f attentuation). V parametru „attentuation“ jsou uloženy po řadě koeficienty konstantní, lineární a kvadratický.

Posledním typem světla je SpotLight, neboli reflektorové světlo. Reflektorové světlo je podobné bodovému světlu v tom, že se také šíří z jednoho bodu a se vzdáleností od zdroje se může zeslabovat (viz parametr „attentuation“), nicméně se na rozdíl od něj nešíří do všech směrů, ale pouze do zadaného kužele. Konstruktor SpotLight(Color3f color, Point3f position, Point3f attentuation, Vector3f direction, float spreadAngle, float concentration) nám už je zčásti známý, neboť první tři parametry mají stejný význam jako u konstruktoru třídy PointLight. Význam zbývajících je následující: parametr „direction“ určuje směr světelného kužele, parametr „spreadAngle“ udává úhel (v radiánech) mezi směrovým vektorem „direction“ a libovolnou polopřímkou vycházející z vrcholu kužele a ležící v jeho plášti. Jak se v daném kuželu mění podle úhlu intenzita světla, určuje parametr „concentration“ nabývající hodnot z intervalu 1 až 128. Čím větší je jeho hodnota, tím více se intenzita s úhlem mění.

Následuje obrázek s koulí osvětlenou reflektorovým a ambientním světlem:

Koule osvětlená reflektorovým světlem

Jak umístit světlo do scény

Třída Light, od níž jsou odvozeny všechny čtyři typy světel (pro úplnost dodávám, že třída SpotLight je podtřídou třídy PointLight) je odvozena od třídy Leaf, což znamená, že ji můžeme do scény přidat úplně stejně jako všechny ostatní objekty, tedy voláním metody addChild některého objektu Group.

Pouhým přidáním do scény to ale nekončí. U světel totiž musíme určit ještě buď prostor, který budou osvětlovat, nebo jednotlivé objekty, které jimi budou osvětleny. Na tomto místě si nyní ukážeme pouze první možnost.

Určit část scény v níž obsažené objekty budou osvětleny, lze pomocí metody setInfluencingBounds(Bounds region), která přebírá instanci některé z podtříd třídy Bounds. Těmi jsou BoundingBox, BoundingPolytope a BoundingSphere. Třída BoundingBox slouží k vytváření oblastí tvaru kvádru, třída BoundingPolytope umožňuje vytvářet oblasti jako průniky několika poloprostorů a třída BoundingSphere vytváří oblasti tvaru koule. Dále použijeme pouze třídu BoundingSphere.

Příklad použití

Následující program vytváří scénu s osvětlenou koulí. V konstruktoru třídy Sphere, který danou kouli vytváří, je nutné zadat příznak GENERATE_NORMALS, který říká, že si přejeme vygenerovat normálové vektory, které jsou nezbytné k tomu, aby mohl být objekt osvětlen. Více k tomuto tématu si ale povíme, až se naučíme vytvářet vlastní objekty zadané pomocí souřadnic vrcholů a návaznosti mezi nimi.

package interval.j3d;
import java.awt.*;
import javax.swing.*;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.*;
import javax.media.j3d.*;
import javax.vecmath.*;
public class Lights extends JFrame {
public Lights() {
   super(„Světla“);
   addWindowListener(new java.awt.event.WindowAdapter() {
      public void windowClosing(java.awt.event.WindowEvent evt) {
         System.exit(0);
      }
   });
   setSize(400,400);
   // Na následujících řádcích vytvoříme Canvas3D, SimpleUniverse ad.
   Container pane = getContentPane();
   pane.setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
   Canvas3D canvas = new Canvas3D(config, false);
   pane.add(„Center“, canvas);
   SimpleUniverse universe = new SimpleUniverse(canvas);
   universe.getViewingPlatform().setNominalViewingTransform();
   BranchGroup br = new BranchGroup();
   // vytvoříme kouli
   Sphere sphere = new Sphere(0.5f, Primitive.GENERATE_NORMALS, 120);
   // nastavíme kouli objekt Appearance,
   // který vytvoříme v metodě vytvorAppearance
   sphere.setAppearance(vytvorAppearance());
   br.addChild(sphere);
   // vytvoříme světlo a přidáme jej do scény
   br.addChild(vytvorSvetlo());
   br.addChild(vytvorAmbientniSvetlo());
   br.compile();
   universe.addBranchGraph(br);
}
Light vytvorSvetlo(){
   // směrový vektor světla
   Vector3f direction = new Vector3f(-1,-1,-1);
   // barva světla (bílá)
   Color3f color = new Color3f(1,1,1);
   DirectionalLight dl = new DirectionalLight(color, direction);
   // nastavíme oblast působení
   BoundingSphere bs = new BoundingSphere(new Point3d(0,0,0), 4);
   dl.setInfluencingBounds(bs);
   return dl;
}
Light vytvorAmbientniSvetlo(){
   // vytvoříme ambientní světlo šedé barvy
   AmbientLight amb = new AmbientLight(new Color3f(0.6f, 0.6f, 0.6f));
   // nastavíme oblast působení
   BoundingSphere bs = new BoundingSphere(new Point3d(0,0,0), 4);
   amb.setInfluencingBounds(bs);
   return amb;
}
Appearance vytvorAppearance(){
   // vytvoříme objekt Appearance kvůli objektu Material
   Appearance app = new Appearance();
   Color3f ambient = new Color3f(0.3f, 0.3f, 0.3f);
   Color3f emissive = new Color3f(0,0,0);
   Color3f diffuse = new Color3f(0.7f, 0.5f, 0.3f);
   Color3f specular = new Color3f(1,1,1);
   Material material = new Material(ambient, emissive, diffuse, specular, 128);
   app.setMaterial(material);
   return app;
}
public static void main(String[] args) {
   new Lights().show();
}
}

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět