Simpler Software - Rasterizer

Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Neue Antwort erstellen

Noobody

Betreff: Simpler Software - Rasterizer

BeitragDi, Jun 23, 2009 20:53
Antworten mit Zitat
Benutzer-Profile anzeigen
Als Einstiegsprojekt in BMax habe ich einen kleinen Software - Rasterizer gebastelt.

Für diejenigen, denen der Begriff noch nicht so vertraut ist: Ein Rasterizer rendert Vektorfiguren, so dass sie auf dem Bildschirm dargestellt werden können. In diesem Fall also zeichnet das Programm ein paar 3D - Objekte, wie sie von einer Kamera aufgenommen werden - Eine sehr simple Software - Grafikkarte, wenn man so will.

Um dies zu erreichen, bedient sich das Programm allerhand Vektor- und Matrizenrechnungen. Die Rechenreihenfolge habe ich aus schwachen Erinnerungen an einen Artikel über 3D - Transformationen in der Grafikkarte und Trial and Error hergeleitet, kann also sein, dass ich das sehr viel umständlicher mache, als es normalerweise gemacht wird Razz

Da ich mich mit OGL noch nicht wirklich auskenne, werden die Objekte einfach als Wireframe gezeichnet. Wenn zwei Objekte übereinander liegen, ist also nicht direkt erkennbar, welches Objekt nun vorne ist und welches hinten (ist manchmal ziemlich verwirrend).

Abgesehen davon läuft das Programm aber relativ gut. Objekte werden nach B3D - ähnlichem Prinzip bewegt, skaliert und rotiert, einzig den Positionsbefehl musste ich von 'Position' in 'Locate' umtaufen, weil ein Field des Entity - Types den selben Namen hatte.

Ein kleiner Screenshot:
user posted image

Der Code: [AUSKLAPPEN]
SuperStrict

Const GWIDTH:Int = 800
Const GHEIGHT:Int = 600

Graphics GWIDTH, GHEIGHT, 0

Local Timer:TTimer = CreateTimer( 60 )

Local Cam:TCamera = TCamera.Camera()
Cam.Move( Vec3( 0, 0, -15 ) )

Local Mesh:TMesh = Cam.AddMesh()
Mesh.CreateCube()

Local Cube:TMesh = Cam.AddMesh()
Cube.CreateCube()

Local Pivot:TEntity = New TEntity
Cam.SetParent( Pivot )

MoveMouse( GWIDTH/2, GHEIGHT/2 )

Local OldMouseX:Int = MouseX(), OldMouseY:Int = MouseY(), OldMouseZ:Int = MouseZ()

While Not KeyHit( KEY_ESCAPE )
   Cls
   
   Cam.Render( GWIDTH, GHEIGHT )
   
   Pivot.Turn( Vec3( OldMouseY - MouseY(), OldMouseX - MouseX(), 0 ) )
   Cam.Move( Vec3( 0, 0, MouseZ() - OldMouseZ ) )
   
   Local Angle:Float = ( MilliSecs() Mod 3600 )/10.0
   
   Mesh.Turn( Vec3( 1, 2, 3 ) )
   Mesh.Locate( Vec3( Sin( Angle )*2, 0, 0 ) )
   
   Cube.Locate( Vec3( Cos( Angle )*3, Sin( Angle*2 )*2, 5 ) )
   Cube.Turn( Vec3( 0, 2, 0 ) )
   Cube.Scale( Vec3( Cos( Angle ) + 1.5, 0.5, 1 ) )
   
   OldMouseX = MouseX()
   OldMouseY = MouseY()
   OldMouseZ = MouseZ()
   
   Flip 0
   WaitTimer Timer
Wend
End

Function Vec3:TVector( X:Float, Y:Float, Z:Float )
   Return TVector.Vector( X, Y, Z )
End Function

Function EntityCoords:String( Entity:TEntity )
   If Entity.Parent Then Entity.Parent.InvTransform()
   Entity.Transform()
   
   Local Mat:TMatrix = Entity.Parent.InvFinalTransform.Copy().MatrixMultiply( Entity.FinalTransform )
   
   Return Mat.A[ 0, 3 ] + " : " + Mat.A[ 1, 3 ] + " : " + Mat.A[ 2, 3 ]
