dotNed

Welkom bij dotNed Inloggen | Aanmelden | Help
in Zoeken

Dennis' avonturen in .net

3D Graphics in WPF, deel 4 (intermezzo 2 over vectors, punten en andere wiskunde)

Laat de titel je niet afschrikken: we gaan niet samen je hele middelbare school wiskunde herhalen. Toch zal je, als je serieus met WPF 3D bezig wilt zijn, iets van vector berekeningen moeten weten. In deze post geen mooie plaatjes van vierkantjes en kubussen, maar we gaan rekenen! Geloof me: je zult het nodig hebben.

Als eerste: wat is een punt? Deze is eenvoudig te beantwoorden. In computer graphics worden punten op het scherm weergegeven als pixels (samenvoeging van picture element). Deze hebben een X en een Y waarde waarbij X=0 en Y=0 het punt links boven het scherm is. Als je de X waarde positief laat oplopen ga je meer naar rechts, terwijl je als je Y waarde positief laat oplopen je naar beneden op het scherm gaat. Een punt op je scherm kun je dus weergeven door het getallen paar X en Y. Op mijn scherm hier is het punt (X=0, Y=0) links boven, (X=1440, Y=0) rechtsboven, (X=0, Y=900) links onder en (X=1440, Y=900) rechts onder. Meestal laten we de X en Y weg in getallenparen en geven we deze vier punten weer als (0,0), (1440,0), (0,900) en (1440, 900).

Als we het over pixels hebben zijn de getallen meestal integers: je kunt immers niet een halve pixel tekenen.

In 3D WPF is dat iets anders. Zoals ik al eerder opmerkte kunnen we niet over pixels praten omdat we niet weten hoe groot een object gerenderd wordt: dit hangt af van de positie van het object, de positie van de camera, het type camera en de instellingen van de camera. In plaats daarvan praten we over logische eenheden. Of 1 unit nu 1 milimeter, 1 kilometer, 1 lichtjaar of 1 micrometer voorstelt hangt af van wat JIJ vindt dat het voorstelt: voor WPF maakt het niets uit. Deze werkt gewoon met eenheden. Deze eenheden zijn altijd doubles: we werken dus met waardes achter de komma.

Uiteraard hebben we niet genoeg aan een X en Y waarde om iets in een 3D ruimte aan te duiden. We hebben ook de Z waarde nodig. X en Y hebben dezelfde betekenis als in de 2D wereld (behalve dat Y oplopend naar boven gaat in plaats van naar beneden, iets waar we later nog last mee gaan krijgen!) maar we hebben ook een Z waarde. Deze Z-waarde geeft aan of iets meer in de diepte ligt of juist meer naar de voorgrond. Met de Z-waarde gaan we dus het scherm in, of juist er uit!

imageAls we nu even voorstellen dat ons 3D wereldje een grootte heeft van 1 eenheid bij 1 eenheid bij 1 eenheid (nogmaals: we weten niet hoe groot dat is op het scherm) dan is de hoek linksboven vooraan (X=0, Y=1, Z=0), rechtsboven vooraan (X=1, Y=1, Z=0), links onder vooraan (X=0, Y=0, Z=0), rechts onder vooraan (X=1, Y=0, Z=0). Uiteraard laten we hier ook de X,Y en Z weg, dus dat wordt (0,1,0), (1,1,0), (0,0,0) en (1,0,0).

Dit zijn alle punten die voor aan in onze wereld liggen. De punten aan de achterkant hebben als coordinaten, linksboven achter (0, 1, -1), rechtsboven achter (1, 1, -1), linksonder achter (0, 0, -1) en rechtsonder achter (1, 0, -1). Je ziet dat de Z waarde negatief wordt als we naar achter gaan. Je kunt besluiten om dat niet te doen, maar ik garandeer je dat je dan later enorm in de moelijkheden komt. Hou deze conventie maar vast: naar achter in het scherm is negatief voor Z. En nogmaals: Y naar beneden is richting 0, precies omgekeerd ten opzichte van standaard 2D grafieken).

We kunnen in WPF coordinaten vast leggen met de structure Point3D. Deze heeft als properties X, Y en Z: allen zijn double.

Als tweede: wat is een vector?

Een vector is een eenheid die grootte en richting heeft. In de natuurkunde gebruiken we vectoren om bijvoorbeeld krachten weer te geven. Er zijn enorm veel wiskundige boeken verschenen over het onderwerp vectoren, met name hoe je berekeningen met ze uitvoert. Je kunt vectoren bij elkaar optellen, vermenigvuldigen (met elkaar en met normale waardes) enzovoorts. Het slechte nieuws is dat we dat ook moeten gaan doen in WPF. Het goede nieuws is dat we heel veel details kunnen overlaten aan WPF.

