Παρασκευή 16 Δεκεμβρίου 2011

DirectX3d 10 part 5 ( loading models, WVP matrices, Depth state) )

Καλά τα τριγωνάκια αλλά για να δούμε και κάνα 3d μοντέλο.
Τούτο εδώ το κόβω να είναι μεγάλο.

Φόρτωμα μοντέλου
Το Directx δεν έχει κάποια ρουτίνα που να φορτώνει 3d μοντέλα γιατί δεν είναι εφαρμογή μοντελοποίησης. Δεν έχει ούτε καν κάποιο interface που να προσδιορίζεται ως μοντέλο. Μην συσχετίζετε το ID3DX10Mesh ως μοντέλο, αυτή η κλάση είναι ένας buffer wrapper που έχει μπόλικους buffers αλλά όχι shaders ετσι ώστε να θεωρηθεί μοντέλο, μπορούσα εξαρχής να μην την χρησιμοποιήσω mesh, αλλά δεν έχει νόημα να κάνω κάτι το απλό.. πολύπλοκο.

Ω ναι, θα φτιάξουμε μια δική μας κλάση που θα παίζει τον ρολό του μοντέλου. Για να το κάνουμε αυτό πρέπει να δούμε κάποια πράγματα που δεν είδαμε με τα τριγωνάκια.

Πρόγραμμα μοντελοποίησης
Δεν θα κάτσω να βάζω καρφωτά vertex για φτιάξω ένα 3d model. Έξαλλου είναι πολύ δύσκολο, άντε να φτιάξεις κάνα κύβο αλλά και πάλι δεν έχει νόημα.
Εγώ έχω το blender για μοντελοποίηση, τα καλά του, είναι τζάμπα, είναι σούπερ ελαφρύ. Τα κακά του, δεν ξέρω πολλά απο αυτά τα προγράμματα και έτσι δεν μπορώ να κρίνω.

Μια λίστα με προγράμματα μοντελοποίησης
3ds max
Maya
blender <-- :D
google

Μια εισαγωγή για το blender
Menu -> add -> εδώ έχει έτοιμα σχήματα
Menu -> File -> Export -> εδώ κάνουμε export

Camera

numpad
1 -> πάει στον άξονα y
3 -> πάει στον άξονα x
7 -> πάει στον άξονα z
4,6,8,2 περιήγηση
5 αλλαγή της προβολής ( presp/ortho θα το δούμε και αυτό)
+- ζουμ

ποντίκι
πατημένο το μεσαίο κλικ (ροδέλα) free move



Δεξί κλικ -> επιλογή του σχήματος
tab εναλλαγή του modeling object/edit

Όταν έχουμε κάτι επιλεγμένο:
S scale
R rotate
G translate
Αριστερό κλικ free move (η G)
I πληροφορίες
X ή delete δελετε :p

ctrl + δεξί κλικ επιλογή vertices

όταν είναι σε edit mode και έχεις επιλέξει κάποια veritices
 W -> pop menu με smooth,subdivide κλπ

Αυτά αρκούν για να φτιάξουμε ένα "περίεργο" μοντέλο.

Να και το περίεργο μοντέλο

(για να το φτιάξεις.  Ανοίγεις το blender και
tab -> αλλαγή σε edit model
w -> subdivide αυτό 6-7 φορές
Α -> deselect all
ctrl left click και με το ποντικάκι επιλέγουμε τα μάτια στόμα, τα σπρώχνουμε μέσα, τα ιδία για την μύτη)





Φτιάχνοντας το δικό μας file format
Επειδή το directx δεν έχει κάποιο συγκεκριμένο file format που να κρατάει τα models, πρέπει να φτιάξουμε ένα δικό μας. Λοιπόν τι θέλουμε για ένα σχήμα; vertices και indices. Άρα το αρχείο μας θα έχει ας πούμε το παρακάτω format, με όνομα v1 και extension *.v1

File
offset 0
UINT vertex count
vertex array
offset vertex count + sizeof(UINT)
UINT index count
index array

Απλό, φυσικά θα είναι σε binary format και όχι string.
Από τα vertex θα κρατήσουμε μονό τα positions δηλαδή ο vertex struct θα είναι struct Vertex{D3DXVECTOR3 pos;};

Μετατροπή κάποιο αρχείου modeling στο δικό μας format
Τα περισσότερα file format υπάρχουν στην wiki. Αυτό που θα μετατρέψω είναι το .x3d που είναι σε xml άρα εύκολο. ( στο blender file-> export -> *.x3d)
Να και το cubeFace.x3d