End Function

Type TVector
   Field X:Float
   Field Y:Float
   Field Z:Float
   Field W:Float
   
   Method Add:TVector( B:TVector )
      Self.X :+ B.X
      Self.Y :+ B.Y
      Self.Z :+ B.Z
      
      Return Self
   End Method
   
   Method Substract:TVector( B:TVector )
      Self.X :- B.X
      Self.Y :- B.Y
      Self.Z :- B.Z
      
      Return Self
   End Method
   
   Method Multiply:TVector( Scalar:Float )
      Self.X :* Scalar
      Self.Y :* Scalar
      Self.Z :* Scalar
      
      Return Self
   End Method
   
   Method Invert:TVector()
      Self.X = 1/Self.X
      Self.Y = 1/Self.Y
      Self.Z = 1/Self.Z
      
      Return Self
   End Method
   
   Method DotProduct:Float( B:TVector )
      Return Self.X*B.X + Self.Y*B.Y + Self.Z*B.Z
   End Method
   
   Method CrossProduct:TVector( B:TVector )
      Local NewX:Float = Self.Y*B.Z - Self.Z*B.Y
      Local NewY:Float = Self.Z*B.X - Self.X*B.Z
      Local NewZ:Float = Self.X*B.Y - Self.Y*B.X
      
      Self.X = NewX
      Self.Y = NewY
      Self.Z = NewZ
      
      Return Self
   End Method
   
   Method Copy:TVector()
      Return TVector.Vector( Self.X, Self.Y, Self.Z, Self.W )
   End Method
   
   Function Vector:TVector( X:Float, Y:Float, Z:Float, W:Float = 1 )
      Local Vector:TVector = New TVector
         Vector.X = X
         Vector.Y = Y
         Vector.Z = Z
         Vector.W = W
      
      Return Vector
   End Function
End Type

