Dieser Artikel wurde maschinell übersetzt.
April 2014
DirectX-Faktor
3D-Transformationen für 2D-Bitmaps
Charles Petzold | April 2014
Dreidimensionale Grafik-Programmierung ist vor allem eine Frage optische Illusionen zu schaffen. Bilder werden auf einem Flachbildschirm, bestehend aus ein zweidimensionales Array von Pixel gerendert, aber diese Objekte müssen eine dritte Dimension mit Tiefe zu haben scheinen.
Wahrscheinlich der größte Beitrag zur Illusion von 3D Schattierung ist — die Kunst und Wissenschaft der Färbung Pixel, so dass Flächen realistischen Texturen mit Licht- und Schattenberechnung ähneln.
Unter all dem ist jedoch eine Infrastruktur von virtuellen Objekten, von 3D Koordinaten beschrieben. Letztendlich sind diese 3D Koordinaten ein 2D Weltraum abgeflacht, aber bis zu diesen letzten Schritt 3D Koordinaten werden oft systematisch auf verschiedene Weise durch den Einsatz von Transformationen geändert. Transformationen sind mathematisch, Vorgänge in der Matrizenalgebra. Erreichen eine fließend in diese 3D Transformationen ist entscheidend für jeden, der ein 3D Grafik-Programmierer werden will.
Vor kurzem habe ich mehrere Möglichkeiten untersucht, die in der Direct2D-Komponente von DirectX 3D unterstützt wird. 3D innerhalb der relativen Vertrautheit und Komfort von Direct2D erkunden, können Sie mit 3D Konzepte vor den sehr erschreckend tiefen Sprung in Direct3D vertraut zu machen.
Bitmaps in 3D?
Eines der mehrere Möglichkeiten, wie, die 3D in Direct2D unterstützt wird, ist als letztes Argument an die DrawBitmap-Methoden, die festgelegten ID2D1DeviceContext versteckt. Dieses Argument können Sie eine 3D Transformation in ein 2D Bitmap anwenden. (Diese Funktion ist besonders für ID2D1DeviceContext. Es wird nicht unterstützt von der DrawBitmap-Methode definiert durch ID2D1RenderTarget oder anderen Schnittstellen, die daraus abgeleitet werden.)
Durch ID2D1DeviceContext definierten DrawBitmap Methoden haben eine endgültige Argument vom Typ D2D1_MATRIX_4X4_F, die eine 4 × 4-Transformationsmatrix, die die Bitmap eine 3D-Transformation durchführt, wie er auf dem Bildschirm gerendert wird:
void DrawBitmap(ID2D1Bitmap *bitmap,
D2D1_RECT_F *destinationRectangle,
FLOAT opacity,
D2D1_INTERPOLATION_MODE interpolationMode,
const D2D1_RECT_F *sourceRectangle,
const D2D1_MATRIX_4X4_F *perspectiveTransform)
Dies scheint zu Zwecken der D2D1_MATRIX_4X4_F. Es ist nicht an anderer Stelle in DirectX verwendet. In Direct3D-Programmierung, wird stattdessen die DirectX-Mathematik-Bibliothek verwendet, 3D Transformationen darstellen.
DD2D1_MATRIX_4X4_F ist ein Typedef für D2D_MATRIX_4X4_F, definiert in Abbildung 1. Es ist im Grunde eine Sammlung von 16 Float-Werte, die in vier Reihen mit je vier Spalten angeordnet. Sie können den Wert in der dritten Zeile und zweiten Spalte mit den Daten Mitglied _32 verweisen können, oder Sie auf den gleichen Wert wie das nullbasiertes Array Element m [2] [1].
Abbildung 1 die 3D Transform Matrix auf Bitmaps angewendet
typedef struct D2D_MATRIX_4X4_F
{
union
{
struct
{
FLOAT _11, _12, _13, _14;
FLOAT _21, _22, _23, _24;
FLOAT _31, _32, _33, _34;
FLOAT _41, _42, _43, _44;
} ;
FLOAT m[4][4];
};
} D2D_MATRIX_4X4_F;
Allerdings werde wenn Sie zeigen, dass die Mathematik der Transformation, ich stattdessen das Element in der dritten Zeile und zweiten Spalte der Matrix als m32 bezeichnen. Die gesamte Matrix dargestellt werden kann, in traditionellen Matrix-Notation, wie folgt:
Die 3D Bitmap-Transformation in Direct2D unterliegt eine vergleichbare Einrichtung in der Windows-Runtime, wo es als die Matrix3D-Struktur und die Projection-Eigenschaft von UIElement definiert zeigt. Die beiden Mechanismen sind so ähnlich, dass Sie Ihr Wissen Cross-fertilize und erleben Sie zwischen den beiden Umgebungen.
Die lineare Transformation
Viele Menschen stoßen 3D-Transformationen zum ersten Mal Fragen: Warum ist es eine 4 × 4-Matrix? Angemessen sollte keine 3 × 3-Matrix 3D?
Zur Beantwortung dieser Frage beim Erkunden der 3D-Transformation auf DrawBitmap habe ich ein Programm namens BitmapTransformExperiment, die mit dem herunterladbaren Code für diesen Artikel enthalten hat. Dieses Programm enthält hausgemachte Spinner-Steuerelemente, mit denen Sie die Werte für die 16 Elemente der Transformationsmatrix auswählen und finden Sie unter Auswirkungen die Matrix die Anzeige einer Bitmap. Abbildung 2 zeigt eine typische Anzeige.
Abbildung 2 das BitmapTransformExperiment-Programm
Beschränken Sie für Ihre ersten Experimente Ihre Aufmerksamkeit auf den ersten drei Zeilen und die am weitesten links stehende drei Spalten der Matrix. Diese besteht aus einer 3 × 3-Matrix, die die folgende Transformation ausführt:
1 × 3-Matrix auf der linken Seite stellt eine 3D Koordinaten. Für die Bitmap, die X-Wert reicht von 0 auf die Bitmap-Breite; y liegt im Bereich von 0 bis zur Höhe der Bitmap. und z ist 0.
Wenn die 1 × 3-Matrix mit 3 × 3-Transformationsmatrix multipliziert wird, ergibt die standard Matrixmultiplikation transformierten Koordinaten:
Die Identitätsmatrix — in dem die Diagonalelemente m11, m22 und m33 alle 1 und alles andere sind ist 0 — keine Transformation führt.
Weil ich bin erst aus mit einer flachen Bitmap, die Z-Koordinate 0, m31, m32 und m33 haben daher keinen Einfluss auf das Ergebnis. Wenn die transformierte Bitmap auf dem Bildschirm dargestellt wird der (X*', y', Z') Ergebnis ist reduziert auf ein flach 2D Koordinatensystem durch das Ignorieren der z'* zu koordinieren, was bedeutet, dass m13, m23 und m33 wirkungslos. Deshalb der dritten Zeile und dritten Spalte in das BitmapTransformExperiment-Programm heraus grau unterlegt sind. Sie können Werte für diese Matrixelemente festlegen, sondern sie wirken sich nicht auf wie die Bitmap gerendert wird.
Was Sie entdecken, dass m11 ist ist eine horizontale Skalierungsfaktor mit einem Standardwert von 1. Machen Sie vergrößern oder verkleinern, vergrößern oder verkleinern die Bitmap-Breite, oder machen Sie es negativ für die Bitmap um die vertikale Achse drehen. M22 ist ebenso eine vertikale Skalierungsfaktor.
Der m21-Wert ist eine vertikale neigen Faktor: Werte ungleich 0 verwandeln die rechteckige Bitmap in ein Parallelogramm, wie am rechte Rand nach oben oder unten verschoben wird. Ebenso ist die m12 eine horizontale neigen Faktor.
Eine Kombination von neigen von horizontale und vertikale Neigung kann Drehung führen. Um die Bitmap, die von einem bestimmten Winkel im Uhrzeigersinn drehen, legen Sie m11 und m22 den Kosinus dieses Winkels, m21 den Sinus des Winkels und m12 den negativen Sinus festgelegt. Abbildung 2 Drehung um 30 Grad zeigt.
Im Zusammenhang mit 3D, die Rotation angezeigt, die Abbildung 2 ist eigentlich die Drehung um die Z-Achse, die konzeptionell, aus dem Bildschirm erweitert. Für einen Winkel von α sieht die Transformationsmatrix wie folgt:
Es ist auch möglich, um die Bitmap um die y-Achse oder die X-Achse zu drehen. Drehung um die y-Achse beeinflussen nicht die y-Koordinate, so ist die Transformationsmatrix dies:
Wenn Sie dies mit dem BitmapTransformExperiment-Programm versuchen, werden Sie feststellen, dass nur der m11-Wert eine Wirkung hat. Einstellung auf den Kosinus des einen Drehwinkel lediglich verringert sich die Breite der gerenderte Bitmap. Dieser Rückgang der Breite steht im Einklang mit Drehung um die y-Achse.
Ebenso ist die Drehung um die x-Achse:
In das BitmapTransformExperiment-Programm führt dies bei der Verringerung der Höhe der Bitmap.
Die Anzeichen für die zwei Sinus-Faktoren in der Transform-Matrizen Regeln der Drehrichtung. Im Prinzip wird angenommen, dass die positive Z-Achse um vom Bildschirm zu erweitern und die Rotationen Folgen der linken Regel: Richten Sie den Daumen der linken Hand mit der Drehachse und zeigen Sie es in Richtung positive Werte; die Kurve der anderen Finger gibt die Richtung der Drehung für positive Winkel.
Die Art der 3D-Transformation vertreten durch diese 3 × 3-Transformationsmatrix ist als lineare Transformation bekannt. Die Transformation umfasst nur Konstanten multipliziert mit der x-, y- und Z-Koordinaten. Egal, welche Zahlen Sie in den ersten drei Zeilen und Spalten der Transformationsmatrix eingeben, wird die Bitmap nie etwas exotischer als ein Parallelogramm umgewandelt; in drei Dimensionen ist ein Würfel immer einen Quader umgewandelt.
Du hast gesehen, wie die Bitmap, schiefe und gedrehte skaliert werden kann, aber während dieser Übung die oberen linken Ecke der Bitmap blieb fest an einem Ort. (Dieser Position unterliegt einer 2D Transformation inmitten der Render-Methode vor dem Aufruf von DrawBitmap.) Die Unfähigkeit die oberen linken Ecke der Bitmap verschieben ergibt sich aus der Mathematik der linearen Transformation. Gibt es nichts in der Transform-Formel, die verschieben kann ein (0, 0, 0) zeigen Sie an einen anderen Ort, das ist eine Art Transformation als Übersetzung bezeichnet.
Übersetzung zu erreichen
Um zu verstehen, so erhalten Sie die Übersetzung in 3D, kurz denken wir 2D Transformationen.
In zwei Dimensionen eine lineare Transformation ist eine 2 × 2-Matrix, und es kann skalieren, neigen und drehen. Um Übersetzung sowie zu erhalten, werden angenommen, dass die 2D Grafiken gibt es im dreidimensionalen Raum, sondern auf eine 2D-Ebene wo die Z-Koordinate immer gleich 1 ist. Um die zusätzliche Dimension zu berücksichtigen, wird die 2D lineare Transformationsmatrix in einer 3 × 3-Matrix erweitert, aber in der Regel die letzte Zeile steht fest:
Die Matrix-Multiplikation-Ergebnisse in diesen verwandeln Formeln:
Die m31 und m32 Faktoren sind die Übersetzung. Das Geheimnis hinter diesem Prozess ist, dass die Übersetzung in zwei Dimensionen neigen in drei Dimensionen entspricht.
Ein ähnliches Verfahren wird für 3D-Grafiken verwendet: Tatsächlich wird angenommen, dass der 3D Koordinaten im 4D Raum gelten, wenn die Koordinate der vierten Dimension 1. Aber um eine 4D-Koordinatenpunkt darzustellen, Sie haben ein albern etwas praktisches Problem: Ist ein 3D Punkt (X, y, Z) und kein Buchstabe kommt nach Z, also welchem Buchstaben verwenden Sie für die vierte Dimension? Der nächsten verfügbare Buchstaben ist w, so ist die 4D-Koordinate (X, y, Z, w).
3D lineare Transformationsmatrix wird auf 4 × 4, um Platz für die zusätzliche Dimension erweitert:
Die Transform-Formeln sind nun:
Sie haben nun die Verschiebung entlang der X, Y und Z Achsen mit m41, m42 und m43. Die Berechnung scheint in 4D Raum auftreten, aber es ist tatsächlich auf einen 3D-Querschnitt 4D Raum wo die w-Koordinate immer 1 ist beschränkt.
Homogene Koordinaten
Wenn Sie dann doch mit den m41 und m42 Werten in BitmapTransformExperiment spielen, sehen Sie, dass sie tatsächlich in der horizontalen und vertikalen Übersetzung führen.
Aber was ist mit der letzten Zeile der Matrix? Was passiert, wenn Sie nicht die letzte r. auf 0en und 1en zu beschränken? Hier ist die volle 4 × 4-Transformation auf ein 3D Punkt angewendet:
Die Formeln für x*', y'* und z*'* bleiben gleich, aber w*'* wird nun wie folgt berechnet:
Und das ist ein echtes Problem. Zuvor war ein kleiner Trick eingesetzt, um einen 3D-Querschnitt 4D Speicherplatz zu verwenden, wo die w-Koordinate immer gleich 1 ist. Aber jetzt ist die W-Koordinate nicht mehr 1 und Sie haben aus diesem 3D-Querschnitt angetrieben wurde. Du bist verloren in 4D Raum, und Sie müssen zu diesem 3D-Querschnitt zurück wo w ist gleich 1.
Es gibt keine Notwendigkeit, eine Trans-dimensionale Raum-Zeit-Maschine, jedoch aufzubauen. Zum Glück, du schaffst den Sprung mathematisch dividiert die transformierten Koordinaten von w*'*:
Nun w*'* gleich 1 und du bist wieder nach Hause!
Aber zu welchem Preis? Sie haben nun eine Division in der Transform-Formeln, und es ist leicht zu sehen, wie dieser Nenner 0 unter bestimmten Umständen sein könnte. Dies würde unendlich Koordinaten führen.
Nun, vielleicht ist das eine gute Sache.
Als deutscher Mathematiker August Ferdinand Möbius (1790) erfand das System, die, das ich gerade beschrieben habe ("Homogene Koordinaten" oder "projektive Koordinaten" genannt), war eines seiner Ziele zum unendliche Koordinaten mit endlichen Zahlen darstellen.
Die Matrix mit der 0en und 1en in der letzten Zeile heißt eine affine Transformation, was bedeutet, dass es nicht unendlich, führen daher eine Transformation der Unendlichkeit nicht affine Transformation bezeichnet.
In 3D Grafik sind nicht affinen Transformationen äußerst wichtig, denn dies ist, wie Perspektive erreicht wird. Jeder weiß, dass in der Realität Objekte weiter von deinem Auge kleiner angezeigt. In 3D Grafik erhalten Sie diesen Effekt mit einem Nenner, der eine Konstante 1 nicht.
Probieren Sie es aus in BitmapTransformExperiment: Wenn Sie m14 eine kleine positive Zahl machen — und nur kleine Werte für interessante Ergebnisse erforderlich sind — dann Werte von x und y sind proportional verringert, je x größer. M14 eine kleine negative Zahl, und größere Werte von x und y werden erhöht. Abbildung 3 zeigt, dass die Wirkung mit einem Wert ungleich NULL m12 kombiniert. Die gerenderte Bitmap ist nicht mehr ein Parallelogramm und die Perspektive legt nahe, dass der Rechte Rand näher an die Augen geschwungen hat.
Abbildung 3-Perspektive im BitmapTransformExperiment
Ebenso können NULL-Werte der m24 stellen der Ober- oder Unterseite der Bitmap scheinbar Schaukel zu sich hin oder weiter entfernt. In real 3D Programmierung, ist es der m34-Wert, der häufig verwendet wird, für das ermöglicht Objekte erhöht oder verringert in der Größe, die auf der Grundlage ihrer Z-Koordinaten — die Entfernung des Betrachters Augen.
Wenn eine 3D-Transformation auf 2D Objekte angewendet wird, der m44-Wert bleibt in der Regel bei 1, aber es kann als eine allgemeine Skalierungsfaktor fungieren. In echtem 3D Programmierung wird m44 beim Perspektive beteiligt ist, weil die Kamera konzeptuell am Ursprung ist in der Regel auf 0 festgelegt. 0 Wert m44 funktioniert nur, wenn 3D Objekte nicht Z-Koordinaten 0, aber bei der Arbeit mit 2D-Objekten die Z-Koordinate immer ist 0.
Jede konvexe Viereck
Bei der Anwendung dieser 4 × 4-Transformationsmatrix in eine flache Bitmap, du bist nur unter Nutzung der Hälfte der Matrix-Elemente. Trotzdem Abbildung 3 zeigt etwas geht nicht mit der normalen zweidimensionalen Direct2D Transformationsmatrix, die eine Transformation anwenden, die in etwas anderes als ein Parallelogramm ein Rechteck verwandelt. Tatsächlich heißen die Transform-Objekten, die in den meisten Direct2D D2D1_MATRIX_3X2_F und Matrix3x2F, Betonung der Unzugänglichkeit der dritten Zeile und der Unfähigkeit, nicht affinen Transformationen durchführen.
Mit D2D1_MATRIX_4X4_F, ist es möglich, eine Transformation ableiten, die eine Bitmap in jedem konvexen Viereck Karten — d. h. jede beliebige vierseitiger Abbildung wo die Seiten nicht cross und wo Innenwinkel an die Scheitelpunkte sind weniger als 180 Grad.
Wenn Sie mir nicht glauben, versuchen Sie Herumspielen mit dem NonAffineStretch-Programm. Beachten Sie, dass dieses Programm aus einem Windows Runtime-Programm, auch genannt NonAffineStretch, in Kapitel 10 von meinem Buch "Programming Windows, 6th Edition" angepasst wird (Microsoft Press, 2013).
Abbildung 4 NonAffineStretch im Einsatz zeigt. Der Maus oder den Fingern können Sie die grünen Punkte an eine beliebige Stelle auf dem Bildschirm ziehen. Solange Sie der Figur einer konvexen Viereck halten, kann eine 4 × 4-Transformation basiert auf den Punkt-Standorten abgeleitet werden. Die Transformation wird verwendet, um die Bitmap zu zeichnen und wird auch in der rechten unteren Ecke angezeigt. Nur acht Werte beteiligt sind; die Elemente in der dritten Zeile und dritten Spalte sind immer Standardwerte und m44 ist immer 1.
Abbildung 4 das NonAffineStretch-Programm
Die Mathematik dahinter sind ein wenig behaart, aber die Herleitung des Algorithmus ist in Kapitel 10 meines Buches dargestellt.
Die Matrix4x4F-Klasse
Um das Arbeiten mit der D2D1_MATRIX_4X4_F-Struktur ein wenig zu erleichtern, wird die Matrix4x4F-Klasse im Namespace D2D1 von dieser Struktur abgeleitet. Diese Klasse definiert einen Konstruktor und einen Multiplikationsoperator (die ich in der NonAffineStretch-Algorithmus verwendet) und mehrere hilfreiche statische Methoden zum Erstellen von gemeinsamen transformieren Matrizen. Beispielsweise akzeptiert die Matrix4x4F::RotationZ-Methode ein Argument, das ist ein Winkel in Grad und gibt eine Matrix, die Drehung um die Winkel um die Z-Achse darstellt:
Andere Matrix4x4F-Funktionen erstellen Matrizen für Drehung um die X und Y Achsen und Drehung um eine beliebige Achse, die eine viel schwieriger Matrix ist.
Eine Funktion namens Matrix4x4F::PerspectiveProjection hat ein Argument mit dem Namen Tiefe. Die Matrix, die es gibt ist:
Dies bedeutet die Transformation-Formel für X' ist:
Und ebenso für y*'* und z*'*, d. h. wenn die Z-Koordinate gleich Tiefe, ist der Nenner ist 0 und alle Koordinaten werden unendlich.
Im Prinzip bedeutet dies, dass Sie den Computer-Bildschirm aus einer Entfernung von Tiefeneinheiten, anzeigen wo die Einheiten der Bildschirm selbst, Bedeutung Pixel oder geräteunabhängige Einheiten identisch sind. Wenn ein grafisches Objekt eine Z-Koordinate gleich Tiefe hat, ist es Tiefeneinheiten vor dem Bildschirm, der direkt an Ihr Augapfel ist! Das Objekt sollte sehr groß, um Sie erscheinen — mathematisch unendlich.
Aber Moment mal: Der einzige Zweck der D2D1_MATRIX_4X4_Fstructure und der Matrix4x4F-Klasse ist für Anrufe auf DrawBitmap, und Bitmaps haben immer Z-Koordinaten 0. Wie hat dieser m34-Wert von-1/Tiefe überhaupt keine Wirkung?
Wenn die PerspectiveProjection-Matrix von selbst in den DrawBitmap-Aufruf verwendet wird, wird es tatsächlich wirkungslos. Aber es hat in Verbindung mit anderen Matrix-Transformationen verwendet werden soll. Matrix-Transformationen können verstärkt werden, durch sie zu multiplizieren. Obwohl die ursprüngliche Bitmap keine Z-Koordinaten hat und Z-Koordinaten werden für das Rendern ignoriert, können Z-Koordinaten sicherlich in das Zusammensetzen von Transformationen eine Rolle spielen.
Schauen wir uns ein Beispiel. Das RotatingText-Programm erstellt eine Bitmap mit dem Text "ROTIEREN" mit einer Breite, die nur über Hälfte die Breite des Bildschirms ist. Viel von dem Update und Render-Methoden werden dargestellt Abbildung 5.
Abbildung 5 Code aus RotatingTextRenderer.cpp
void RotatingTextRenderer::Update(DX::StepTimer const& timer)
{
...
// Begin with the identity transform
m_matrix = Matrix4x4F();
// Rotate around the Y axis
double seconds = timer.GetTotalSeconds();
float angle = 360 * float(fmod(seconds, 7) / 7);
m_matrix = m_matrix * Matrix4x4F::RotationY(angle);
// Apply perspective based on the bitmap width
D2D1_SIZE_F bitmapSize = m_bitmap->GetSize();
m_matrix = m_matrix *
Matrix4x4F::PerspectiveProjection(bitmapSize.width);
}
void RotatingTextRenderer::Render()
{
...
ID2D1DeviceContext* context =
m_deviceResources->GetD2DDeviceContext();
Windows::Foundation::Size logicalSize =
m_deviceResources->GetLogicalSize();
context->SaveDrawingState(m_stateBlock.Get());
context->BeginDraw();
context->Clear(ColorF(ColorF::DarkMagenta));
// Move origin to top center of screen
Matrix3x2F centerTranslation =
Matrix3x2F::Translation(logicalSize.Width / 2, 0);
context->SetTransform(centerTranslation *
m_deviceResources->GetOrientationTransform2D());
// Draw the bitmap
context->DrawBitmap(m_bitmap.Get(),
nullptr,
1.0f,
D2D1_INTERPOLATION_MODE_LINEAR,
nullptr,
&m_matrix);
...
}
In der Update-Methode erstellt die Matrix4x4F::RotationY-Methode die folgende Transformation:
Multiplizieren Sie diese mit der Matrix angezeigt, früher von der Matrix4x4F::PerspectiveProjection-Methode zurückgegeben, und Sie erhalten:
Die Transform-Formeln sind:
Dazu gehören auf jeden Fall Perspektive, und Sie sehen das Ergebnis im Abbildung 6.
Abbildung 6 die RotatingText-Anzeige
Aufpassen: Das Argument Tiefe Matrix4x4F::PerspectiveProjektion auf die Bitmap-Breite festgelegt ist, so wie die rotierende Bitmap, um ihn herum schwingt sehr nah an der Nase kommen könnte.
Charles Petzold ist ein langjähriger Beitrag zum MSDN Magazine und Autor von "Programming Windows, 6th Edition" (Microsoft Press, 2013), ein Buch über das Schreiben von Anwendungen für Windows 8. Seiner Website lautet charlespetzold.com.
Unser Dank gilt den folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: Jim Galasyn und Mike Reichtum