Normalen - Interpolation für 3D Modelle

Übersicht BlitzBasic Codearchiv

Neue Antwort erstellen

Noobody

Betreff: Normalen - Interpolation für 3D Modelle

BeitragMi, Mai 27, 2009 18:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Nach Lektüre eines kleinen Artikels auf Gamasutra habe ich eine Funktion für die Interpolation von 3D - Modellen geschrieben.
Ist wohl einer der kompliziertesten Codes, der je aus meiner Feder floss Razz

Die Funktion geht alle Dreiecke des Ursprungsmodells durch und verfeinert das Modell, indem es das Dreieck 'wölbt'. Wie genau diese Wölbung aussieht, wird durch die Vertexnormalen der drei anliegenden Vertices bestimmt.

Wofür ist das gut?
Nun, das wird ganz praktisch, wenn man verschieden detaillierte Ausführungen eines Modells in seinem Spiel verwenden will, um nahe Objekte mit hoher und ferne Objekte mit niedriger Polygonzahl rendern zu lassen. Wenn man die Vertexnormalen geschickt setzt, kann man mit der Funktion eine detailliertere Version des Meshes aus dem Low-Poly-Mesh generieren lassen. Dadurch braucht man nicht immer mehrere verschiedene Modelldateien zu seinem Spiel beilegen, sondern erstellt alles beim Start des Programms auf Basis eines einzigen Modells.

Wie fein das generierte Mesh sein soll, kann durch den Parameter LOD der Interpolationsfunktion festelegt werden. Je höher der Wert, desto mehr Polygone.
Beim Aufruf von SetNormals im Beispielcode (Zeile 19) kann man den letzten Parameter von 1 auf höhere Werte abändern, um die Interpolation noch zu verstärken - ergibt einen tollen visuellen Effekt.

Der Code demonstriert die Funktionsweise des Algorithmus anhand den Primitiven von Blitz (Würfel, Kegel, Kugel etc.) Code: [AUSKLAPPEN]
Graphics3D 800, 600, 0, 2
SetBuffer BackBuffer()

Dim Vertices( 0, 0 ) ;Vertexarray

Local Cam = CreateCamera() ;Kamera
PositionEntity Cam, 0, 0, -5

Local Light = CreateLight( 2 ) ;Beleuchtungskram
PositionEntity Light, 0, 5, -2
LightRange Light, 3
LightColor Light, 255, 255, 255

Local Mesh = CreateCube() ;Das Mesh, das interpoliert werden soll - einfach die verschiedenen Blitz - Primitiven ausprobieren
;Local Mesh = CreateSphere( 2 )
;Local Mesh = CreateCone( 4 )
;Local Mesh = CreateCylinder()

PositionEntity Mesh, -2.5, 0, 0
SetNormals Mesh, 1 ;Normalen neu berechnen; Blitz setzt die Normalen in einer für die Interpolation ungünstigen Weise

Local Spline = InterpolateMesh( Mesh, 15 ) ;Das Mesh mit LOD 15
PositionEntity Spline, 2.5, 0, 0

Local Timer = CreateTimer( 60 )

While Not KeyHit( 1 )
   Cls
   
   Wireframe False
   EntityColor Mesh, 255, 255, 255
   EntityColor Spline, 255, 255, 255
   ScaleEntity Mesh, 1, 1, 1
   ScaleEntity Spline, 1, 1, 1
   CameraClsMode Cam, True, True
   
   RenderWorld
   
   If MouseDown( 1 ) Then ;Mit linker Maustaste Wireframe - Overlay
      Wireframe True
      EntityColor Mesh, 0, 0, 0
      EntityColor Spline, 0, 0, 0
      ScaleEntity Mesh, 1.005, 1.005, 1.005
      ScaleEntity Spline, 1.005, 1.005, 1.005
      CameraClsMode Cam, False, False
      
      RenderWorld
   EndIf
   
   TurnEntity Mesh, 0.5, 1, 1.5
   TurnEntity Spline, 0.5, 1, 1.5
   
   Flip 0
   WaitTimer Timer
Wend
End