Type TMatrix
   Field A:Float[ 4, 4 ]
   
   Method Copy:TMatrix()
      Local Matrix:TMatrix = New TMatrix
      
      For Local Row:Int = 0 To 3
         For Local Column:Int = 0 To 3
            Matrix.A[ Row, Column ] = Self.A[ Row, Column ]
         Next
      Next
      
      Return Matrix
   End Method
   
   Method MatrixMultiply:TMatrix( B:TMatrix )
      Local NewA:Float[ 4, 4 ]
      
      For Local Row:Int = 0 To 3
         For Local Column:Int = 0 To 3
            NewA[ Row, Column ]  = Self.A[ Row, 0 ]*B.A[ 0, Column ]
            NewA[ Row, Column ] :+ Self.A[ Row, 1 ]*B.A[ 1, Column ]
            NewA[ Row, Column ] :+ Self.A[ Row, 2 ]*B.A[ 2, Column ]
            NewA[ Row, Column ] :+ Self.A[ Row, 3 ]*B.A[ 3, Column ]
         Next
      Next
      
      Self.A = NewA
      
      Return Self
   End Method
   
   Method VectorMultiply:TVector( B:TVector )
      Local X:Float = Self.A[ 0, 0 ]*B.X + Self.A[ 0, 1 ]*B.Y + Self.A[ 0, 2 ]*B.Z + Self.A[ 0, 3 ]*B.W
      Local Y:Float = Self.A[ 1, 0 ]*B.X + Self.A[ 1, 1 ]*B.Y + Self.A[ 1, 2 ]*B.Z + Self.A[ 1, 3 ]*B.W
      Local Z:Float = Self.A[ 2, 0 ]*B.X + Self.A[ 2, 1 ]*B.Y + Self.A[ 2, 2 ]*B.Z + Self.A[ 2, 3 ]*B.W
      Local W:Float = Self.A[ 3, 0 ]*B.X + Self.A[ 3, 1 ]*B.Y + Self.A[ 3, 2 ]*B.Z + Self.A[ 3, 3 ]*B.W
      
      Return TVector.Vector( X, Y, Z, W )
   End Method
   
   Function Identity:TMatrix()
      Local Matrix:TMatrix = New TMatrix
         Matrix.A[ 0, 0 ] = 1
         Matrix.A[ 1, 1 ] = 1
         Matrix.A[ 2, 2 ] = 1
         Matrix.A[ 3, 3 ] = 1
      
      Return Matrix
   End Function
   
   Function Translation:TMatrix( Vector:TVector )
      Local Matrix:TMatrix = TMatrix.Identity()
         Matrix.A[ 0, 3 ] = Vector.X
         Matrix.A[ 1, 3 ] = Vector.Y
         Matrix.A[ 2, 3 ] = Vector.Z
      
      Return Matrix
   End Function
   
   Function Scale:TMatrix( Vector:TVector )
      Local Matrix:TMatrix = New TMatrix
         Matrix.A[ 0, 0 ] = Vector.X
         Matrix.A[ 1, 1 ] = Vector.Y
         Matrix.A[ 2, 2 ] = Vector.Z
         Matrix.A[ 3, 3 ] = 1
      
      Return Matrix
   End Function
   
   Function RotationX:TMatrix( Angle:Float )
      Local Matrix:TMatrix = TMatrix.Identity()
         Matrix.A[ 1, 1 ] =  Cos( Angle )
         Matrix.A[ 1, 2 ] =  Sin( Angle )
         Matrix.A[ 2, 1 ] = -Sin( Angle )
         Matrix.A[ 2, 2 ] =  Cos( Angle )
      
      Return Matrix
   End Function
   
   Function RotationY:TMatrix( Angle:Float )
      Local Matrix:TMatrix = TMatrix.Identity()
         Matrix.A[ 0, 0 ] =  Cos( Angle )
         Matrix.A[ 0, 2 ] =  Sin( Angle )
         Matrix.A[ 2, 0 ] = -Sin( Angle )
         Matrix.A[ 2, 2 ] =  Cos( Angle )
      
      Return Matrix
   End Function
   
   Function RotationZ:TMatrix( Angle:Float )
      Local Matrix:TMatrix = TMatrix.Identity()
         Matrix.A[ 0, 0 ] =  Cos( Angle )
         Matrix.A[ 0, 1 ] =  Sin( Angle )
         Matrix.A[ 1, 0 ] = -Sin( Angle )
         Matrix.A[ 1, 1 ] =  Cos( Angle )
      
      Return Matrix
   End Function
   
   Function RotationXYZ:TMatrix( Rotation:TVector )
      Return TMatrix.RotationZ( Rotation.Z ).MatrixMultiply( RotationY( Rotation.Y ).MatrixMultiply( RotationX( Rotation.X ) ) )
   End Function
   
   Function RotationZYX:TMatrix( Rotation:TVector )
      Return TMatrix.RotationX( Rotation.X ).MatrixMultiply( RotationY( Rotation.Y ).MatrixMultiply( RotationZ( Rotation.Z ) ) )
   End Function
   
   Method DebugDraw( X:Int, Y:Int )
      DrawLine X - 5, Y - 6, X - 5, Y + 94 'Hardcoding \o/
      DrawLine X - 5, Y - 6, X + 2, Y - 6
      DrawLine X - 5, Y + 94, X + 2, Y + 94
      
      Local ColumnWidth:Int
      For Local Column:Int = 0 To 3
         For Local Row:Int = 0 To 3
            Local Element:Float = Self.A[ Row, Column ]
            
            If Element < 0 Then
               DrawText Element, X, Y + Row*25
            Else
               DrawText Element, X + TextWidth( "-" ), Y + Row*25
            EndIf
            
            If TextWidth( Element ) > ColumnWidth Then ColumnWidth = TextWidth( Element )
         Next
         
         X :+ 20 + ColumnWidth
         ColumnWidth = 0
      Next
      
      DrawLine X - 5, Y - 6, X - 5, Y + 94
      DrawLine X - 5, Y - 6, X - 12, Y - 6
      DrawLine X - 5, Y + 94, X - 12, Y + 94
   End Method
