Sutherland-Hodgman Clipping for Physics-motoren begrijpen

knipsel is het proces om te bepalen welk gebied van ruimte binnen een ander gebied van ruimte is. Dit is vooral belangrijk in studiegebieden zoals computergraphics en simulatie van natuurkunde. Wanneer u bijvoorbeeld een wereld op het scherm weergeeft, is het het beste om te weten wat er op het scherm zal staan ​​voordat u gegevens verwerkt. Hierdoor kunnen veel externe gegevens worden genegeerd, terwijl alleen de gegevens worden verwerkt en gepresenteerd door de gebruiker.

In fysieke simulatie worden vaak starre lichamen gevonden doordringend - dat wil zeggen, twee afzonderlijke objecten overlappen elkaar. Dit is slecht. Interpenetratie is iets wat nooit in de echte wereld gebeurt, maar is een probleem dat moet worden aangepakt in botsingsdetectie. Vaak wordt de ene vorm afgekapt tegen de andere om te bepalen welke functies elkaar raken. Vanaf hier kan een botsingsreactie worden gestart.

Geavanceerde functies van game-engines kunnen worden geïmplementeerd met een vorm van knippen; drijfvermogen simulatie, camera zichtvlakken; brosse breuk, contactgeneratie met de Seperating Axis Test; al deze dingen (en nog veel meer) kunnen worden bereikt met clipping-algoritmen. In feite was het knippen zoals dit nodig in mijn vorige tutorial over het bouwen van een rigide fysica-engine.


voorwaarden

Dit artikel vereist wat dat je een goed begrip hebt van:

  • Elementaire lineaire algebra
  • Eenvoudige 3D-wiskunde

