Mesh คือ...
ในสมัยที่เรายังไม่รู้ว่าการสร้างภาพ 3มิติคืออะไร เราก็จะเรียก mesh ว่า Model บ้าง หรือ polycon กันนั่นแหละ แต่พอเรามาเขียนโปรแกรกันแล้ว เราจะคุ้นกับคำว่า mesh กันมากกว่า ผมไม่รู้ที่มานะครับว่ามันมาจากอะไร แต่ลองไปค้นหาข้อมูลใน internet กันดูได้
Mesh ที่ว่าก็คือวัตถุในโลกของ 3มิตินั่นเอง วัตถุตัวนี้จะมีองค์ประกอบเป็นรูปสามเหลี่ยมที่เอามาเรียงต่อกัน กลายเป็นรูปหลายเหลี่ยม การ render mesh 1 ก้อน ก็คือการวาดแบบที่เล็กที่สุดที่เรียกว่า draw primitives เป็นกว่าวาดรูปสามเหลี่ยมโดยการดึงข้อมูลของจุดมาทีละสามจุด เรียกว่า vertex มาวาดต่อ ๆ กัน ตามลำดับ เรียกว่า index
ในการวาดนั้นเราสามารถมีข้อมูลอื่นประกอบการวาดเช่น material , texture เป็นต้น ดังนั้นแสดงว่า การจะ render mesh ออกมาก้อนนึงต้องประกอบไปด้วยส่วนประกอบหลาย ๆ อย่าง
ส่วนประกอบของ mesh
ส่วนประกอบที่สำคัญของ mesh คือ
- vertex buffer คือ ข้อมูลต่าง ๆ ของ vertex จากที่บอกไปแล้วว่า vertex ก็คือจุด 1 จุด ที่เป็นข้อมูลที่ประกอบกันขึ้นมาเป็น mesh ชิ้นใหญ่ ๆ หนึ่งก้อน ก็พอจะเดากันได้ว่า ข้อมูลที่เก็บอยู่ใน vertex buffer นี้ต้องมี ตำแหน่งใน 3มิติแน่นอน แต่ความเป็นจริงแล้วยังมีข้อมูลอีกหลาย ๆ อย่าง ที่เราสามารถระบุให้เก็บใน vertex buffer ได้อีกด้วย
- position ตำแหน่งของจุด ประกอบไปด้วย จุด 3 จุด คือ x,y,z มีขนาดข้อมูลเป็น float
- color สีของจุด ขนาดในการเก็บอาจจะเก็บเป็น 4 byte ก็ได้ ซึ่งต้องดู device ว่าสามารถสร้างขนาดได้เท่าไหร่ บางคนอาจจะงงว่า ทำไมจุดต้องเก็บค่าสีด้วย สีเหล่านี้จะบอกว่าจุดสามจุดที่ต่อกันเป็นสามเหลี่ยมนั้นจะเป็นสามเหลี่ยมสีอะไร หาก vertex ทั้งสามเป็นคนละสีกัน รูปสามเหลี่ยมนั้นจะออกเป็นดังนี้