End Type

Type TVertex Extends TVector
   Field TFormedCoords:TVector
   Field ProjectedCoords:TVector
   
   Method Copy:TVertex()
      Return TVertex.Vertex( Vec3( Self.X, Self.Y, Self.Z ) )
   End Method
   
   Function Vertex:TVertex( Position:TVector )
      Local Vertex:TVertex = New TVertex
         Vertex.X = Position.X
         Vertex.Y = Position.Y
         Vertex.Z = Position.Z
         Vertex.W = 1
         Vertex.ProjectedCoords = Vec3( 0, 0, 0 )
      
      Return Vertex
   End Function
End Type

Type TTriangle
   Field V1:TVertex
   Field V2:TVertex
   Field V3:TVertex
   
   Field Normal:TVector
   
   Function Triangle:TTriangle( V1:TVertex, V2:TVertex, V3:TVertex )
      Local Triangle:TTriangle = New TTriangle
         Triangle.V1 = V1
         Triangle.V2 = V2
         Triangle.V3 = V3
         
         Triangle.Normal = V3.Copy().Substract( V1 ).CrossProduct( V2.Copy().Substract( V1 ) )
      
      Return Triangle
   End Function
End Type

Type TEntity
   Field Parent:TEntity
   Field FinalTransform:TMatrix
   Field InvFinalTransform:TMatrix
   
   Field Position:TVector
   Field Scaling:TVector
   Field Rotation:TMatrix
   Field InvRotation:TMatrix
   
   Method New()
      Self.Locate( Vec3( 0, 0, 0 ) )
      Self.Rotate( Vec3( 0, 0, 0 ) )
      Self.Scale ( Vec3( 1, 1, 1 ) )
   End Method
   
   Method Turn( Vector:TVector )
      Self.Rotation = TMatrix.RotationXYZ( Vector ).MatrixMultiply( Self.Rotation )
      Self.InvRotation = Self.InvRotation.MatrixMultiply( TMatrix.RotationZYX( Vector.Multiply( -1 ) ) )
   End Method
   
   Method Move( Vector:TVector )
      Self.Position.Add( Vector )
   End Method
   
   Method Rotate( Vector:TVector, GlobalSystem:Int = False )
      If GlobalSystem And Self.Parent <> Null Then
         Throw "Global rotation not yet implementet!"
      Else
         Self.Rotation = TMatrix.RotationXYZ( Vector )
         Self.InvRotation = TMatrix.RotationZYX( Vector.Multiply( -1 ) )
      EndIf
   End Method
   
   Method Locate( Vector:TVector, GlobalSystem:Int = False )
      If GlobalSystem And Self.Parent <> Null Then
         Self.Parent.Transform()
         
         Self.Position = Self.Parent.FinalTransform.VectorMultiply( Vector )
      Else
         Self.Position = Vector
      EndIf
   End Method
   
   Method Scale( Vector:TVector )
      Self.Scaling = Vector
   End Method
   
   Method SetParent( Entity:TEntity )
      Self.Parent = Entity
   End Method
   
   Method Transform()
      Self.FinalTransform = TMatrix.Translation( Self.Position ).Copy().MatrixMultiply( Self.Rotation.Copy().MatrixMultiply( TMatrix.Scale( Self.Scaling ) ) )
      
      If Self.Parent Then
         Self.Parent.Transform()
         
         Self.FinalTransform.MatrixMultiply( Self.Parent.FinalTransform )
      EndIf
   End Method
   
   Method InvTransform()
      Self.InvFinalTransform = TMatrix.Translation( Self.Position.Copy().Multiply( -1 ) ).MatrixMultiply( Self.InvRotation.Copy().MatrixMultiply( TMatrix.Scale( Self.Scaling ) ) )
      
      If Self.Parent Then
         Self.Parent.InvTransform()
         
         Self.InvFinalTransform = Self.Parent.InvFinalTransform.Copy().MatrixMultiply( Self.InvFinalTransform )
      EndIf
   End Method