Δεν θα μπω στην διαδικασία της μετατροπής γιατί είναι θέμα επιλογής. Εδώ ένα πρόγραμμα σε Net που μετατρέπει το x3d σε v1.

εδώ το παραπάνω σχήμα σε v1 format


κλάση Model
Όπως είχαμε την κλάση "ένα τριγωνάκι" έτσι τώρα θα έχουμε τη κλάση C3DModelV1.



#pragma once 
#include "dxandwindow.h"
#include <stdio.h> // File IO
#include <string> // std::swap
class C3DModelV1
{
 
 ID3D10Device     *pRefDevice;
 ID3DX10Mesh      *pMesh;
 ID3D10Effect     *pEffect;
 ID3D10InputLayout    *pLayout;
 ID3D10EffectTechnique   *pTech;

 ID3D10EffectMatrixVariable  *pMyTransformationVar;
 ID3D10EffectVectorVariable  *pObjColorVar;

 D3D10_TECHNIQUE_DESC   techDesc;

 D3DXVECTOR4      objColor;
 D3DXMATRIX      transformation;

 bool ReadFileV1(LPCWSTR fn,
  D3DXVECTOR3 **pVertexBuffer,UINT **pIndexBuffer,
  UINT *pVertexCount,UINT *pIndexCount)
 {

  //v1 file format
  //uint array count 
  //float3 array (positions)
  //uint array count
  //uint array (indices)
  FILE *fp = _wfopen(fn,L"rb");

  if(!fp) return true;
  if(fread(pVertexCount,sizeof(UINT),1,fp) != 1) 
  {
   fclose(fp);
   return true;
  }
  *pVertexBuffer = new D3DXVECTOR3[*pVertexCount];
  
  if(fread(*pVertexBuffer,sizeof(D3DXVECTOR3),*pVertexCount,fp) != *pVertexCount)
  {
   delete [] *pVertexBuffer;
   fclose(fp);
   return true;
  }
   
  if(fread(pIndexCount,sizeof(UINT),1,fp) != 1)
  {
   delete [] *pVertexBuffer;
   fclose(fp);
   return true;
  }
  *pIndexBuffer = new UINT[*pIndexCount];

  if(fread(*pIndexBuffer,sizeof(UINT),*pIndexCount,fp) != *pIndexCount)
  {
   delete[] *pVertexBuffer ;
   delete[] *pIndexBuffer;
   fclose(fp);
   return true;
  }
  fclose(fp);
  return false;

 }

public:
 C3DModelV1(ID3D10Device *pRefDevice,LPCWSTR _v1FileName,D3DXVECTOR4 objColor)
  : pRefDevice(pRefDevice),objColor(objColor),
  pMesh(NULL),pEffect(NULL)
 {
  pRefDevice->AddRef();
  D3D10_INPUT_ELEMENT_DESC  layout;
  

  ZeroMemory(&layout, sizeof(layout));
  layout.SemanticName   = "POSITION";
  layout.Format    = DXGI_FORMAT_R32G32B32_FLOAT;
  layout.InputSlotClass  = D3D10_INPUT_PER_VERTEX_DATA;

  D3DXVECTOR3 *vertices;
  UINT  *indices;
  UINT  verticesCount,indicesCount;
  
  if(ReadFileV1(_v1FileName,&vertices,&indices,&verticesCount,&indicesCount))
   throw std::exception("Error at reading v1 file");

  //RH to LH
  for(UINT i =0; i < verticesCount;i++)
  {
   std::swap(vertices[i].y,vertices[i].z);
   vertices[i].z = 0.0f - vertices[i].z;
  }
                  
  //-----------------------IA stage begin-----------------------------------
  if(
   FAILED(D3DX10CreateMesh(pRefDevice,&layout,1,layout.SemanticName,
   verticesCount,indicesCount / 3u,D3DX10_MESH_32_BIT,&pMesh))
   || FAILED(pMesh->SetVertexData(0,vertices))
   || FAILED(pMesh->SetIndexData(indices,indicesCount))
   || FAILED(pMesh->CommitToDevice())
   )
  {
   delete []vertices;
   delete []indices;
   throw std::exception("C3DModelV1 init fail.");
  }
  delete []vertices;
  delete []indices;
  ID3D10Blob *pCompileError = NULL;
  if(FAILED(D3DX10CreateEffectFromFile(L"v1.fx",NULL,NULL,"fx_4_0",
   D3D10_SHADER_ENABLE_STRICTNESS,0,pRefDevice,NULL,NULL,&pEffect,
   &pCompileError,NULL)))
  {
   if(pCompileError && pCompileError->GetBufferPointer())
   {
    std::string ce("Compile shader fail:\n");
    ce += (char*) pCompileError->GetBufferPointer();
    pCompileError->Release();
    throw std::exception(ce.c_str());
   }
   else
    throw std::exception("C3DModelV1 init fail(fx file missing.");
  }

  pTech = pEffect->GetTechniqueByName("Tech");
  if(!pTech)
   std::exception("v1 fx error");
  
  pMyTransformationVar = pEffect->GetVariableByName("MyTransformation")->AsMatrix();
  pObjColorVar = pEffect->GetVariableByName("ObjColor")->AsVector();

  
  D3D10_PASS_DESC passDesc;
  if(
   FAILED(pTech->GetPassByIndex(0))
   ||FAILED(pTech->GetPassByIndex(0)->GetDesc(&passDesc))
   ||FAILED(pRefDevice->CreateInputLayout(&layout,1,
   passDesc.pIAInputSignature,passDesc.IAInputSignatureSize,&pLayout))
   ||FAILED(pTech->GetDesc(&techDesc))
   )
  {
   std::exception("C3DModelV1 init fail");
  }
  //-------------------IA stage end ------------------------------------

  D3DXMatrixIdentity(&transformation);
 }
 ~C3DModelV1()
 {
  if(pRefDevice) pRefDevice->Release();
  if(pMesh) pMesh->Release();
  if(pEffect) pEffect->Release();
 }