Stel je voor dat je in de kubus die je hier ziet staan met je 3D mousepointer (als die zou bestaan) op de linkeronderhoek vooraan zou staan, oftewel op punt (0,0,0). Als we de mousepointer zouden willen vertellen dat die naar de linker beneden hoek achter (0,0,-1) moet gaan, dan zouden we hem moeten vertellen dat hij zijn X waarde niet moet wijzigen, ook zijn Y waarde blijft gelijk maar de Z waarde moet met 1 verminderd worden. Dat geven we aan met een vector, om precies te zijn de Vector (0,0,-1). Dit zegt dus: ga 0 stappen naar rechts, 0 stappen naar boven en -1 stappen naar de voorkant van het scherm (oftewel 1 stap naar achteren...).

Als we vanuit dat punt naar rechtsboven vooraan willen gaan, dus vanuit (0,0,-1) naar (1,1,0) dan moet hij de vector (1,1,1) mee krijgen: ga 1 stap naar rechts, 1 stap omhoog en 1 stap naar voren.

Je ziet dat de wiskunde van het optellen van vectors bij punten niet zo moelijk is: Point3d(0,0,-1) + Vector3D (1,1,1) levert Point3D (1,1,0) op: we tellen bij de X waarde van de punt de X waarde van de vector op, bij de Y waarde van de punt de Y waarde van de vector en bij de Z waarde van de punt de Z waarde van de vector. Het resultaat is dus (0 + 1, 0 + 1, -1 + 1) = (1, 1, 0).

Zoals je al zag is in WPF een vector weergegeven met de struct Vector3D. Deze heeft dezelfde X, Y en Z properties als de Point3D. Maar vergeet niet dat een vector een richting en grootte aangeeft en niet een punt!

Kijk nog eens naar de code voor onze PerspectiveCamera. Deze had als positie (0,0,5) en een LookDirection van (0,0,-1). De position is een Point3D (de camera 'staat' immers op een punt in onze scene) maar de LookDirection is een Vector3D: deze wijst in de richting waarnaar de camera kijkt: 0,0,-1 oftewel recht het scherm in (en niet naar links, rechts, beneden of boven). Wat had er nu gebeurdt als we als LookDirection (0,0,-5) hadden opgegeven? Het antwoord is: niets. De vector hier is een richting. Als je een pijl tekent om iemand te laten zien waar hij heen moet rijden, is het niet van belang hoe lang die pijl is: of deze nu 1 cm of 1 meter lang is: de richting is hier waar het om gaat.

Maar wat als we nu als LookDirection (0.5, 0, -1) hadden gezet? Dan had de camera het scherm en en schuin naar rechts gekeken. Ook dit kunnen we veranderen in (1,0, -2), dat maakt niets uit: het gaat niet om de lengte van de pijl (de tweede is twee keer zo lang als de eerste) maar om de richting.

Als we nog eens nadenken over ons voorbeeld van de mousepointer die we willen verplaatsen: daar is het wel van belang hoe lang de pijl is! Als we die waardes niet goed maken komt de muis op de verkeerde plek uit.

Onthoudt dus: soms is de richting van de Vector van belang, soms de grootte, soms allebei....

De reden dat we als lookdirection (0,0,-1) opgeven en niet (0,0,-5242) is dat de eerste een zogenaamde genormaliseerde vector is: dit is een vector met waardes als fracties van 1. WPF probeert dit soort vectoren zoveel mogelijk te gebruiken waar het kan (dus wanneer alleen de richting van belang is) en zal altijd dit soort vectoren omrekenen. Dit kost rekentijd, wat we in dit geval makkelijk zelf hadden kunnen doen door (0,0,-1) te gebruiken.

Hetzelfde geldt voor de UpDirection van onze camera: die is (0,1,0) oftewel recht naar boven.

Mocht je nu een vector willen normalizeren, dan is dat niet zo moeilijk: de struct heeft als method Normalize. Kijk eens naar de volgende code:

Vector3D myVector = new Vector3D(0.0, 0.0, -515423.243);

myVector.Normalize(  );

MessageBox.Show(myVector.Z.ToString(  )) ;

Als resultaat krijg je een messagebox met de waarde -1.

De struct Vector3D heeft nog meer handigheidjes in zich, bekijk de help eens.

Naast punten en vectoren zijn ook matrixen erg belangrijk in WPF 3D. Dit is echter iets wat vele malen ingewikkelder is dan dit stukje dus dat zal ik je nu besparen. We zien die wel verschijnen als het over transformaties (roteren, schalen en verplaatsten!) gaan hebben.

Goed. We weten nu wat punten en wat vectoren zijn. Dank je dat je je door dit toch wat saaie stuk heen gelezen hebt. Ik zal het de volgende keer goed maken: we gaan ons vierkantje vervangen door een kubus! En uiteraard komen we daar heel veel Point3D en Vector3D tegen zodat je dit niet voor niets gelezen hebt!

Published Wednesday, August 27, 2008 1:26 PM door dvroegop
Filed Under: ,

Comments

No Comments
Anonymous comments are disabled

About dvroegop

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