De bovenstaande studiegebieden kunnen worden geleerd van een breed scala aan bronnen op het internet (zoals Wolfire's guide to linear algebra of Khan Academy - en ze zijn niet zo moeilijk om te leren!


Vliegtuigen splitsen

Veel complexe kniproutines omvatten het gebruik van een enkel type bewerking: het splitsen van een vorm met een enkel vlak. Een vorm splitsen door een vlak houdt in dat je een vorm krijgt en in twee stukken snijdt door een solide vlak.

Het is belangrijk om te beseffen dat het splitsen van een vorm met een vlak een fundamenteel hulpmiddel is in deze kniproutine.

Zie de uitstekende animaties van Davide Cervone voor een duidelijke demonstratie hiervan:


Door Davide P. Cervone. Gebruikt met toestemming.

Sutherland Hodgman Clipping

Sutherland Hodgman knippen is mijn favoriete kniproutine, omdat het voor het knippen van lijnlussen tegen vliegtuigen werkt. Met dit algoritme, gegeven een lijst met ingevoerde hoekpunten en een lijst met vlakken, kan een uitvoer van nieuwe hoekpunten die alleen in de reeks vlakken bestaan, worden berekend.

De term vlak kan worden gebruikt om naar een vlak in 3D en 2D te verwijzen. Een 2D-vlak kan ook a worden genoemd lijn.

We kunnen de lijst met hoekpunten voorstellen als een enkele polygoon. We kunnen ons (voorlopig) de lijst met vliegtuigen als één regel voorstellen. Het visualiseren van dit in twee dimensies kan er als volgt uitzien:

Met Sutherland Hodgman-clipping is de veelhoek aan de rechterkant de gewenste vorm, terwijl de rode veelhoek aan de linkerkant de invoer is en nog moet worden afgekapt. De blauwe lijn vertegenwoordigt een splijtvlak in twee dimensies. Elk aantal splitsvlakken kan worden gebruikt; in het bovenstaande voorbeeld wordt slechts een enkel splijtvlak gebruikt. Als er veel splijtvlakken worden gebruikt, dan zou het bijsnijden op hetzelfde moment plaatsvinden, waarbij steeds meer van een oorspronkelijke vorm weggesneden wordt om een ​​nieuwe uit te voeren:

Kanten van een vliegtuig

Laten we voor de eenvoud in twee strikte dimensies werken. Bij het splitsen van een polygoon tegen een vlak, is het belangrijk om te weten aan welke kant van het vlak een bepaald punt is.


Boven is de meest gebruikte manier om de afstand van een punt naar een vlak te vinden. Merk op dat het puntsymbool in de bovenstaande vergelijking het puntproduct vertegenwoordigt.

De afstand hoeft niet expliciet te worden berekend; de normale vector n hoeft niet genormaliseerd te zijn (dat wil zeggen, het hoeft niet een lengte van exact één eenheid te hebben). Alles wat moet worden gecontroleerd, is het teken van het resultaat op afstand d. Als n is dan niet genormaliseerd d zal in eenheden van de lengte van zijn n. Als n is dan genormaliseerd d zal zijn in eenheden die equivalent zijn aan eenheden voor wereldruimten. Dit is belangrijk om te realiseren omdat het mogelijk maakt om de berekening uit te voeren om te zien of d is positief of negatief. Positief d duidt op dat punt p bevindt zich aan de voorkant van het vliegtuig. Negatief betekent dat het aan de achterkant is.

Wat bedoelen we precies met de "voorkant" en "achterkant" van een vliegtuig? Nou, het hangt echt af van wat je voor en achter definieert als. Meestal betekent "voorkant" dat het zich aan dezelfde kant van het vliegtuig bevindt als het normale.

Het algoritme

Laten we direct in het algoritme duiken. Maak een snelle scan over de pseudocode:

 Polygon SutherlandHodgman (const Polygoon startingPolygon, Plane [] clippingPlanes) Polygoonuitvoer = startPolygoon voor elke vlakknipsel Plane in clippingPlanes input = output output.Clear () Vec2 startingPoint = input.Last () voor elke Vec2 endPoint in invoer als startingPoint en endPoint in front of clippingPlane out.push (endPoint) anders als startingPoint vooraan en endPoint achter clippingPlane out.push (Intersection (clippingPlane, startingPoint, endPoint)) anders als startingPoint en endPoint achter clippingPlane out.push (Intersection (clippingPlane, startingPoint, endPoint) ) out.push (endPoint) endPoint = startingPoint retouroutput

Het algoritme neemt een invoerpolygoon en enkele uitknipvlakken en geeft een nieuwe veelhoek af. Het idee is om elk lijnsegment van de invoerpolygoon per clipclip tegelijk te knippen. Deze afbeelding van Wikimedia Commons laat dit vrij goed zien:


Sutherland-Hodgman clipping algoritme, door Wojciech Mula.

Elke knipbewerking heeft slechts een paar verschillende gevallen om vertices uit te voeren en kan als volgt worden geschetst:

 // InFront = plane.Distance (punt)> 0.0f // Behind = plane.Distance (punt) < 0.0f Vec2 p1, p2; ClipPlane plane; case p1 InFront and p2 InFront push p2 case p1 InFront and p2 Behind push intersection case p1 Behind and p2 InFront push intersection push p2

De beste manier om dit algoritme te begrijpen, is door een 2D-vorm op een stuk papier te tekenen en een rechte lijn door de vorm te tekenen. Loop vervolgens langs de hoekpunten van de veelhoek en voer het Sutherland-Hodgman-algoritme uit en teken de resultaten. Hiermee bouw je intuïtie op over hoe het algoritme achtereenvolgens langs elke regel loopt, en zorg ervoor dat alle hoekpunten achter het vlak blijven.

Na je eigen potlood en papierloop, probeer deze geweldige webpagina. Er zijn een aantal geweldige visuals en een Java-applet (bovenaan de pagina) waarmee de gebruiker gedeelten van het algoritme daadwerkelijk kan zien rennen!

Intersectie berekening

Het berekenen van de kruising van een vliegtuig op twee punten is heel eenvoudig. Het idee is om de afstandsberekening te gebruiken om de afstand van elk punt tot een vlak te vinden. Gegeven deze afstanden kan een kruispunt vervolgens worden berekend door gebruik te maken van lineaire interpolatie.

Tip: Lineaire interpolatie is een uiterst bruikbaar concept om te begrijpen, met een vruchtbare toepassing in alle grafische software en in fysieke simulatiesoftware. Lineaire interpolatie kan in de volksmond worden aangeduid als lerp. Alles kan lineair worden geïnterpoleerd van een beginpositie naar een eindpositie, zolang het Bewegingsvergelijking is lineair.

Over het algemeen de formule voor het lineair interpoleren vanuit begin naar einde met kruising alpha is:

 Lerp (begin, einde, alpha) begin start * (1 - alpha) + einde * alpha
Tip: In het bovenstaande voorbeeld, alpha is wat een an wordt genoemd interpolant. Interpolanten worden gebruikt in de lineaire interpolatieberekeningen om posities tussen begin- en eindpunten te berekenen.

Dit wetende, kunnen de afstanden van het vlak tot elk punt worden gebruikt als waarden waarvan om een ​​te berekenen alpha interpolant. De variabelen t1 en T2 kan de afstanden tot het vlak van weergeven p1 en p2 respectievelijk.

In dit opzicht is het kruispunt eenvoudigweg een Lerp van het beginpunt tot het eindpunt, gegeven een kruisingstijd.

Uitbreiding naar 3D

Dit algoritme kan eenvoudig worden uitgebreid naar een driedimensionale ruimte en daar worden uitgevoerd. (Dit algoritme kan in elke hogere dimensie worden uitgevoerd, wat dat ook betekent.) In praktische toepassingen is dit algoritme echter meestal nooit werkelijk uitgevoerd in 3D. Door slimme inverse transformaties te gebruiken, kan het probleem van het knippen van een 3D-veelvlak tegen een vlak (in bepaalde scenario's) worden vereenvoudigd tot een 2D-polygoon tot polygoonkniproutine. Dit maakt een aanzienlijke computationele optimalisatie mogelijk.

Evenzo, als men de broncode van de Bullet Physics zou bestuderen, zou men ontdekken dat knippen in feite in een enkele dimensionale ruimte wordt gedaan, hoewel volledige 3D-clipping echt wordt uitgevoerd. Dit komt door de mogelijkheid om een ​​geldige interpolant te berekenen die slechts één dimensie van de probleemruimte bevat.

Als u bijvoorbeeld de kruisingstijd kent van de X waarden van een eenvoudig probleem, kan deze kruistijd worden gebruikt als een interpolant om a uit te voeren Lerp op een driedimensionaal punt.

Laten we dit in een beetje meer detail bekijken. Bekijk het volgende diagram:

Neem aan dat de gele lijn de grond is op een y-positie van 0. Ga er nu van uit dat de startpositie van y is 10, en de eindigende y-positie is om -1. Laten we een geldige interpolant- en intersectiepositie langs de y-coördinaat berekenen:

 // alpha = 0.9 / 10.0f float alpha = 0.9f // finalY = 0.0f float finalY = Lerp (y1, y2, alpha);

Het bovenstaande kan worden gelezen als "90% van de weg van 10 naar -1", wat nul zou zijn. Deze interpolant kan worden toegepast op willekeurige gegevenstypen, waaronder een tweedimensionale vector:

 // alpha = 9.0f / 10.0f float alpha = 0.9f Vec2 finalPosition = Lerp (p1, p2, alpha);

De bovenstaande code zou eigenlijk de correcte x-coördinaat voor de impacttijd berekenen, zonder zelfs bewerkingen met de x-coördinaat uit te voeren om de interpolant te bepalen. Dit idee om interpolanten te berekenen en toe te passen op hogere dimensies dan waarvan ze zijn berekend, is een geweldige manier om de code te optimaliseren.

Numerieke robuustheid

Er zijn enkele problemen die kunnen blijven bestaan ​​bij het uitvoeren van een naïeve implementatie van deze kniproutine. Bij het berekenen van het snijpunt kruipt de numerieke fout in de berekening. Dit vormt een groot probleem bij het splitsen van een polygoon met een vlak terwijl output wordt gegenereerd beide zijden van het vliegtuig. Voor het genereren van uitvoer voor beide zijden van een splijtvlak, moet het oorspronkelijke Sutherland-Hodgman-algoritme enigszins worden aangepast en dit is waar de problemen zich voordoen.

Telkens wanneer een kruispunt wordt berekend, kruipt numerieke fout. Dit is een probleem aangezien het snijpunt wordt berekend op basis van punt EEN wijzen B zal enigszins afwijken van de berekening van punt B wijzen EEN. Om dergelijke problemen te voorkomen, moet de kruising op een consistente manier worden berekend. Dit vermijdt een verschrikkelijke T-kruising kwestie. Het is goed als er een numerieke fout is zolang de fout consistent is.

T-Junction-probleem: Een opening tussen twee mazen waardoor driehoeksrasterisatie een zichtbare opening achterlaat tussen drie driehoeken. Meestal veroorzaakt door slechte verwerking van machine-epsilon tijdens drijvende-komma-berekening. Mogelijke oplossingen: consistente vertex-transformaties; vertex lassen nabewerking.

Een ander probleem is het bepalen of een punt zich aan een kant van een vliegtuig of een ander bevindt. Om een ​​hele reeks redenen, moeten dikke vliegtuigcontroles worden uitgevoerd. Het idee is om een ​​vlak te behandelen als een dik vlak, in plaats van een vlak met een oneindig kleine breedte. Dit zorgt ervoor dat er een extra geval ontstaat in de Sutherland-Hodgman-routine: de in het vliegtuig geval.

 zweven D = vlak. Afstand (punt); // EPSILON is een numeriek significant en visueel onbeduidend nummer. Misschien 0.00001f. bool OnPlane (zweven D) return D> -EPSILON en D < EPSILON; 

Als er een punt is gevonden in het opknipvlak, drukt u gewoon op het eindpunt. Hierdoor wordt de telling van 3 gevallen opgeteld tot 4 in totaal. Voor meer informatie over EPSILON, kijk hier.


Referenties en broncode

De beste referentie voor dit clippingalgoritme bevindt zich in het real-time botsingsdetectieboek van Christer Ericson (ook bekend als het oranje boek). Je vindt dit boek in vrijwel elke gamestudio die er is.

Naast het schenken van veel geld voor een boek, bestaan ​​er een paar gratis bronnen:

  • Enigszins aangepaste versie van Sutherland-Hodgman voor 2D-clipping in een fysica-engine
  • Rosetta-codevoorbeelden (niet de beste code, maar een goede referentie)
  • Visualisatie van het algoritme en psuedocode

Conclusie

Sutherland-Hodgman knippen is een geweldige manier om knippen in zowel 2D- als 3D-ruimte uit te voeren. Dit type routine kan worden toegepast om vele verschillende problemen op te lossen, waarvan sommige problemen van toepassing zijn op vrij geavanceerde onderzoeksgebieden. Zoals altijd, aarzel dan niet om eventuele vragen te stellen in de comments hieronder!