End Type

Type TCamera Extends TEntity
   Field AOV:Float 'Angle of view
   
   Field Meshes:TList
   
   Method AddMesh:TMesh()
      Local Mesh:TMesh = TMesh.Mesh()
      
      Self.Meshes.AddLast( Mesh )
      
      Return Mesh
   End Method
   
   Method Render( ScreenWidth:Int, ScreenHeight:Int )
      Local PlaneDist:Float = 1/Tan( Self.AOV/2.0 )
      
      Self.Transform()
      
      For Local Mesh:TMesh = EachIn Self.Meshes
         Mesh.InvTransform()
      Next
      
      For Local Mesh:TMesh = EachIn Self.Meshes
         For Local Vertex:TVertex = EachIn Mesh.Vertices
            Vertex.TFormedCoords = Self.FinalTransform.VectorMultiply( Mesh.InvFinalTransform.VectorMultiply( Vertex.Copy() ) )
            
            Vertex.ProjectedCoords.X = ( Vertex.TFormedCoords.X*PlaneDist/Vertex.TFormedCoords.Z + 1 )*ScreenWidth/2
            Vertex.ProjectedCoords.Y = ( ScreenHeight/Float( ScreenWidth ) - Vertex.TFormedCoords.Y*PlaneDist/Vertex.TFormedCoords.Z )*ScreenWidth/2
         Next
         
         For Local Triangle:TTriangle = EachIn Mesh.Triangles
            DrawLine Triangle.V1.ProjectedCoords.X, Triangle.V1.ProjectedCoords.Y, Triangle.V2.ProjectedCoords.X, Triangle.V2.ProjectedCoords.Y
            DrawLine Triangle.V2.ProjectedCoords.X, Triangle.V2.ProjectedCoords.Y, Triangle.V3.ProjectedCoords.X, Triangle.V3.ProjectedCoords.Y
            DrawLine Triangle.V3.ProjectedCoords.X, Triangle.V3.ProjectedCoords.Y, Triangle.V1.ProjectedCoords.X, Triangle.V1.ProjectedCoords.Y
         Next
      Next
   End Method
   
   Function Camera:TCamera( AOV:Float = 60 )
      Local Cam:TCamera = New TCamera
         Cam.AOV = AOV
         Cam.Meshes = New TList
      
      Return Cam
   End Function
End Type

Type TMesh Extends TEntity
   Field Vertices:TList
   Field Triangles:TList
   
   Method AddVertex:TVertex( Position:TVector )
      Local Vertex:TVertex = TVertex.Vertex( Position )
      
      Self.Vertices.AddLast( Vertex )
      
      Return Vertex
   End Method
   
   Method AddTriangle:TTriangle( V1:TVertex, V2:TVertex, V3:TVertex )
      Local Triangle:TTriangle = TTriangle.Triangle( V1, V2, V3 )
      
      Self.Triangles.AddLAst( Triangle )
      
      Return Triangle
   End Method
   
   Method CreateCube()
      Local V1:TVertex = Self.AddVertex( Vec3( -1, -1, -1 ) )
      Local V2:TVertex = Self.AddVertex( Vec3( -1, -1,  1 ) )
      Local V3:TVertex = Self.AddVertex( Vec3(  1, -1,  1 ) )
      Local V4:TVertex = Self.AddVertex( Vec3(  1, -1, -1 ) )
      
      Local V5:TVertex = Self.AddVertex( Vec3( -1,  1, -1 ) )
      Local V6:TVertex = Self.AddVertex( Vec3( -1,  1,  1 ) )
      Local V7:TVertex = Self.AddVertex( Vec3(  1,  1,  1 ) )
      Local V8:TVertex = Self.AddVertex( Vec3(  1,  1, -1 ) )
      
      Self.AddTriangle V1, V3, V2
      Self.AddTriangle V1, V4, V3
      
      Self.AddTriangle V5, V6, V7
      Self.AddTriangle V5, V7, V8
      
      Self.AddTriangle V3, V8, V7
      Self.AddTriangle V3, V4, V8
      
      Self.AddTriangle V1, V5, V8
      Self.AddTriangle V1, V8, V4
      
      Self.AddTriangle V1, V2, V6
      Self.AddTriangle V1, V6, V5
      
      Self.AddTriangle V2, V7, V6
      Self.AddTriangle V2, V3, V7
   End Method
   
   Function Mesh:TMesh()
      Local Mesh:TMesh = New TMesh
      
      Mesh.Vertices = New TList
      Mesh.Triangles = New TList
      
      Return Mesh
   End Function
