dotNed

Welkom bij dotNed Inloggen | Aanmelden | Help
in Zoeken

Dennis' avonturen in .net

3D Graphics in WPF, deel 3 (intermezzo 1 over lampjes)

Ik moet iets bekenen: ik heb valsgespeeld in mijn vorige post. Ik heb als laatste een vierkant getekend en die de kleur zwart gegeven. De waarheid is dat het niet uitmaakt welke kleur ik hem had gegeven, hij zou altijd zwart worden weergegeven. Probeer maar eens om de de kleur Brush="Black" bij de DiffuseMaterial te vervangen door Brush="White". Je krijgt nog steeds een saai zwart vierkantje.

De reden daarvoor is simpel. Denk maar eens na: wat gebeurdt er als je een foto neemt in een ruimte zonder licht? Het antwoord: alles wordt zwart. De reden dat bij ons alleen het vierkant zwart is en niet het hele scherm is dat de default achtergrond kleur (die niet door de camera gerenderd wordt!) wit is. Dus we krijgen een zwart vierkant op een witte achtergrond.

Hoe los je dat op? Wel, net als in het echte leven: als je in een ruimte niets kunt zien doe je het licht aan. In WPF is dat niet anders. In tegenstelling tot de 2D graphics in WPF (of welk ander framework dan ook) zul je moeten aangeven wat voor licht er is.

En nu wordt het ingewikkeld. Licht is een enorm complex iets, zowel in het echte leven als in onze gesimuleerde computer omgeving. Er zijn heel veel verschillende soorten licht, ieder met een eigen gedrag. Zo moet je aangeven wat voor kleur licht je hebt (met een rode lamp op een wit oppervlak schijnen levert een rood vlak op) maar ook wat voor soort lampen je hebt.

WPF heeft 4 soorten licht. Je kunt deze classes zien als lampen (waarbij er een is, AmbientLight die geen echte lamp voorstelt). Deze zijn: AmbientLight, DirectionalLight, PointLight en SpotLight. De eerste twee zul je het meeste gebruiken.

Alle light objecten bij elkaar leveren licht op. Je moet er op letten dat je niet teveel lampen in je scene zet: je loopt anders het risico dat je scene overbelicht raakt en je geen details meer kunt zien.

Bij het renderen berekent WPF hoeveel licht er op een object valt en wat voor kleur er dan zichtbaar is. Een object kan niet helderder zijn dan puur wit (RGB waarde 255, 255, 255). Meer licht erop levert niet witter wit op: het blijft RGB(255,255,255).

Als eerste kijken we even naar AmbientLight. AmbientLight is het licht dat er altijd is. Dit is vergelijkbaar met wat je ziet op een grijze, bewolkte dag: alles is zichtbaar maar mist diepte. Schaduwen zie je eigenlijk niet, alles wordt vanuit alle kanten gelijkmatig verlicht. Door AmbientLight te gebruiken geef je een basis belichting op.

DirectionalLight is hetzelfde als AmbientLight behalve dat het een richting heeft. Een DirectionalLight heeft geen duidelijke oorsprong, maar het licht gaat wel duidelijk een kant op. Objecten die geraakt worden door de DirectionalLight zijn zichtbaar, objecten die in de "schaduw" liggen zijn zwart (WPF heeft geen schaduw berekening, maar het heeft wel ongeveer hetzelfde effect). Een screenshot maakt dit duidelijker.

ambientLight

In het eerste plaatje zie je een box die 45 graden gedraaid is over de X,Y en Z as. Ik heb er alleen een puur witte AmbientLight bijgezet. Je ziet dat alle vlakken dezelfde hoeveelheid licht ontvangen en dat er dus geen enkel verschil zichtbaar is in de kanten.

Niet echt realistisch lijkt mij.

Als we de AmbientLight nu vervangen door een DirectionalLight die vanaf links boven achter naar rechts beneden voor schijnt, krijgen we een ander effect.

image Hier zie je dat het bovenste vlak goed uitgelicht wordt: deze staat volop in het zonnetje. Het vlak aan de linkerkant is een stuk donkerder: het licht komt immers van schuin boven dus dit is minder zichtbaar. Het vlak aan de voorkant is zwart: hier valt geen licht op.

In het echte leven zien we dit niet: er is immers vrijwel altijd wel wat omgevingslicht (= ambientlight) beschikbaar dus we kunnen altijd wel details zien.

Als we nu een klein beetje AmbientLight nemen (RGB kleur 64, 64, 64 dus donker grijs) en een lichte DirectionalLight (RGB 191, 191, 191 dus  licht grijs) dan krijgen we het volgende effect:

image Het verschil is subtiel, maar de kleuren komen nu wel beter uit: je kunt zien dat het voorvlak inderdaad blauw is (en het linkervlak is nu ook beter zichtbaar). Overigens: de totale lichtwaarde (RGB (64,64,64) + RGB(191, 191, 191)) is puur wit: we komen niet boven RGB (255,255,255) uit.

Het is in de praktijk erg lastig om je belichting goed te krijgen. Je moet er op letten dat je niet boven de totale waarde van wit uitkomt (meer heeft geen zin, dat doet niets) en je moet er goed opletten dat alle kanten van de objecten genoeg licht krijgen om zichtbaar te zien, zonder het 3D effect te niet te doen zoals in het eerste plaatje met alleen AmbientLight.