Function InterpolateMesh( Mesh, LOD = 5 )
   Local NewMesh = CreateMesh()
   Local Brush = GetEntityBrush( Mesh )
   
   For SurfIndex = 1 To CountSurfaces( Mesh )
      Local Surf = GetSurface( Mesh, SurfIndex )
      Local NewSurf = CreateSurface( NewMesh )
      
      Local TriCount = CountTriangles( Surf )
      
      PaintSurface NewSurf, GetSurfaceBrush( Surf )
      
      For TriIndex = 0 To TriCount - 1
         Local P102#[ 2 ], P201#[ 2 ], P012#[ 2 ], P111#[ 2 ], P210#[ 2 ], P021#[ 2 ], P120#[ 2 ], P003#[ 2 ], P030#[ 2 ], P300#[ 2 ] ;Koordinaten
         Local N200#[ 2 ], N020#[ 2 ], N002#[ 2 ], N110#[ 2 ], N011#[ 2 ], N101#[ 2 ] ;Vertexnormalen
         Local T100#[ 1 ], T010#[ 1 ], T001#[ 1 ] ;Texturkoordinaten
         
         Local V1 = TriangleVertex( Surf, TriIndex, 0 )
         Local V2 = TriangleVertex( Surf, TriIndex, 1 )
         Local V3 = TriangleVertex( Surf, TriIndex, 2 )
         
         P003[ 0 ] = VertexX( Surf, V1 ) ;Die wichtigsten Werte zwischenspeichern
         P003[ 1 ] = VertexY( Surf, V1 )
         P003[ 2 ] = VertexZ( Surf, V1 )
         P300[ 0 ] = VertexX( Surf, V2 )
         P300[ 1 ] = VertexY( Surf, V2 )
         P300[ 2 ] = VertexZ( Surf, V2 )
         P030[ 0 ] = VertexX( Surf, V3 )
         P030[ 1 ] = VertexY( Surf, V3 )
         P030[ 2 ] = VertexZ( Surf, V3 )
         
         N002[ 0 ] = VertexNX( Surf, V1 )
         N002[ 1 ] = VertexNY( Surf, V1 )
         N002[ 2 ] = VertexNZ( Surf, V1 )
         N200[ 0 ] = VertexNX( Surf, V2 )
         N200[ 1 ] = VertexNY( Surf, V2 )
         N200[ 2 ] = VertexNZ( Surf, V2 )
         N020[ 0 ] = VertexNX( Surf, V3 )
         N020[ 1 ] = VertexNY( Surf, V3 )
         N020[ 2 ] = VertexNZ( Surf, V3 )
         
         T001[ 0 ] = VertexU( Surf, V1 )
         T001[ 1 ] = VertexV( Surf, V1 )
         T010[ 0 ] = VertexU( Surf, V2 )
         T010[ 1 ] = VertexV( Surf, V2 )
         T100[ 0 ] = VertexU( Surf, V3 )
         T100[ 1 ] = VertexV( Surf, V3 )
         
         InterpolateControlPoint( P003, P300, N002, P102 ) ;Die Kontrollpunkte interpolieren
         InterpolateControlPoint( P300, P003, N200, P201 )
         
         InterpolateControlPoint( P003, P030, N002, P012 )
         InterpolateControlPoint( P030, P003, N020, P021 )
         
         InterpolateControlPoint( P300, P030, N200, P210 )
         InterpolateControlPoint( P030, P300, N020, P120 )
         
         ;Den letzten Kontrollpunkt müssen wir anders berechnen
         P111[ 0 ] = ( P102[ 0 ] + P201[ 0 ] + P012[ 0 ] + P210 [ 0 ] + P021[ 0 ] + P120[ 0 ] )/4. - ( P003[ 0 ] + P030[ 0 ] + P300[ 0 ] )/6.
         P111[ 1 ] = ( P102[ 1 ] + P201[ 1 ] + P012[ 1 ] + P210 [ 1 ] + P021[ 1 ] + P120[ 1 ] )/4. - ( P003[ 1 ] + P030[ 1 ] + P300[ 1 ] )/6.
         P111[ 2 ] = ( P102[ 2 ] + P201[ 2 ] + P012[ 2 ] + P210 [ 2 ] + P021[ 2 ] + P120[ 2 ] )/4. - ( P003[ 2 ] + P030[ 2 ] + P300[ 2 ] )/6.
         
         InterpolateNormal( N002, N020, P003, P030, N011 ) ;Normalen der Kontrollpunkte interpolieren
         InterpolateNormal( N200, N020, P300, P030, N110 )
         InterpolateNormal( N200, N002, P300, P003, N101 )
         
         Dim Vertices( LOD, LOD ) ;Vertexarray für schnellen Zugriff
         
         For A = 0 To LOD ;Vertices je nach LOD - Grad erstellen
            For B = 0 To LOD
               If A + B <= LOD Then
                  Local U# = A/Float( LOD ) ;Index in barycentrische Koordinaten umrechnen
                  Local V# = B/Float( LOD )
                  Local W# = 1 - U# - V#
                  
                  ;Position mithilfe eines kubischen Splines ermitteln
                  ;Die Formel ist leider gigantisch, weswegen ich sie auf mehrere Zeilen verteilt habe
                  Local VX# = U#*U#*U#*P300[ 0 ] + V#*V#*V#*P030[ 0 ] + W#*W#*W#*P003[ 0 ] + 3*U#*U#*V#*P210[ 0 ]
                        VX# = VX# + 3*U#*U#*W#*P201[ 0 ] + 3*U#*V#*V#*P120[ 0 ] + 3*V#*V#*W#*P021[ 0 ]
                        VX# = VX# + 3*V#*W#*W#*P012[ 0 ] + 3*U#*W#*W#*P102[ 0 ] + 6*U#*V#*W#*P111[ 0 ]
                  
                  Local VY# = U#*U#*U#*P300[ 1 ] + V#*V#*V#*P030[ 1 ] + W#*W#*W#*P003[ 1 ] + 3*U#*U#*V#*P210[ 1 ]
                        VY# = VY# + 3*U#*U#*W#*P201[ 1 ] + 3*U#*V#*V#*P120[ 1 ] + 3*V#*V#*W#*P021[ 1 ]
                        VY# = VY# + 3*V#*W#*W#*P012[ 1 ] + 3*U#*W#*W#*P102[ 1 ] + 6*U#*V#*W#*P111[ 1 ]
                  
                  Local VZ# = U#*U#*U#*P300[ 2 ] + V#*V#*V#*P030[ 2 ] + W#*W#*W#*P003[ 2 ] + 3*U#*U#*V#*P210[ 2 ]
                        VZ# = VZ# + 3*U#*U#*W#*P201[ 2 ] + 3*U#*V#*V#*P120[ 2 ] + 3*V#*V#*W#*P021[ 2 ]
                        VZ# = VZ# + 3*V#*W#*W#*P012[ 2 ] + 3*U#*W#*W#*P102[ 2 ] + 6*U#*V#*W#*P111[ 2 ]
                  
                  ;Vertexnormale auf dem quadratischen Spline ermitteln
                  Local NX# = U#*U#*N200[ 0 ] + V#*V#*N020[ 0 ] + W#*W#*N002[ 0 ] + U#*V#*N110[ 0 ] + U#*W#*N101[ 0 ] + V#*W#*N011[ 0 ]
                  Local NY# = U#*U#*N200[ 1 ] + V#*V#*N020[ 1 ] + W#*W#*N002[ 1 ] + U#*V#*N110[ 1 ] + U#*W#*N101[ 1 ] + V#*W#*N011[ 1 ]
                  Local NZ# = U#*U#*N200[ 2 ] + V#*V#*N020[ 2 ] + W#*W#*N002[ 2 ] + U#*V#*N110[ 2 ] + U#*W#*N101[ 2 ] + V#*W#*N011[ 2 ]
                  
                  ;Je nach dem ist die berechnete Normale nicht normalisiert - das holen wir nach
                  TFormNormal NX#, NY#, NZ#, 0, 0
                  
                  ;Schlussendlich Texturkoordinaten linear interpolieren
                  Local TU# = U#*T010[ 0 ] + V#*T100[ 0 ] + W#*T001[ 0 ]
                  Local TV# = U#*T010[ 1 ] + V#*T100[ 1 ] + W#*T001[ 1 ]
                  
                  Vertices( A, B ) = AddVertex( NewSurf, VX#, VY#, VZ#, TU#, TV# ) ;Vertex hinzufügen
                  VertexNormal NewSurf, Vertices( A, B ), TFormedX(), TFormedY(), TFormedZ() ;Normale setzen
               EndIf
            Next
         Next
         
         For A = 0 To LOD - 1 ;Dreiecke erstellen
            For B = 0 To LOD - 1
               If A + B < LOD Then
                  AddTriangle NewSurf, Vertices( A, B ), Vertices( A + 1, B ), Vertices( A, B + 1 )
                  If A + B < LOD - 1 Then AddTriangle NewSurf, Vertices( A + 1, B + 1 ), Vertices( A, B + 1 ), Vertices( A + 1, B )
               EndIf
            Next
         Next
      Next
   Next
   
   PaintEntity NewMesh, Brush
   
   Return NewMesh
End Function

Function InterpolateControlPoint( P1#[ 2 ], P2#[ 2 ], N#[ 2 ], Result#[ 2 ] )
   Result[ 0 ] = ( 2*P1[ 0 ] + P2[ 0 ] )/3. ;Den Punkt auf der Strecke P1-P2 positionieren und auf die Normalenebene durch P1 projizieren
   Result[ 1 ] = ( 2*P1[ 1 ] + P2[ 1 ] )/3.
   Result[ 2 ] = ( 2*P1[ 2 ] + P2[ 2 ] )/3.
   
   Local D# = -( P1[ 0 ]*N[ 0 ] + P1[ 1 ]*N[ 1 ] + P1[ 2 ]*N[ 2 ] )
   
   Local W# = N[ 0 ]*Result[ 0 ] + N[ 1 ]*Result[ 1 ] + N[ 2 ]*Result[ 2 ] + D#
   
   Result[ 0 ] = Result[ 0 ] - W#*N[ 0 ]
   Result[ 1 ] = Result[ 1 ] - W#*N[ 1 ]
   Result[ 2 ] = Result[ 2 ] - W#*N[ 2 ]
End Function

Function InterpolateNormal( N1#[ 2 ], N2#[ 2 ], P1#[ 2 ], P2#[ 2 ], Result#[ 2 ] )
   Local DX# = P2[ 0 ] - P1[ 0 ] ;Durchschnittsnormale errechnen und an der Mittelnormalebene zu P1-P2 spiegeln
   Local DY# = P2[ 1 ] - P1[ 1 ]
   Local DZ# = P2[ 2 ] - P1[ 2 ]
   
   Local Nominator# = DX#*( N1[ 0 ] + N2[ 0 ] ) + DY#*( N1[ 1 ] + N2[ 1 ] ) + DZ#*( N1[ 2 ] + N2[ 2 ] )
   Local Denominator# = DX#*DX# + DY#*DY# + DZ#*DZ#
   Local Fraction# = 2*Nominator#/Denominator#
   
   Result[ 0 ] = N1[ 0 ] + N2[ 0 ] - Fraction#*DX#
   Result[ 1 ] = N1[ 1 ] + N2[ 1 ] - Fraction#*DY#
   Result[ 2 ] = N1[ 2 ] + N2[ 2 ] - Fraction#*DZ#
End Function

Function SetNormals( Entity, Scale# = 1 )
   For SurfIndex = 1 To CountSurfaces( Entity )
      Local Surf = GetSurface( Entity, SurfIndex )
      
      For i = 0 To CountVertices( Surf ) - 1
         TFormNormal VertexX( Surf, i ), VertexY( Surf, i ), VertexZ( Surf, i ), 0, 0
         VertexNormal Surf, i, TFormedX()*Scale#, TFormedY()*Scale#, TFormedZ()*Scale#
      Next
   Next
End Function

Mit gedrückter linker Maustaste kann man die Wireframe - Ansicht einblenden.

Screenshot mit Textur (links das Ursprungsmesh, rechts das interpolierte):
user posted image
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun
  • Zuletzt bearbeitet von Noobody am Mi, Mai 27, 2009 19:35, insgesamt einmal bearbeitet

Chrise

Betreff: Re: Normalen - Interpolation für 3D Modelle

BeitragMi, Mai 27, 2009 18:47
Antworten mit Zitat
Benutzer-Profile anzeigen
Hui, nice!
Vorallem bei Sphere(8) war ich überrascht, wie "rund" das ganze lief Wink
Llama 1 Llama 2 Llama 3
Vielen Dank an Pummelie, der mir auf seinem Server einen Platz für LlamaNet bietet.

ozzi789

BeitragMi, Mai 27, 2009 19:25
Antworten mit Zitat
Benutzer-Profile anzeigen
Super wie immer Very Happy

Als attackierend gemeldete Website! -Gamasutra
??

mfg ozzi
0x2B || ! 0x2B
C# | C++13 | Java 7 | PHP 5

Noobody

BeitragMi, Mai 27, 2009 19:35
Antworten mit Zitat
Benutzer-Profile anzeigen
ozzi789 hat Folgendes geschrieben:
Als attackierend gemeldete Website! -Gamasutra

Ja, die Meldung kam bei mir auch eben. Eigentlich schade, die Seite war vorgestern noch in Ordnung Confused

Naja, ich nehm den Link vorerst mal raus, bis die das wieder beheben (scheinbar hat jemand Schadcode eingeschleust).
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun
 

Kruemelator

BeitragDo, Mai 28, 2009 12:12
Antworten mit Zitat
Benutzer-Profile anzeigen
Sehr interessant!

Wenn ich LOD >= 74 habe dann gibt es bei mir einen Fehler. Das Mesh wird nicht richtig angezeigt, es fehlen Polygone.

FireballFlame

BeitragDo, Mai 28, 2009 13:58
Antworten mit Zitat
Benutzer-Profile anzeigen
74 ist ja auch ein bisschen arg übertrieben
PC: Intel Core i7 @ 4x2.93GHz | 6 GB RAM | Nvidia GeForce GT 440 | Desktop 2x1280x1024px | Windows 7 Professional 64bit
Laptop: Intel Core i7 @ 4x2.00GHz | 8 GB RAM | Nvidia GeForce GT 540M | Desktop 1366x768px | Windows 7 Home Premium 64bit

Lord_Vader

BeitragDo, Mai 28, 2009 14:32
Antworten mit Zitat
Benutzer-Profile anzeigen
Sehr nett. Vorallem für die einstellungen Wink

Noobody

BeitragDo, Mai 28, 2009 16:04
Antworten mit Zitat
Benutzer-Profile anzeigen
Kruemelator hat Folgendes geschrieben:
Wenn ich LOD >= 74 habe dann gibt es bei mir einen Fehler. Das Mesh wird nicht richtig angezeigt, es fehlen Polygone.

Logisch, ab LOD > 73 sprengst du die magische Grenze von 65536 erlaubten Triangles in DirectX 7.
Man könnte das natürlich umgehen, indem man eine neue Surface erstellt, sobald man an die Grenze kommt; allerdings halte ich das wenig sinnvoll - bereits ab LOD 15 ist keine sichtbare Verfeinerung mehr festzustellen, und mehr als 65536 Polygone pro Mesh ist sowieso der Tod der anständigen Performance Razz
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun
 

Kruemelator

BeitragDo, Mai 28, 2009 16:45
Antworten mit Zitat
Benutzer-Profile anzeigen
Ein MAV erreiche ich aber erst bei LOD = 104. Wenn ich trisrendered anzeigen lasse dann habe ich bei LOD 103 den Wert 127320.
Aber der Sinn fehlt, da hast du recht Laughing

Noobody

BeitragDo, Mai 28, 2009 17:00
Antworten mit Zitat
Benutzer-Profile anzeigen
Kruemelator hat Folgendes geschrieben:
Ein MAV erreiche ich aber erst bei LOD = 104. Wenn ich trisrendered anzeigen lasse dann habe ich bei LOD 103 den Wert 127320.

Der MAV ist von Situation zu Situation unterschiedlich, aber TrisRendered sagt ja schon ziemlich viel aus (127320 ist weit über den erlaubten 65536, daher Anzeigefehler).
Alternativ kannst du dir die Polygonzahl mal per CountTriangles anzeigen lassen, dann siehst du das Problem Wink
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun

Neue Antwort erstellen


Übersicht BlitzBasic Codearchiv

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group