 D3DXMATRIX& Transformation()
 {
  return transformation;
 }
 void Render()
 {
  // IA Stage begin
  pRefDevice->IASetInputLayout(pLayout);
  pRefDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  // IA Stage end
  pMyTransformationVar->SetMatrix(transformation);
  pObjColorVar->SetFloatVector(objColor);
  for(UINT pass = 0 ; pass < techDesc.Passes; pass++)
  {
   pTech->GetPassByIndex(pass)->Apply(0);
   
   pMesh->DrawSubset(0);
  }

 }

};
.

Τι το καινούριο έχουμε εδώ; Πρώτα έχουμε μια function (ReadFileV1) που διαβάζει το mesh μας, δεν νομίζω να θέλει εξήγηση. Έχουμε μια γέφυρα pObjColorVar που οδηγεί στο ObjColor στην gpu. Και τέλος κάνουμε IA binding στο render, αυτό επειδή πλέον δεν έχουμε ένα model, έχουμε και το informationLable που αλλάζει το IA state, και όταν αλλάξει το IA state, εμείς πρέπει να το ξαναφέρουμε εκεί που ήταν. Πχ το ID3D10Font δεν χρησιμοποιεί την tringle list topology, έτσι όταν κάνει render το informationlable αυτό αλλάζει το topology, έτσι εμεις πρέπει να το επαναφέρουμε.

Μια πολύ σημαντική αλλαγή είναι αυτό το loop(εκεί που έχω σχόλια RH to LH) που περνάει όλα τα vertices πριν το IA και κάνει κάτι περίεργα. Εδώ μετατρέπουμε όλα τα vertices από RH σε LH.

RH LH
Αυτά είναι καρτεσιανά συστήματα, και τα δυο είναι 3δ. Το πρώτο λέγεται Right handed και το δεύτερο left handed. Με απλά λογία.
LH βαλέ το αριστερό χέρι μπροστά σου, ο αντίχειρας να δείχνει πάνω, ο δείκτης μπροστά, το κωλοδάχτυλο να δείχνει το δεξί σου χέρι. Τώρα έχεις
Αντίχειρας: ειναι το y
δείκτης: z
κωλοδάχτυλο: χ

Γύρνα τον καρπό σου προς τα εσένα. Αυτή είναι μια θετική περιστροφή του άξονα y

Τώρα κανε το ίδιο με το δεξί. Ωπ :p τι έχουμε! Η θετική περιστροφή του αριστερού είναι αρνητική του δεξιού και το ανάποδο.

Οκ αυτό λύνεται εύκολα, το μονό που κάνουμε είναι να αλλάξουμε τα θετικά με αρνητικά και τα αρνητικά με θετικά σημεία του άξονα z.
'z = 0 - z

Έλα που δεν τελειώσαμε, έχουμε και τα swap! Αυτό πάλι το κάνουμε γιατί σχεδόν όλα τα 3dmodeling προγράμματα έχουν τον πάνω άξονα ως z. Ω ναι, το z δεν δείχνει το βάθος αλλά το ύψος.