Naast de AmbientLight en DirectionalLight hebben we ook nog de PointLight en SpotLight. Deze kun je gebruiken om nog realistischer effecten te krijgen. Let echter op: dit soort geavanceerde technieken zorgen ervoor dat WPF veel meer tijd nodig heeft om je scene te berekenen. Als je met animaties gaat werken merk je dat door het gebruik van SpotLight en PointLight het aantal frames per seconde die je kunt halen erg naar beneden gaat (wij gebruiken ze dan ook niet in onze Business Visualization oplossingen).

Een PointLight begint al op een echt lampje te lijken. Je geeft hem een plaats in je 3D wereld, en de hoeveelheid licht die het verspreid neemt af naarmate de objecten verder weg liggen van de lamp (dit is allemaal instelbaar). Een SpotLight is een gespecialiseerde vorm van de PointLight (die in alle richtingen schijnt): deze schijnt in een bepaalde richting en heeft een duidelijke 'cone', dwz een gebied waar het licht duidelijk zichtbaar is en een gebied waar het licht minder zichtbaar is en dus in sterkte afneemt.

Spotlight Een spotlight heeft een innercone, waar het licht 100% is, en een outercone, waarin de lichtsterkte afneemt naarmate je meer naar de buitenkant gaat. De cones worden ingesteld door de hoeken op te geven. Zo kun je dus een dunne, felle bundel licht maken of een brede waaier van licht.

Tot zover de theorie: laten we de lampjes eens in onze scene plaatsen.

Nog een keer de code van vorige keer (maar dan met een vierkant die blauw moet worden):

<Window x:Class="WpfApplication11.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Viewport3D>
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <GeometryModel3D>
                    <GeometryModel3D.Geometry>
                        <MeshGeometry3D Positions="0 0 0 1 0 0 1 1 0 0 1 0"
                                        TriangleIndices="0 1 2 0 2 3"/>
                    </GeometryModel3D.Geometry>
                    <GeometryModel3D.Material>
                        <DiffuseMaterial Brush="Blue"/>
                    </GeometryModel3D.Material>
                </GeometryModel3D>                
            </ModelVisual3D.Content>                       
        </ModelVisual3D>
        
        <Viewport3D.Camera>
            <PerspectiveCamera Position="0 0 5" 
                           LookDirection="0 0 -1" 
                           UpDirection="0 1 0" 
                           FieldOfView="60.0" 
                           />
        </Viewport3D.Camera>
    </Viewport3D>
</Window>

Dit levert dus een zwart vierkantje op...

Tussen de </ModelVisual3D> en de <Viewport3D.Camera> plaatsen we nu de volgende code:

        <!-- Lampjes! -->
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <AmbientLight Color="White"/> 
            </ModelVisual3D.Content>
        </ModelVisual3D>

We maken een ModelVisual3D aan (die bevat alles wat getekend moet worden, wat ook voor lampen geldt), in de content daarvan plaatsen we een AmbientLight. Deze geeft wit licht. Run je applicatie en voila: een blauw vierkantje!

Mocht je nu een DirectionalLight willen plaatsen dan vervang je de AmbientLight regel door

<DirectionalLight Color="White" Direction="1 -1 -1"/>

... en je hebt een DirectionalLight. Het vierkantje wordt nu een stuk donkerder: ons lampje schijnt van linksboven voor naar rechtsonder beneden (Vector: 1, -1, -1)) en dus 'strijkt' het licht langs ons vierkantje.

Wat als je nu zowel een Ambient als een DirectionalLight wil hebben? Dan moet je even een trucje uithalen: De ModelVisual3D.Content kan maar 1 item bevatten... Dat is niet zo moelijk op te lossen. We voegen als Content een Model3DGroup toe (dat is precies 1 item) en die Model3DGroup bevat op zijn (haar?) beurt weer andere items:

<!-- Lampjes! -->
<ModelVisual3D>
    <ModelVisual3D.Content>
        <Model3DGroup>
            <AmbientLight Color="#404040"/>
            <DirectionalLight Color="#BFBFBF" Direction="1 -1 -1"/>
        </Model3DGroup>
    </ModelVisual3D.Content>
</ModelVisual3D>

Probleem opgelost! Je ziet dat ik hier met RGB waardes werk (in hexadecimale waardes) in plaats van de named colors: op deze manier heb ik precieze controle over de totale hoeveelheid licht in mijn scene.

Speel eens met deze code, verander het licht (de kleuren bijvoorbeeld) en voeg een spotlight toe. Kijk eens wat er gebeurd als je je vierkant wit maakt en je met andere kleuren licht werk!

Volgende post wordt weer een intermezzo waar ik dieper in ga op de Vector3D en Point3D: we hebben ze al voorbij zien komen maar we hebben het er eigenlijk niet over gehad. Daarna gaan we dan echt kijken naar andere figuurtjes behalve dit nuttige maar toch wel saaie vierkantje!

Published Wednesday, August 27, 2008 12:34 PM door dvroegop
Filed Under: ,

Comments

 

Dennis' avonturen in .net said:

In de vorige posting liet ik je zien hoe je een vierkant definieert. Ik sloot af met de opmerking dat...
August 27, 2008 4:35 PM
Anonymous comments are disabled

About dvroegop

Programmeert al sinds 1982. Microsoft Surface MVP.
Powered by Community Server, by Telligent Systems