End Type


Bei Verbesserungsvorschlägen und Tipps bin ich ganz Ohr, bei mir als BMax - Neuling haben sich sicher noch ein paar Schönheitsfehler reingeschlichen Razz

Edit: Ich habe das Programm noch um ein kleines Parent-Child System erweitert.
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, Jul 15, 2009 20:49, insgesamt einmal bearbeitet

ComNik

BeitragDi, Jun 23, 2009 21:42
Antworten mit Zitat
Benutzer-Profile anzeigen
Zitat:
Als Einstiegsprojekt in BMax habe ich einen kleinen Software - Rasterizer gebastelt.


wenn ich das schon höre...

Tolle sache, und wie soll ich sagen ein sehr ausergehöhnliches EINSTEIGER Projekt...
Der sinn ist mir allerdings noch nicht so entgegengesprungen Embarassed

mfg
WIP: Vorx.Engine
 

Ava

Gast

BeitragDi, Jun 23, 2009 21:46
Antworten mit Zitat
Haha, wie geil ! Very Happy beschäftigen wir uns scheinbar grad mit sehr ähnlichem Krams, was? *g*

PS: wenn Du Hilfe / Denkanstösse zu OpenGL benötigst, kann ich Dir ggf. gern weiterhelfen Smile

Noobody

BeitragMi, Jun 24, 2009 13:15
Antworten mit Zitat
Benutzer-Profile anzeigen
ComNik hat Folgendes geschrieben:
Der sinn ist mir allerdings noch nicht so entgegengesprungen Embarassed

Naja, es ist auch nicht dazu gedacht, in einem Projekt verwendet zu werden (wie viele andere meiner Codes Razz ).
Es zeigt mehr, wie ein Rasterizer im Grunde funktioniert; ausserdem kann man die Vektor- bzw. Matrixklassen direkt rauskopieren, um diese in Verbindung mit irgendeiner anderen 3D - Bibliothek zu verwenden, die Matrizen erfordert.
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

Noobody

BeitragMi, Jul 15, 2009 21:01
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe den Code vor kurzem wieder ausgegraben und ihn als Vorbereitung auf ein ähnliches Projekt um ein Parent-Child System erweitert.
Dessen Funktionsweise ist gleich wie die in B3D - wenn man ein Objekt A als Parent des Objekts B setzt, so bewegt sich Objekt B automatisch mit, wenn A bewegt wird. Ausserdem (was fast noch wichtiger ist), dreht sich Objekt B um A, wenn ich A drehe.
Falls ich also einen Mond um die Erde drehen lassen will, setze ich die Erde als Parent des Mondes. Wenn ich nun die Erde drehe, dreht sich der Mond mit und umkreist fortan die Erde.

Im Beispielcode benutze ich das dazu, um einen unsichtbaren Punkt in die Mitte der Szene zu setzen, der als Parent der Kamera fungiert. Den Punkt lasse ich passend zu den Mausbewegungen drehen, wodurch man mit der Kamera die Szene aus beliebiger Richtung betrachten kann.

Den neuen Code habe ich oben hineineditiert; die Bedienung erfolgt mit der Maus inklusive Scrollrad.

Abgesehen von dem neu eingebauten System habe ich auch einige kleinere Bugs entfernt und zwei Hilfsfunktionen implementiert, um den Code eleganter zu machen.

Das wird wohl das letzte Update für diesen Code sein - da er abgesehen vom Lernerfolg eh relativ nutzlos ist, lohnt es sich wohl kaum, ihn weiter auszufeilen 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

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group