Αν κάνετε μετατροπή RH -> LH με τα δυο να έχουν ως ύψος το +y τότε πρέπει να κάνετε ένα reverse των indices i1 i2 i3 να γίνει i1 i3 i2 και δεν κάνουμε swap του z με y.
Άρα έχουμε για να αλλάξουμε το +z σε - z -> 'z = 0 - z
για να αλλάξουμε το y top σε z τοπ swap(y,z)
αν και τα δυο συστήματα έχουν y ως top τότε κάνουμε reverse τους indices και 'z = 0 - z

Τέλος πάντων, εγώ χρησιμοποιώ το z ως βάθος, το y ως ύψος και x ως πλάτος. Επίσης χρησιμοποιώ Left handed (αν δείτε καμιά συνάρτηση με ένα suffix τύπου LH ή RH, θα ξέρετε τι είναι).

Αυτά με τα καρτεσιανά, ΠΡΟΣΟΧΉ!!!

Ας κάνουμε ένα update στη main και effect file.







#include "dxandwindow.h"
#include "InformationLable.hpp" 
#include "C3DModelV1.hpp"



//config
int       Height = 600;
int       Width = 800;
double      g_FPS = 100.0;
double      g_SysFreq = 100.0;


//globals
LPCWSTR lpszWndClsName  = L"{BAF17182-20D1-46EB-8CAB-B4AAF5388552}";
LPCWSTR lpszWndTitle = L"Hi2!";
double      g_CurFPS;
HWND      g_Wnd;

ID3D10Device    *pDevice;
IDXGISwapChain    *pSwapChain;
ID3D10RenderTargetView  *pRenderView;

InformationLable   *pInformationLable;
C3DModelV1     *pFaceCube;

HRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
void AppCriticalError(LPCSTR msg);
int MainLoop();

int InitDevice(HWND hWnd,int w,int h);
int InitModels();
int ReleaseDevice();
int ReleaseModels();
void RenderFrame();
void RenderSys();

//Window app begin
int WINAPI wWinMain(HINSTANCE hInst,HINSTANCE,LPWSTR,int nShow)
{
 WNDCLASS wc;
 int   res;
 ZeroMemory(&wc,sizeof(wc));

 wc.lpfnWndProc  = WndProc;
 wc.lpszClassName = lpszWndClsName;
 wc.hCursor   = LoadCursor(NULL,IDC_ARROW);


 if(!RegisterClass(&wc))
  return 1;
 g_Wnd = CreateWindow(lpszWndClsName,lpszWndTitle,
  WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,0,Width,Height,
  NULL,NULL,hInst,NULL);
 if(!g_Wnd)
  return 1;
 if(InitDevice(g_Wnd,Width,Height)
  ||InitModels()
  )
  return 1;
 ShowWindow(g_Wnd, nShow);
 res = MainLoop();
 ReleaseModels();
 ReleaseDevice();
 return res;
}
HRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
 case WM_CLOSE:
  PostQuitMessage(0);
  break;
 default:
  return DefWindowProc(hWnd,msg,wParam,lParam);
 }
 return 0;
}
void AppCriticalError(LPCSTR msg)
{ 
 MessageBoxA(g_Wnd,msg,"Critical Error",MB_ICONERROR);
 SendMessage(g_Wnd,WM_QUIT,0,0);
}
//Window app end

