Matrix στα μαθηματικά είναι ένας δισδιάστατος πινακας n επι m που έχει πολλές εφαρμογές. Στο directx χρησιμοποιούμε matrix 4χ4 (πχ float matrix[4][4], D3DXMATRIX matrix κλπ) για τον μετασχηματισμό των meshes.
Matrix
Λοιπόν, το matrix στα ελληνικά μήτρα κάνει πραγματικά την δούλευα μιας μήτρας. Βάζουμε ένα Α σχήμα πατάμε την μήτρα και βγαίνει ένα σχήμα Β.
Έπειτα έχουμε το matrix Scale Matrix το οποίο είναι
x = σμίκρυνση/μεγέθυνση του x άξονα (v1,v2)
y = σμίκρυνση/μεγέθυνση του y άξονα (v1,v3)
z = σμίκρυνση/μεγέθυνση του z άξονα (v1,v3)
Για μεγέθυνση πρεπει x,y,z > 1
Για σμίκρυνση 0 < x,y,z < 1
Το scale γίνεται ποσοστιαία. πχ αν θέλομε να το μικρύνουμε στο μισό τότε x=y=z= 1/2, για να το διπλασιάσουμε x=y=z= 1 * 2.
Στο παράδειγμα μου το σχήμα Β είναι 3 φορές μικρότερο, άρα έχουμε x=y=z = 1/3
το matrix γίνεται
Τώρα το μόνο που μένει να κάνουμε είναι να πολλαπλασιάσουμε την μήτρα με όλα τα σημεία
Το παραπάνω το εφαρμόζουμε σε όλα τα vertex-position-vector-whatever
Για περισσότερα περί του πολλαπλασιασμού matrix - matrix matrix - vector κλπ google linear matrix product.
Λοιπόν, στο(προηγούμενο) project πάμε στον constructor της C3DModelEnaTrigwnaki και κάνουμε τις παρακάτω αλλαγές
Matrix
Λοιπόν, το matrix στα ελληνικά μήτρα κάνει πραγματικά την δούλευα μιας μήτρας. Βάζουμε ένα Α σχήμα πατάμε την μήτρα και βγαίνει ένα σχήμα Β.
Τι γίνεται στο παραπάνω;
Για αρχή έχουμε κάποια σημεία (point3d ή vertex ή πες τα όπως θες, αρκεί να περιγραφεί μια θέση σε 3δ κόσμο) που περιγράφουν το σχήμα Α
A =
v1(-1,3,0)
v2(3,3,0)
v3(-1,-1,0)
v4(3,-1,0)
Έπειτα έχουμε το matrix Scale Matrix το οποίο είναι
(Αριστερά μαθηματική απεικόνιση δεξιά c++ 2d array)
Όπου:x = σμίκρυνση/μεγέθυνση του x άξονα (v1,v2)
y = σμίκρυνση/μεγέθυνση του y άξονα (v1,v3)
z = σμίκρυνση/μεγέθυνση του z άξονα (v1,v3)
Για μεγέθυνση πρεπει x,y,z > 1
Για σμίκρυνση 0 < x,y,z < 1
Το scale γίνεται ποσοστιαία. πχ αν θέλομε να το μικρύνουμε στο μισό τότε x=y=z= 1/2, για να το διπλασιάσουμε x=y=z= 1 * 2.
Στο παράδειγμα μου το σχήμα Β είναι 3 φορές μικρότερο, άρα έχουμε x=y=z = 1/3
το matrix γίνεται
Τώρα το μόνο που μένει να κάνουμε είναι να πολλαπλασιάσουμε την μήτρα με όλα τα σημεία
Το παραπάνω το εφαρμόζουμε σε όλα τα vertex-position-vector-whatever
Για περισσότερα περί του πολλαπλασιασμού matrix - matrix matrix - vector κλπ google linear matrix product.
Λοιπόν, στο(προηγούμενο) project πάμε στον constructor της C3DModelEnaTrigwnaki και κάνουμε τις παρακάτω αλλαγές
C3DModelEnaTrigwnaki(ID3D10Device *pRefDevice) : pRefDevice(pRefDevice) { pRefDevice->AddRef(); a = 0.0f; D3D10_INPUT_ELEMENT_DESC layout[]= { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 } }; Vertex vertices[] = { { D3DXVECTOR3( 0.0f, 0.5f, 0.0f ),D3DXVECTOR4(1.0f,0.0f,0.0f,1.0f) }, { D3DXVECTOR3( 0.5f, -0.5f,0.0f ),D3DXVECTOR4(1.0f,0.0f,0.0f,1.0f) }, { D3DXVECTOR3( -0.5f, -0.5f,0.0f ) ,D3DXVECTOR4(1.0f,0.0f,0.0f,1.0f)}, { D3DXVECTOR3( 0.3f, -0.3f,0.0f ),D3DXVECTOR4(1.0f,1.0f,0.0f,1.0f) }, { D3DXVECTOR3( -0.3f, -0.3f,0.0f ) ,D3DXVECTOR4(1.0f,1.0f,0.0f,1.0f)}, }; UINT indecis[] = { 0 , 1 , 2, 0, 3 , 4 }; float scale[4][4] = { { .33f, 0.0f, 0.0f, 0.0f }, { 0.0f, .33f, 0.0f, 0.0f }, { 0.0f, 0.0f, .33f, 0.0f }, { 0.0f, 0.0f, 0.0f, 1.0f } }; for(int i = 0 ; i < 5; i++) { D3DXVec3TransformCoord(&vertices[i].position,&vertices[i].position,&D3DXMATRIX((float*)scale)); } D3D10_PASS_DESC passDesc; if(FAILED(D3DX10CreateMesh( this->pRefDevice, layout,_countof(layout), layout[0].SemanticName, _countof(vertices), _countof(indecis) / 3u, D3DX10_MESH_32_BIT, &this->pMesh))) { //error throw std::exception("Mesh init fail"); } if(FAILED(this->pMesh->SetVertexData(0,vertices))) { //error throw std::exception("Mesh init fail @SetVertexData"); } if(FAILED(this->pMesh->SetIndexData(indecis,_countof(indecis)))) { //error throw std::exception("Mesh init fail @SetIndexData"); } if(FAILED(this->pMesh->CommitToDevice())) { //error throw std::exception("Mesh init fail @CommitToDevice"); } ID3D10Blob *pCompileError = NULL; if(FAILED(D3DX10CreateEffectFromFile( L"EnaTrigwnaki.fx",NULL,NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS,0, pRefDevice,NULL,NULL,&this->pEffect, &pCompileError,NULL))) { char tmp[1024]; ::strcpy(tmp,"Effect compile error: "); ::strncpy(tmp + strlen(tmp),(char*)pCompileError->GetBufferPointer(),_countof(tmp) - strlen(tmp)); pCompileError->Release(); throw std::exception(tmp); } this->pTech = this->pEffect->GetTechniqueByIndex(0); if(FAILED(pTech->GetPassByIndex(0)->GetDesc(&passDesc))) { throw std::exception("Effect error @Tech::GetPassByIndex::GetDesc"); } if(FAILED(pRefDevice->CreateInputLayout( layout, _countof(layout), passDesc.pIAInputSignature,passDesc.IAInputSignatureSize, &this->pLayout))) { throw std::exception("Layout init fail @ID3D10Device::CreateInputLayout"); } if(FAILED(pTech->GetDesc(&this->techDesc))) { throw std::exception("Error @pTech::GetDesc"); } pRefDevice->IASetInputLayout(pLayout); pRefDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); }.
scale x=y=z= 1/3
scale x =1/3 y = 1.5 z = 1
Φυσικά το directx έχει έτοιμες αυτές τις συναρτήσεις που φτιάχνουν matrix.
πχ
(Αριστερά πριν, δεξιά μετά)
D3DXMatrixRotationZ (X/Y)
D3DXMatrixScaling
D3DXMatrixTranslation
Κλπ.
Matrix & VertexShader
Μέχρι τώρα εφαρμόσαμε το matrix πριν βάλουμε τον vertex buffer στο IA stage. Τώρα ήρθε η ώρα να το εφαρμόσουμε στο VS stage. Δηλαδή ο μετασχηματισμός να γίνεται στη GPU!
Στο effect file θα βάλουμε ενα global matrix (float4x4) με όνομα... MyTransformation. Έπειτα θα πολλαπλασιαστούμε το input του VS με το MyTransformation.
float4x4 MyTransformation; struct Vertex { float4 pos : SV_POSITION ; float4 col : COLOR; }; Vertex MyVertexShader( float4 Pos : POSITION,float4 Col : COLOR ) { Vertex v = (Vertex)0; v.pos = mul(Pos,MyTransformation); v.col = Col; return v; } float4 MyPixelShader(Vertex v) : SV_Target { return v.col; } technique10 Tech { pass P0 { SetVertexShader( CompileShader( vs_4_0, MyVertexShader() ) ); SetPixelShader( CompileShader( ps_4_0, MyPixelShader() ) ); } }.
Ωραία φτιαξαμε ένα matrix, αλλάξαμε το VS έτσι ώστε ολα τα position που δέχεται να τα πολλαπλασιάζει με το matrix δηλαδή να μετασχηματίζει το σχήμα και μας λείπει... ένα τρόπο με το οποίο θα αλλάζουμε το MyTransformation μέσα απο το πρόγραμμα.
Εδώ έρχεται το interface ID3D10EffectMatrixVariable που δημιουργεί μια γέφυρα απο μια variable που είναι στην GPU σε μια άλλη που είναι στο πρόγραμμα μας.
C3DModelEnaTrigwnaki
#pragma once #include "dxandwindow.h" #includeclass C3DModelEnaTrigwnaki { struct Vertex { D3DXVECTOR3 position; D3DXVECTOR4 color; }; D3D10_TECHNIQUE_DESC techDesc; ID3D10Device *pRefDevice; ID3DX10Mesh *pMesh; ID3D10Effect *pEffect; ID3D10InputLayout *pLayout; ID3D10EffectTechnique *pTech; ///////////////////////////////////////////////////// ID3D10EffectMatrixVariable *pMyTransformationVar; float r; ///////////////////////////////////////////////////// public: C3DModelEnaTrigwnaki(ID3D10Device *pRefDevice) : pRefDevice(pRefDevice) { pRefDevice->AddRef(); D3D10_INPUT_ELEMENT_DESC layout[]= { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 } }; Vertex vertices[] = { { D3DXVECTOR3( 0.0f, 0.5f, 0.0f ),D3DXVECTOR4(1.0f,0.0f,0.0f,1.0f) }, { D3DXVECTOR3( 0.5f, -0.5f,0.0f ),D3DXVECTOR4(1.0f,0.0f,0.0f,1.0f) }, { D3DXVECTOR3( -0.5f, -0.5f,0.0f ) ,D3DXVECTOR4(1.0f,0.0f,0.0f,1.0f)}, { D3DXVECTOR3( 0.3f, -0.3f,0.0f ),D3DXVECTOR4(1.0f,1.0f,0.0f,1.0f) }, { D3DXVECTOR3( -0.3f, -0.3f,0.0f ) ,D3DXVECTOR4(1.0f,1.0f,0.0f,1.0f)}, }; UINT indecis[] = { 0 , 1 , 2, 0, 3 , 4 }; D3D10_PASS_DESC passDesc; if(FAILED(D3DX10CreateMesh( this->pRefDevice, layout,_countof(layout), layout[0].SemanticName, _countof(vertices), _countof(indecis) / 3u, D3DX10_MESH_32_BIT, &this->pMesh))) { //error throw std::exception("Mesh init fail"); } if(FAILED(this->pMesh->SetVertexData(0,vertices))) { //error throw std::exception("Mesh init fail @SetVertexData"); } if(FAILED(this->pMesh->SetIndexData(indecis,_countof(indecis)))) { //error throw std::exception("Mesh init fail @SetIndexData"); } if(FAILED(this->pMesh->CommitToDevice())) { //error throw std::exception("Mesh init fail @CommitToDevice"); } ID3D10Blob *pCompileError = NULL; if(FAILED(D3DX10CreateEffectFromFile( L"EnaTrigwnaki.fx",NULL,NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS,0, pRefDevice,NULL,NULL,&this->pEffect, &pCompileError,NULL))) { char tmp[1024]; ::strcpy(tmp,"Effect compile error: "); ::strncpy(tmp + strlen(tmp),(char*)pCompileError->GetBufferPointer(),_countof(tmp) - strlen(tmp)); pCompileError->Release(); throw std::exception(tmp); } this->pTech = this->pEffect->GetTechniqueByIndex(0); ////////////////////////////////////////////////// this->pMyTransformationVar = this->pEffect->GetVariableByName("MyTransformation")->AsMatrix(); r = .0f; ////////////////////////////////////////////////// if(FAILED(pTech->GetPassByIndex(0)->GetDesc(&passDesc))) { throw std::exception("Effect error @Tech::GetPassByIndex::GetDesc"); } if(FAILED(pRefDevice->CreateInputLayout( layout, _countof(layout), passDesc.pIAInputSignature,passDesc.IAInputSignatureSize, &this->pLayout))) { throw std::exception("Layout init fail @ID3D10Device::CreateInputLayout"); } if(FAILED(pTech->GetDesc(&this->techDesc))) { throw std::exception("Error @pTech::GetDesc"); } pRefDevice->IASetInputLayout(pLayout); pRefDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); } ~C3DModelEnaTrigwnaki() { if(this->pEffect) { this->pEffect->Release(); this->pEffect = NULL; } if(this->pLayout) { this->pLayout->Release(); this->pLayout = NULL; } if(this->pMesh) { this->pMesh->Release(); this->pMesh = NULL; } pRefDevice->Release(); } void Render() { ///////////////////////////////////////////////// r+= 0.01; if(r > 2.f * D3DX_PI) r =.0f; D3DXMATRIX MyTransformation; D3DXMatrixRotationZ(&MyTransformation,r); pMyTransformationVar->SetMatrix(MyTransformation); //////////////////////////////////////////////// for(UINT pass = 0 ; pass < this->techDesc.Passes; pass++) { pTech->GetPassByIndex(pass)->Apply(0); pMesh->DrawSubset(0); } } };
Και ναι, έχουμε κίνηση!
Πως γίνεται αυτό;
Λοιπόν, ο vertex buffer που περιγράφει το σχήμα μας είναι constant στη vram. Πριν ζωγραφιστεί στο surface πρέπει να περάσει απο μερικά στάδια του directx pipeline. Στη render (στην παραπάνω κλάση) έχουμε μια μεταβλητή r η οποια αυξάνεται απο 0 σε 2π (2π πλήρη περιστροφή, δεν ειναι μοίρες αλλα ακτίνια), έπειτα φτιάχνουμε μια μήτρα η οποία είναι για περιστροφή. Αυτή τη μήτρα την βάζουμε στη vram (sto effect μας) μέσου τη γέφυρα pMyTransformationVar. (η μεταφορά γίνεται όταν καλέσουμε την pTech->GetPassByXX()->Apply(0))
Μέτα καλούμε την DrawSubset η οποία αρχίζει τα stage. Όταν φτάσει στο VS τότε γίνεται η παραμόρφωση του σχήματος βάση της μήτρας.
Επειδή δεν έχει γινει ακόμα το swap των back-front buffers, μπορούμε το ιδιο σχήμα να το ξαναζωγραφίσουμε!
Αλλάζοντας μονο την render
void Render() { ///////////////////////////////////////////////// r+= 0.05; if(r > 2.f * D3DX_PI) r =.0f; D3DXMATRIX MyTransformation; D3DXMatrixRotationZ(&MyTransformation,r); pMyTransformationVar->SetMatrix(MyTransformation); //////////////////////////////////////////////// for(UINT pass = 0 ; pass < this->techDesc.Passes; pass++) { pTech->GetPassByIndex(pass)->Apply(0); pMesh->DrawSubset(0); } D3DXMATRIX scale,trans,rot; D3DXMatrixTranslation(&trans,0.6f,0.6f,0.0); D3DXMatrixScaling(&scale,1/4.0f,1/4.0f,1.0f); D3DXMatrixRotationZ(&rot,r); MyTransformation = scale * trans * rot; pMyTransformationVar->SetMatrix(MyTransformation); for(UINT pass = 0 ; pass < this->techDesc.Passes; pass++) { pTech->GetPassByIndex(pass)->Apply(0); pMesh->DrawSubset(0); } D3DXMatrixTranslation(&trans,-0.6f,0.6f,0.0); MyTransformation = scale * trans * rot; pMyTransformationVar->SetMatrix(MyTransformation); for(UINT pass = 0 ; pass < this->techDesc.Passes; pass++) { pTech->GetPassByIndex(pass)->Apply(0); pMesh->DrawSubset(0); } }.
Με 16 αριθμούς μπορεις να παραμορφώσεις όπως θες ενα σχήμα με 10-100-1000-10κ-100κ σημεία.
Στο πάνω παράδειγμα:
Το πρώτο τρίγωνο μένει ως έχει απο πριν.
Το δεύτερο το περιστρέφουμε, το μικραίνουμε και τέλος το μετακινούμε. Το ίδιο και το τρίτο.
Matrix & σωστή σειρά πολλαπλασιασμού
Αν μπήκατε στο κόπο να κάνετε το παραπάνω, τότε αυτό που είδατε στην οθόνη σας δεν έχει καμιά σχέση με το video. Αυτό επειδή στο κωδικά εχω βάλει τον πολλαπλασιασμό των matrices με διαφορετική σειρά. Ο λογος; Να'χω να γράψω κατι παραπάνω σε αυτή την παράγραφο :p .
Για να βγάλουμε το transformation απο συνδυασμό συγκεκριμένων transformations πχ scale rotation etc πρέπει να ξέρουμε τι θέλουμε να κάνουμε και πως είναι ο vertex buffer δηλαδή ποιο είναι το κέντρο κέντρο του σχήματος.
Λοιπόν, ας κάψουμε κανα κύτταρο!!!
Το σχήμα που εχω είναι
Στο παραπάνω παράδειγμα περιστρέφω το σχήμα με βάση του άξονα z ο οποίος ειναι στο κέντρο του τριγώνου. Με αυτό το αποτέλεσμα
MyTransformation = rot
Αν εγώ θελω να περιστρέφεται σε κάποιο άλλο σημείο της οθόνης; Φαίνεται απλό, MyTransformation = rot * trans και ναι αυτό είναι.
MyTransformation = rot * trans
Τώρα το τρίγωνο περιστρέφεται λίγο πιο κάτω. Ναι αλλά αν θέλω να περιστρέφεται με το άξονα z να είναι στη κορυφή του τριγώνου; Το ίδιο με το προηγούμενο αλλά με άλλη σειρά.
MyTransformation = trans * rot
Γιατί σημαίνει αυτό; -στη c++ οι πράξεις εκτελούνται απο αριστερά προς τα δεξιά πχ int o = 1 + 2 + 3 μεταφράζεται σε int o = ( (1 + 2 ) + 3 )- Για να το εξηγήσω αυτό θα το πάω γεωμετρικά. Έστω οτι η παράσταση rot * trans δεν πολλαπλασιάζει αλλά εκτελεί τους μετασχηματισμούς εναν εναν σειριακά.
1 rot -> περίστρεψε το σχήμα
2 trans -> μετακίνησε το σχήμα
με αυτά βγαίνει η πρόταση "γύρνα το τρίγωνο και μετά μετακίνησε το πιο κάτω"
σε αντίθεση με
1 trans μετακίνησε το σχήμα
2 rot περίστρεψε το σχήμα
που η πρόταση γίνεται "μετακίνησε το και μετά γύρνα το"
Με τις παραπάνω προτάσεις σκεφτείτε τι θα κάνατε αν είχατε μπροστά σας ενα πιάτο με ενα σπιρτόκουτο πανω στο κέντρο του πιάτου.
αν σας έλεγα "γύρνα το πιάτο και μετά τραβά το σπιρτόκουτο προς τα εσένα" θα βλέπατε το ίδιο μοτίβο με το πρώτο vid(rot*trans)
αν όμως σας έλεγα "τραβά το σπιρτόκουτο και γύρνα το πιάτο" θα βλέπατε το ίδιο πράγμα με το δεύτερο vid (trans * rot)
Με λίγα λόγια το κέντρο βάρος ενοε σχήματος εδω είναι το x,y,z 0 το οποίο δεν εγγυάται οτι είναι πραγματικά το κέντρο βάρος του σχήματος! Φυσικά η κλάση ID3DX10Mesh έχει μια επιλογή που ξαναφτιαχνει το σχήμα με το κέντρο βάρος να είναι το x,y,z 0.
Ας το κάνουμε λίγο πιο πολύπλοκο, ας φτιάξουμε τον ήλιο και την γη. ( με τα τριγωνάκια, προς θεού οχι ακόμα 3d models)
Για τον ήλιο scale * rot απλό.
για την γη scale * rot * trans * rot μπέρδεμα, στην αρχή την μικραίνουμε, μετά την περιστρέφουμε από τον εαυτό της, την μετακινούμε μακριά από τον ήλιο, και τέλος την περιστρέφουμε γύρο από τον ήλιο.
οκ οκ δεν ήταν και τόσο πολύπλοκο, ας βάλουμε και τη σελήνη
Τα άλλα έχουν ως έχουν, σελήνη = scale * rot * trans * rot * trans * rot
Έχουμε και λέμε μίκρυνε την, περιστροφή απο τον εαυτό της, μετακίνησε την στη γη, περιστροφή μαζί με τι γη, μετακίνησε την μακριά απο την γη, περιστροφή γύρο από την γη :p
Στο παραπάνω το μόνο που άλλαξε είναι το Render της C3DModelEnatrigvnaki
void Render() { r+= 0.025; if(r > 2.f * D3DX_PI) r =.0f; D3DXMATRIX MyTransformation; D3DXMATRIX scale,scale1,trans,trans1,rot; D3DXMatrixTranslation(&trans,0.0f,0.7f,0.0); D3DXMatrixTranslation(&trans1,0.0f,0.2f,0.0); D3DXMatrixScaling(&scale,1/4.0f,1/4.0f,1.0f); D3DXMatrixScaling(&scale1,1/2.0f,1/2.0f,1.0f); D3DXMatrixRotationZ(&rot,r); //ilios MyTransformation = scale1 * //mikrine ton oxi poly rot ; // peristrofi apo ton eayto toy pMyTransformationVar->SetMatrix(MyTransformation); for(UINT pass = 0 ; pass < this->techDesc.Passes; pass++) { pTech->GetPassByIndex(pass)->Apply(0); pMesh->DrawSubset(0); } //gi MyTransformation = scale * // mikrine tin rot * // peristofi apo to eayto tis trans * // metakinise tin rot ; // peristrofi apo ton ilio pMyTransformationVar->SetMatrix(MyTransformation); for(UINT pass = 0 ; pass < this->techDesc.Passes; pass++) { pTech->GetPassByIndex(pass)->Apply(0); pMesh->DrawSubset(0); } //selini D3DXMatrixScaling(&scale1,1/6.0,1/6.0,1.0); MyTransformation = scale1 * // mikrine tin rot * // peristofi apo to eayto tis trans1 * // metakinise tin sti gi rot * // peristrofi mazi me ton ilio trans * // metakinise tin ligo pio makria apo ti gi rot; // peristrofi giro apo tob ilio pMyTransformationVar->SetMatrix(MyTransformation); for(UINT pass = 0 ; pass < this->techDesc.Passes; pass++) { pTech->GetPassByIndex(pass)->Apply(0); pMesh->DrawSubset(0); }.
Λόγου οτι στο επόμενο το project θα αλλάξει αρκετά να ο source code
Δεν υπάρχουν σχόλια:
Δημοσίευση σχολίου