vertex ทั้งสาม มีสี แดง น้ำเงิน และเขียว จะ render ได้ตามภาพ เมื่อ pixcel ที่อยู่ระหว่างจุดทั้งสามจุด จุดนั้นจะมีสี ตามอัตราส่วนของความห่างจากจุดสีต่าง ๆ ทั้งสามจุดนั้นด้วย
- texture vector เป็น vector แบบ 2มิติ เก็บค่าเป็น float 2 ตัว ค่านี้จะเก็บว่า มันเรียก texture จากตำแหน่งไหนมาวาด ซึ่งทั้งสามจุดที่มีตำแหน่ง texture ไม่ตรงกัน ก็จะ render ในลักษณะเดียวกับสีนะครับ นั่นคือ pixcel แรกจะเป็นจุดตามตำแหน่งของ texture ที่กำหนดในจุดนั้น และ pixcel ต่อ ๆ มาจะคำนวณหาสีใน texture ตามอัตราส่วนของความห่างของ pixcel นั้นจาก vertex ทั้งสาม
- normal vector คือ vector ที่บอกทิศทางที่ด้านของสามเหลี่ยมนั้นหันออกไป เก็บค่าเป็น float 3 ตัว normal vector นี้จะใช้ในการคำนวณการมองเห็นของสามเหลี่ยมได้ด้วย เช่น ถ้ามุมมองของกล้องหันสวนกับทิศทางของ normal vector นั่นแสดงว่า สามเหลี่ยมนั้นหันหน้าเข้าหาเรา นั่นคือเราจะมองเห็นนั่นเอง
นอกจากนี้ยังมีค่าอื่น ๆ ให้เราสามารถเก็บเพิ่มเติมได้ ซึ่งเราสามารถนำไปใช้ในการคำนวณเรื่อง shader ซึ่งจะกล่าวในโอกาสข้างหน้านะครับ - index buffer เป็นการบอกว่าสามเหลี่ยมแต่ละรูปที่จะ render นั้นเรียงตามลำดับอย่างไร ค่าที่เก็บก็คือหมายเลขของ vertex นั่นเอง ซึ่ง index นี้จะต้องเก็บเป็นชุด ชุดละ 3 ค่าเสมอครับ (ก็วาดสามเหลี่ยมนี่นะ)
นอกจากนี้ยังมีส่วนประกอบอื่น ๆ ที่สามารถเอามาประกอบกับ mesh เพือเพิ่มความสามารถในการ render mesh นั้นได้
- texture เป็นภาพ ที่เราสามารถสร้างหรือโหลดเข้ามาเก็บไว้ได้ texture เหล่านี้เราสามารถใช้ร่วมกับ texture coordinate เพื่อสร้างเป็นพื้นผิวให้กับ mesh ได้ครับ
- animation เราสามารถสร้าง mesh ผ่านโปรแกรม 3D อย่าง 3Dsmax เพื่อสร้าง animation ต่าง ๆ ให้กับ mesh เราได้
- world matrix เป็น matrix ที่ใช้ ควบคุม mesh ในการวาด ให้เคลื่อนที่ (transform) หมุน (rotate) และขยาย (scale) ได้
การ render ด้วย vertex buffer และ index buffer แบบง่าย ๆ ใน DirectX
|
#define FVF_SAMPLE D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX1 struct SampleVector{ D3DXVECTOR3 position; D3DXVECTOR3 normal; DWORD col; float u,v; //Texture } LPDIRECT3DVERTEXBUFFER9 g_pd3dVb = NULL; LPDIRECT3DINDEXBUFFER9 g_pd3dIb = NULL; |
เรากำหมด ประเภทของ vertex buffer ก่อนว่าเราจะเก็บค่าอะไรไว้บ้าง จากตัวอย่างเราเก็บค่าตำแหน่ง normal vector ค่าสี (diffuse) และตำแหน่งของ texture (texture coordinate)
จากนั้นก็สร้าง struct ขึ้นมาเพื่ออ้างอิงค่าต่าง ๆ ใน vertex buffer ได้ง่าย ๆ
สร้างตัวแปร ที่ใช้สร้าง vertex buffer และ index buffer ไว้
|
float u1,u2,v1,v2; u1=0.0f; u2=1.0f; v1=0.0f; v2=1.0f; SampleVector VertexArray[4] = { { 0-1, 0+1,0, 0.0f,0.0f,1.0f, D3DCOLOR_XRGB(255,255,255), u1,v1 }, { 0+1, 0+1,0, 0.0f,0.0f,1.0f, D3DCOLOR_XRGB(255,255,255), u2,v1 }, { 0+1, 0-1,0, 0.0f,0.0f,1.0f, D3DCOLOR_XRGB(255,255,255), u2,v2 }, { 0-1, 0-1,0, 0.0f,0.0f,1.0f, D3DCOLOR_XRGB(255,255,255), u1,v2 }, }; unsigned int IndexArray[6] = {0, 1, 2,0, 2, 3}; if(FAILED(g_pd3dDevice->CreateVertexBuffer(4* sizeof(SampleVector),D3DUSAGE_WRITEONLY,FVF_SAMPLE,D3DPOOL_MANAGED,&g_pd3dVb,NULL))) return -1; if(FAILED(g_pd3dVb->Lock(0,0,(void**)&BufferBlt1,0)))return -1; memcpy(BufferBlt1,&VertexArray[0],4* sizeof(SampleVector)); if(FAILED(g_pd3dVb->Unlock()))return -1; if(FAILED(g_pd3dDevice->CreateIndexBuffer(6 * sizeof(unsigned int),D3DUSAGE_WRITEONLY,D3DFMT_INDEX32,D3DPOOL_MANAGED,&g_pd3dIb,NULL))) return -1; if(FAILED(g_pd3dIb->Lock(0,0,(void**)&BufferBlt1,0)))return -1; memcpy(BufferBlt1,&IndexArray[0],6*sizeof(unsigned int)); if(FAILED(g_pd3dIb->Unlock()))return -1; |
ส่วนแรกเรากำหนดค่า u,v ที่มีค่า เป็น 0 และ 1 หมายถึงการวาดภาพ ด้วย texture แบบเต็มภาพ การเก็บขนาดของภาพนั้นเราจะเก็บเป็น float เนื่องจากเราจะไม่สนใจว่า texture ที่จะใช้ render ขนาดเป็นเท่าไหร่ หากกำหนดเป็น 0 ก็หมายตำแหน่งแรกสุด และถ้ากำหนดเป็น 1 ก็หมายถึงตำแหน่งท้ายสุด
จากนั้นนำมาเตรียมข้อมูลเพื่อสร้าง vertex buffer ที่เป็น array ขนาด 4 จุด
จากข้อมูลนั้นจะได้เป็น จุด 4 จุดที่มี normal vector หันไปทางแกน z ที่เป็นบวก ส่วนค่าสีก็จะเป็นสีขาวทั้งหมด ตามคำสั่ง D3DCOLOR_XRGB
จากนั้นก็เตรียมข้อมูล สำหรับ index buffer จะเห็นว่ามี 6 ค่า แสดงว่าเป็นรูป สามเหลี่ยม 2 รูปนั่นเอง ในข้อมูลจะเห็นอีกว่า ใน index เราสามารถใช้ vertex ซ้ำกันได้ครับ เพราะรูป สามเหลี่ยมที่เราจะวาดนั้นเป็นสามเหลี่ยมที่ต่อกันจนเป็นสี่เหลี่ยมนั่นเอง
ทำการ CreateVertexBuffer โดยกำหนดขนาดเป็น byte ครับ และ ลำดับข้อมูลที่เก็บในแต่ละจุดของ buffer ก็เรียงกันตาม FVF_SAMPLE ครับ ทำการ copy ข้อมูลด้วย memcpy ครับ
ในส่วนของ CreateIndexBuffer ก็เช่นเดียวกันครับ
|
g_pd3dDevice->SetStreamSource(0,g_pd3dVb,0,sizeof(SampleVector)); g_pd3dDevice->SetFVF(FVF_SAMPLE); g_pd3dDevice->SetTexture(0,texture); g_pd3dDevice->SetIndices(g_pd3dIb); g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,4,0,2); g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2); |
ในขั้นตอนการ render ด้วย vertex buffer และ index buffer นั้น ก็แค่ เซ็ทว่าให้อ่านค่าของ vertex buffer ผ่าน g_pd3dVb ที่เราทำไว้ครับ
ในส่วนของ SetTexture จะเป็นการสร้าง render ด้วย texture นี้ (ในกรณีที่ใช้ shader เราจะส่งค่า texture เข้าไปใน shader เองครับ)
จากนั้นก็ SetIndices ติดตั้งตัว index ในการวาด ติดตั้งวิธีการวาด DrawIndexedPrimitive
แล้ว DrawPrimitive มันโลดครับ
การสร้าง mesh ด้วย vertex buffer และ index buffer
|
SampleVector *verts = NULL; DWORD *dice = NULL; LPD3DXMESH pMesh; D3DXCreateMeshFVF(2,4,D3DXMESH_MANAGED,FVF_SAMPLE,g_pd3dDevice,&pMesh); g_meshstagemap->pMesh->LockVertexBuffer(0,(void**)&verts); memcpy( verts, VertexArray[0], sizeof(SampleVector)*4); g_meshstagemap->pMesh->UnlockVertexBuffer(); g_meshstagemap->pMesh->LockIndexBuffer(0,(void**)&dice); memcpy( dice, IndexArray[0], sizeof(DWORD)*6); g_meshstagemap->pMesh->UnlockIndexBuffer(); pMesh->DrawSubset( 0 ); |
หลังจากที่เราได้ vertex buffer และ index buffer มาแล้ว เราสามารถเอามาสร้างต่อให้เป็น mesh ได้ด้วยคำสั่ง D3DXCreateMeshFVF ที่มี สามเหลี่ยม 2 อัน จากจุด 4 จุด
จากนั้นทำการ lock ตัว vertex และ index ไว้ เพื่อให้เราสามารถใส่ข้อมูลเข้าไปได้ ด้วยคำสั่ง memcpy เมือใส่ข้อมูลเข้าไปเสร็จ ก็สั่ง unlock ซะ
สั่งวาดด้วย DrawSubset
(จริง ๆ แล้วขั้นตอนในการ render ยังมีอีกหลายส่วนนะครับ แล้วจะพูดถึงอย่างละเอียดต่อไปครับ)
</span></span></span></span>