//Dx begin
int MainLoop()
{
 MSG msg;
 LARGE_INTEGER liCur,liFreq,liFpsCounter;
 LARGE_INTEGER liLastSys ={0};
 LARGE_INTEGER liLastFrame = {0};
 long frameCount =0;

 QueryPerformanceCounter(&liFpsCounter);
 for(;;)
 {
  //window part
  if ( PeekMessage(&msg,0,0,0,PM_REMOVE) > 0)
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
   if(msg.message == WM_QUIT)
    return msg.lParam;
   continue;
  }
  //dx part
  else 
  {
   if(!QueryPerformanceCounter(&liCur)
    || !QueryPerformanceFrequency(&liFreq)
    || !liFreq.QuadPart)
   {
    AppCriticalError("Timer Error");
    continue;
   }
   
   if( double(liCur.QuadPart - liLastSys.QuadPart) / double(liFreq.QuadPart)  > 1.0 / g_SysFreq ) 
   // call sysproc
   {
    if(!QueryPerformanceCounter(&liLastSys)) 
    {
     AppCriticalError("Timer Error");
     continue;
    }
    RenderSys();
   }

   if((double(liCur.QuadPart - liLastFrame.QuadPart) / double(liFreq.QuadPart) ) >  1.0 / g_FPS ) 
   {
    frameCount++;
    if(!QueryPerformanceCounter(&liLastFrame)) 
    {
     AppCriticalError("Timer Error");
     continue;
    }
    RenderFrame();
    //calc fps
    if(frameCount == 20)
    {
     g_CurFPS=
      20.0/ (double(liCur.QuadPart - liFpsCounter.QuadPart) / double(liFreq.QuadPart));
     QueryPerformanceCounter(&liFpsCounter);
     frameCount = 0;
    }
    continue;
   }
  }
  Sleep(1);
 }

 return 0;
}
int InitDevice(HWND hWnd,int w,int h)
{

 ID3D10Texture2D    *pBackBuffer;
 DXGI_SWAP_CHAIN_DESC  swapChainDesc;

 
 ZeroMemory(&swapChainDesc,sizeof(swapChainDesc));
 swapChainDesc.BufferCount   = 1;
 swapChainDesc.BufferUsage   = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 swapChainDesc.SampleDesc.Count  = 1;
 swapChainDesc.OutputWindow   = hWnd;
 swapChainDesc.Windowed    = true;

 swapChainDesc.BufferDesc.Height  = h;
 swapChainDesc.BufferDesc.Width  = w;
 swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
 swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
 swapChainDesc.BufferDesc.Format  = DXGI_FORMAT_R8G8B8A8_UNORM;

 if(FAILED(D3D10CreateDeviceAndSwapChain(
  NULL,D3D10_DRIVER_TYPE_HARDWARE,NULL,D3D10_CREATE_DEVICE_DEBUG,
  D3D10_SDK_VERSION,&swapChainDesc,&pSwapChain,&pDevice)))
 {
  return 1;
 }

 if(FAILED(pSwapChain->GetBuffer(0,__uuidof(ID3D10Texture2D),(void**)&pBackBuffer)))
 {
  pSwapChain->Release();
  pDevice->Release();
  return 1;
 }
 
 if(FAILED(pDevice->CreateRenderTargetView(pBackBuffer,0,&pRenderView)))
 {
  pSwapChain->Release();
  pDevice->Release();
  pBackBuffer->Release();
  return 1;
 }
 pBackBuffer->Release();

 pDevice->OMSetRenderTargets(1,&pRenderView,NULL);
 
 D3D10_VIEWPORT vp;
 vp.Height = h;
 vp.Width = w;
 vp.MaxDepth = 1.0f;
 vp.MinDepth = 0.0f;
 vp.TopLeftX = 0;
 vp.TopLeftY = 0;
 pDevice->RSSetViewports(1,&vp);
 
 return 0;
}
int ReleaseDevice()
{
 pRenderView->Release();
 pSwapChain->Release();
 pDevice->Release();
 return 0;
}


int InitModels()
{
 try
 {
  pFaceCube = new C3DModelV1(pDevice,L"cubeFace.v1",D3DXVECTOR4(1.0f,1.0f,0.0f,1.0f));
  pInformationLable = new InformationLable(double(Width)/3.0 ,0,D3DXCOLOR(1.0f,1.0f,1.0f,1.0f),pDevice);
 }
 catch(std::exception e)
 {
  MessageBoxA(NULL,e.what(),"Error",0);
  return 1;
 }
 
 return 0;
}
int ReleaseModels()
{
 delete pInformationLable;
 return 0;
}

void RenderSys()
{
 pInformationLable->Text()
  <<L"FPS:  "
  <<g_CurFPS; 
 D3DXMATRIX r,t;
 static float a =0.0f;
 a+= 0.01f;
 D3DXMatrixScaling(&t,0.4f,0.4f,0.4f);
 D3DXMatrixRotationX(&r,a);
 pFaceCube->Transformation() = r * t;
}
void RenderFrame()
{
 pDevice->ClearRenderTargetView(pRenderView,D3DXCOLOR(0.0f,0.0f,0.30f,1.0f));
 pInformationLable->Render();

 pFaceCube->Render();
 pSwapChain->Present(0,0);
}
//dx end
v1.fx
float4x4 MyTransformation;
float4  ObjColor;
struct Vertex
{
 float4 pos : SV_POSITION;
};
Vertex MyVertexShader(float4 pos : POSITION)
{
 Vertex ver = (Vertex)0;
 ver.pos =  mul(pos,MyTransformation);
 return ver;
}
float4 MyPixelShader(Vertex input) : SV_Target
{
 return ObjColor; // vale to xroma poy einai sto objColor
}
RasterizerState MyWireframe
{
 FillMode = WIREFRAME;
 CullMode = NONE;
};

technique10 Tech
{
 pass P0
 {
  SetVertexShader(CompileShader(vs_4_0, MyVertexShader() ) );
  SetGeometryShader(NULL);
  SetPixelShader(CompileShader(ps_4_0,MyPixelShader() ) );

  SetRasterizerState(MyWireframe);

 }
}
.




Στο effect έχουμε κάτι νέο ένα state (rasterizer). Αυτό το state μπορούμε να το βάλουμε και με c++ (βλ InformationLable.hpp) αλλά στο effect είναι πιο εύκολο. Αυτό το state μας δίνει το παραπάνω αποτέλεσμα (πλέγμα). Επίσης με αυτό μπορούμε να πούμε τι να μην ζωγραφίσει πχ face, back ( CullMode = FACE/BACK/NONE). Τέλος πάντων, τώρα το χρησιμοποιώ μονό και μονό γιατί μας δίνει το 3δ εφέ, αν το ειχα solid ( FillMode) τότε θα βλέπαμε μια κίτρινη παπαριά εφόσον δεν έχουμε φωτισμό ώστε να φανεί 3δ.





Wolrd View Projection (WVP) matrices


Επειδή αρχίζουμε και βάζουμε μοντέλα πρέπει να ορίσουμε κάποια matrices που θα δίνουν μια αναφορά θέσης του κάθε αντικειμένου σχετικά με μένα και με άλλα αντικείμενα. Αρχίζουμε με τα εύκολα.

World
Τούτο εδώ το matrix αναφέρεται στο κόσμο. Είναι το πιο εύκολο στην κατανόηση. Πχ εισαι στο δωμάτιο, αν γυρίσεις το world matrix τότε το δωμάτιο θα γυρίσει, μαζί με αυτό όλα τα αντικείμενα που είναι μέσα.

View
Αυτό εδώ αναφέρεται σε εσένα. Που είσαι, που κοιτάς και που είναι το πάνω. Αυτό μπορείς να το πεις και "κάμερα"

Projection

Με το projection ορίζουμε το τρόπο-τόπο προβολής. (η εικόνα είναι από το net, και είναι σε RH)

Για να βγάλουμε αυτά τα 3 mtrices
Για το world D3DXMatrixIdentity που το μονό που κάνει ειναι αρχικοποιεί ένα matrix
Για view D3DXMatrixLookAtLH η οποία παίρνει
pEye η θέση της κάμερας
pAt που κοιτάει
pUp ποιος άξονας είναι top (+y)

Για projection D3DXMatrixPerspectiveFovLH (ή άλλη προβολή πχ ortho)
fovy FOV (μοίρες είναι οτι πρέπει => π/4)
aspect το aspect του output μας πχ σε εμάς είναι το window αρα width/height
zNear/zFar το βάθος

Για να μην έχουμε 3 γέφυρες προς το effect (world view projection) πολλαπλασιάζουμε τα παραπάνω για να βγάλουμε το τελευταίο matrix που είναι η αναφορά. Επειδή τα matrices πρέπει να πολλαπλασιάζονται με συγκεκριμένη σειρά

WVP = Word * Transformation * View * Projection

Έτσι τώρα με το WVP μπορούμε να περιγράψουμε που ακριβός είναι ένα αντικείμενο μέσα στο κόσμο μας.

Model με WVP

Λοιπόν όπως είπα και πιο πριν, ένα mesh μπορούμε να το ξαναζωγραφισουμε σε άλλο σημείο, με μόνη αναφορά το transformation. Επειδή το WVP είναι το ίδιο για όλη την σκηνή, αυτό δεν χρειάζεται να αλλάζει ξεχωριστά για κάθε αντικείμενο.





#pragma once 
#include "dxandwindow.h"
#include <stdio.h> // File IO
#include <string> // std::swap
#include <map> // vector

class C3DModelV1
{
 struct Instance
 {
  D3DXMATRIX trans;
  D3DXVECTOR4 color;
 };
 
 ID3D10Device     *pRefDevice;
 ID3DX10Mesh      *pMesh;
 ID3D10Effect     *pEffect;
 ID3D10InputLayout    *pLayout;
 ID3D10EffectTechnique   *pTech;

 ID3D10EffectMatrixVariable  *pWVPVar;
 ID3D10EffectVectorVariable  *pObjColorVar;

 D3D10_TECHNIQUE_DESC   techDesc;

 D3DXVECTOR4      objColor;

 D3DXMATRIX      view;
 D3DXMATRIX      world;
 D3DXMATRIX      projection;

 std::map<UINT,Instance>   instances;


 bool ReadFileV1(LPCWSTR fn,
  D3DXVECTOR3 **pVertexBuffer,UINT **pIndexBuffer,
  UINT *pVertexCount,UINT *pIndexCount)
 {
  ...
 }

public:
 C3DModelV1(ID3D10Device *pRefDevice,LPCWSTR _v1FileName,D3DXVECTOR4 objColor)
  : pRefDevice(pRefDevice),objColor(objColor),
  pMesh(NULL),pEffect(NULL)
 {
  ...
 }
 ~C3DModelV1()
 {
  ...
 }

 void AddInstance(UINT id,const D3DXMATRIX& transformation,const D3DXVECTOR4& color)
 {
  instances[id].color = color;
  instances[id].trans = transformation;
 }
 Instance& GetInstance(UINT id)
 {
  return instances[id];
 }
 void RemoveInstance(UINT id)
 {
  instances.erase(id);
 }
 D3DXMATRIX& View()
 {
  return view;
 }
 D3DXMATRIX& World()
 {
  return world;
 }
 D3DXMATRIX& Projection()
 {
  return projection;
 }

 void Render()
 {
  // IA Stage begin
  pRefDevice->IASetInputLayout(pLayout);
  pRefDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  // IA Stage end
  std::map<UINT,Instance>::iterator it;
  for(it = instances.begin(); it != instances.end(); it++)
  {
   pWVPVar->SetMatrix(//WVP
    world * it->second.trans * view * projection
    );
   pObjColorVar->SetFloatVector(it->second.color);
   for(UINT pass = 0 ; pass < techDesc.Passes; pass++)
   {
    pTech->GetPassByIndex(pass)->Apply(0);
    pMesh->DrawSubset(0);
   }
  }
 }

};
.

Να το καινούριο μας μοντέλο. Οι νέες δυνατότητες του είναι:
Έχει WVP
Έχει ένα νέο interface AddInstance,RemoveInstance,GetInstance
AddInstance βάζει άλλο ένα τέτοιο σχήμα στη σκηνή
RemoveInstance διαγράφει ένα σχήμα από την σκηνή με βάση το id που δώσαμε στο πρώτο
GetInstance μας επιστέφει ένα reference του transformation/color του σχήματος. Επιστρέφει αυτά τα δυο γιατί μόνο με αυτά κάνουμε αναφορά στο αντικείμενο.

Η render άλλαξε έτσι ώστε να δουλέψει ο μηχανισμός με τα instances.

Και να πως δουλεύει

int InitModels()
{
 D3DXMATRIX w,v,p;
 D3DXMatrixIdentity(&w);
 D3DXMatrixLookAtLH(
  &v,
  &D3DXVECTOR3(5.0f,5.0f,-5.0f),
  &D3DXVECTOR3(0.0f,0.0f,0.0f),
  &D3DXVECTOR3(0.0f,1.0f,0.0f)
  );
 D3DXMatrixPerspectiveFovLH(
  &p,
  3.14f /4.0f,800.0f/600.0f,1.0f,1000.0f);
 
 try
 {
  pFaceCube = new C3DModelV1(pDevice,L"cubeface.v1",D3DXVECTOR4(1.0f,1.0f,0.0f,1.0f));
  pInformationLable = new InformationLable(double(Width)/3.0 ,0,D3DXCOLOR(1.0f,1.0f,1.0f,1.0f),pDevice);
 }
 catch(std::exception e)
 {
  MessageBoxA(NULL,e.what(),"Error",0);
  return 1;
 }
 //set wvp
 pFaceCube->View() = v;
 pFaceCube->World() = w;
 pFaceCube->Projection() = p;
 //add 2 instance
 D3DXMATRIX t;
 D3DXMatrixIdentity(&t);
 D3DXMatrixTranslation(&t,0.0f,0.0f,2.0f);
 UINT prasinoFaceCube = 1;
 UINT kokkinoFaceCube = 2;
 pFaceCube->AddInstance(prasinoFaceCube,t,D3DXVECTOR4(0.0f,1.0f,0.0f,1.0f));
 D3DXMatrixTranslation(&t,0.0f,0.0f,-2.0f);
 pFaceCube->AddInstance(kokkinoFaceCube,t,D3DXVECTOR4(1.0f,0.0f,0.0f,1.0f));


 
 return 0;
}
.
voila

DepthState


Εφόσον έχουμε ν μοντέλα πρέπει να βάλουμε ένα Depth buffer & ένα depth state.
Το interface ID3D10DepthStencilView. Τι το θέλουμε αυτό; Το depthstate τσεκάρει ποιο pixel θα βγει στο surface. Πχ έχουμε ένα pixelA στο x= 0 y =0 z = 0 και pixelB 0,0,1 αυτά χωρίς το depth state θα ζωγραφιστούν με την σειρά τους δηλαδή θα ζωγραφιστεί το pixelA και μετά το pixelB με αποτέλεσμα το pixelB να είναι πάνω από το pixelA συνεπώς εμείς θα βλέπουμε το pixelB άσχετα αν το pixelA είναι μπροστά από το pixelB. Με το depth state το directx θα τσεκάρει ποιο pixel είναι πιο κοντά στο view port και αυτό θα ζωγραφίσει δηλαδή το pixelA.

Η αλλαγές για να βάλουμε το depth view




int InitDevice(HWND hWnd,int w,int h)
{

 ID3D10Texture2D    *pBackBuffer;
 DXGI_SWAP_CHAIN_DESC  swapChainDesc;

 
 ZeroMemory(&swapChainDesc,sizeof(swapChainDesc));
 swapChainDesc.BufferCount   = 1;
 swapChainDesc.BufferUsage   = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 swapChainDesc.SampleDesc.Count  = 1;
 swapChainDesc.OutputWindow   = hWnd;
 swapChainDesc.Windowed    = true;

 swapChainDesc.BufferDesc.Height  = h;
 swapChainDesc.BufferDesc.Width  = w;
 swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
 swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
 swapChainDesc.BufferDesc.Format  = DXGI_FORMAT_R8G8B8A8_UNORM;

 if(FAILED(D3D10CreateDeviceAndSwapChain(
  NULL,D3D10_DRIVER_TYPE_HARDWARE,NULL,D3D10_CREATE_DEVICE_DEBUG,
  D3D10_SDK_VERSION,&swapChainDesc,&pSwapChain,&pDevice)))
 {
  return 1;
 }

 if(FAILED(pSwapChain->GetBuffer(0,__uuidof(ID3D10Texture2D),(void**)&pBackBuffer)))
 {
  pSwapChain->Release();
  pDevice->Release();
  return 1;
 }
 
 if(FAILED(pDevice->CreateRenderTargetView(pBackBuffer,0,&pRenderView)))
 {
  pSwapChain->Release();
  pDevice->Release();
  pBackBuffer->Release();
  return 1;
 }
 pBackBuffer->Release();

 D3D10_TEXTURE2D_DESC depth;
 ZeroMemory(&depth,sizeof(depth));
 depth.ArraySize = 1;
 depth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
 depth.Height = Height;
 depth.Width = Width;
 depth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
 depth.SampleDesc.Count =1;
 ID3D10Texture2D *pDepthTexture;
 pDevice->CreateTexture2D(&depth,NULL,&pDepthTexture);
 pDevice->CreateDepthStencilView(pDepthTexture,NULL,&pDepthStencilView);
 pDepthTexture->Release();
 pDevice->OMSetRenderTargets(1,&pRenderView,pDepthStencilView);
 
 D3D10_VIEWPORT vp;
 vp.Height = h;
 vp.Width = w;
 vp.MaxDepth = 1.0f;
 vp.MinDepth = 0.0f;
 vp.TopLeftX = 0;
 vp.TopLeftY = 0;
 pDevice->RSSetViewports(1,&vp);
 
 return 0;
}
Με το id3d10depthstencilview να είναι global Η εφαρμογή του state που γίνεται και με c++ (βλ id3ddepthstencilstate) αλλά είπαμε, effect εύκολο. v1.fx
float4x4 WVP;
float4  ObjColor;
struct Vertex
{
 float4 pos : SV_POSITION;
};
Vertex MyVertexShader(float4 pos : POSITION)
{
 Vertex ver = (Vertex)0;
 ver.pos =  mul(pos,WVP);
 return ver;
}
float4 MyPixelShader(Vertex input) : SV_Target
{
 return ObjColor; // vale to xroma poy einai sto objColor
}
RasterizerState MyWireframe
{
 FillMode = WIREFRAME;
 CullMode = BACK;
};
DepthStencilState EnableDepth
{
 DepthEnable = true;
 DepthWriteMask = ALL;
};
technique10 Tech
{
 pass P0
 {
  SetVertexShader(CompileShader(vs_4_0, MyVertexShader() ) );
  SetGeometryShader(NULL);
  SetPixelShader(CompileShader(ps_4_0,MyPixelShader() ) );

  SetRasterizerState(MyWireframe);
  SetDepthStencilState(EnableDepth,0);

 }
}
.

Εδώ το νέο project
bin
source


1 σχόλιο: