By LynxLuna
1
Daftar Isi
Daftar Isi.......................................................................................... 2
Pendahuluan .................................................................................... 6
Asumsi ................................................................................................... 6
Development Tools .................................................................................... 7
Hardware................................................................................................ 7
Instalasi ................................................................................................. 7
Coding Convention .................................................................................... 8
Tentang DirectX .............................................................................. 10
Buzzword 'DirectX' ....................................................................................10
Komponen-komponen yang membangun .................................................................10
Kenapa harus DirectX ................................................................................11
Arsitektur DirectX ....................................................................................11
Component Object Model...................................................................................11
Arsitektur Global DirectX ...................................................................................12
Library D3DX ..................................................................................................12
Math before you code ....................................................................... 13
Vector 3 Dimensi......................................................................................13
Kesamaan Vector.............................................................................................16
Menghitung magnitude vector .............................................................................16
Normalize Vector ............................................................................................17
Penambahan vector..........................................................................................18
Pengurangan vector .........................................................................................18
Perkalian skalar ..............................................................................................19
Dot Products ..................................................................................................20
Cross Products ................................................................................................20
Matrix...................................................................................................21
Kesamaan, perkalian skalar, dan penambahan..........................................................22
Perkalian Matrix ..............................................................................................22
Matrix Identitas ..............................................................................................23
Inverse .........................................................................................................23
Transpose Matrix .............................................................................................24
Matrix D3DX ...................................................................................................24
Transformasi Dasar ...................................................................................27
Matrix Translasi ..............................................................................................28
Matrix Rotasi..................................................................................................29
Matrix Scaling.................................................................................................30
Kombinasi Transformasi.....................................................................................31
Fungsi-fungsi untuk menangani transformasi ............................................................32
Plane (Bidang).........................................................................................33
D3DXPLANE....................................................................................................34
Hubungan Point dan Plane..................................................................................34
Konstruksi .....................................................................................................35
Me-normalize plane..........................................................................................36
Transformasi Plane ..........................................................................................36
Point terdekat dalam plane terhadap point lain........................................................37
Rays .....................................................................................................37
Pengertian Ray ...............................................................................................37
Perpotongan Rays dan Plane ...............................................................................38
Program DirectX Pertama................................................................... 40
Dasar Pemrograman Windows ......................................................................40
Struktur Aplikasi Windows ..................................................................................40
Window dan Message. .......................................................................................46
Membuat dan menampilkan window ......................................................................46
Pengenalan Direct3D .................................................................................53
REF device ....................................................................................................54
2
D3DDEVTYPE ..................................................................................................54
Beberapa hal yang perlu diperhatikan ............................................................54
Surface.........................................................................................................54
Multisample ...................................................................................................55
Pixel Format ..................................................................................................56
Memory Pool ..................................................................................................57
Swap Chain dan Page Flipping .............................................................................57
Depth Buffer ..................................................................................................58
Vertex Processing ............................................................................................59
Device Capabilities ..........................................................................................59
Menambahkan Direct3D..............................................................................60
Membuat Direct3D Object ..................................................................................60
Checking Hardware Vertex Processing....................................................................60
Mengisi structure D3DPRESENT_PARAMETERS .........................................................61
Membuat object IDirect3DDevice9...................................................................63
Operasi Dasar Device.................................................................................64
Membersihkan layar .........................................................................................64
Menampilkan ke layar .......................................................................................65
Melepaskan object Direct3D................................................................................66
Update the code!! ....................................................................................66
Implementasi InitDirect3D.............................................................................67
Implementasi DoFrame .....................................................................................69
Implementasi Cleanup .....................................................................................69
Implementasi EnterMessageLoop ......................................................................70
Implementasi ErrorMessage.............................................................................71
Implementasi CenterWindow.............................................................................72
Perubahan pada InitWindow .............................................................................73
Perubahan pada WndProc..................................................................................74
Menambahkan library Direct3D dan WinMM kemudian mem-build aplikasi .........................75
Rendering Pipeline ........................................................................... 77
Representasi model ..................................................................................77
Vertex Format ................................................................................................78
Triangles.......................................................................................................79
Index ...........................................................................................................79
Virtual Camera ........................................................................................80
Rendering Pipeline ...................................................................................81
Local Space ...................................................................................................81
World Space...................................................................................................82
View Space ....................................................................................................83
BackFace Culling .............................................................................................84
Lighting ........................................................................................................85
Clipping ........................................................................................................85
Proyeksi........................................................................................................86
Viewport transform ..........................................................................................87
Rasterization..................................................................................................89
Menggambar di DirectX...................................................................... 90
Vertex Buffer dan Index Buffer.....................................................................90
Membuat Vertex Buffer dan Index Buffer ................................................................90
Mengakses Buffer Memory ..................................................................................92
Mendapatkan informasi Vertex Buffer dan Index Buffer...............................................94
Render States.................................................................................................94
Persiapan untuk menggambar ......................................................................95
Menggambar Vertex dan Index Buffer .............................................................95
IDirect3DDevice9::DrawPrimitive ...............................................................96
IDirect3DDevice9::DrawIndexedPrimitive ...................................................96
Begin dan End Scene ........................................................................................98
D3DX Geometry .......................................................................................98
Let's Code!! ............................................................................................99
Mengedit Skeleton Code .................................................................................. 100
Sample Code Screenshot .......................................................................... 105
3
Segitiga ...................................................................................................... 105
Kubus......................................................................................................... 105
Teapot ....................................................................................................... 106
Bentuk-bentuk.............................................................................................. 106
Warna ..........................................................................................107
Representasi warna ................................................................................ 107
Vertex Color ......................................................................................... 108
Shading ............................................................................................... 109
Code to The End! ................................................................................... 109
Sample Code Screenshot .......................................................................... 112
Segitiga ...................................................................................................... 112
Kubus......................................................................................................... 112
Lighting/Pencahayaan ......................................................................113
Komponen warna ................................................................................... 113
Material .............................................................................................. 113
Vertex Normal....................................................................................... 115
Light Sources ........................................................................................ 117
Code Your Lights.................................................................................... 120
Sample Code Screenshot .......................................................................... 123
Pyramid...................................................................................................... 123
Directional Lights .......................................................................................... 123
Point Lights ................................................................................................. 124
Spot Lights .................................................................................................. 124
Texturing......................................................................................125
Texture Coordinate ................................................................................ 125
Membuat dan mengaktifkan texture ............................................................ 126
Filters................................................................................................. 127
Mipmap ............................................................................................... 128
Mipmap filter ............................................................................................... 128
Menggunakan mipmap dalam Direct3D ................................................................. 129
Address Mode........................................................................................ 129
Let's Code The Textured Quad ................................................................... 131
Samples ScreenShot ................................................................................ 135
Quad Plain................................................................................................... 135
TexQuad ..................................................................................................... 135
CubeTex ..................................................................................................... 136
Multitexturing ....................................................................................... 137
ScreenShot Multitextured Quad.......................................................................... 138
Blending .......................................................................................139
Persamaan Blending................................................................................ 139
Blend Factor ......................................................................................... 141
Transparansi ......................................................................................... 141
Alpha Channels ............................................................................................. 142
Menentukan sumber alpha................................................................................ 142
Menggunakan DirectX Texture Tool untuk Alpha Channel ................................... 143
Code and Blend ..................................................................................... 144
Sample ScreenShot ................................................................................. 147
Material Blend .............................................................................................. 147
Texture Alpha Blend....................................................................................... 147
Stenciling......................................................................................148
Menggunakan Stencil Buffer ...................................................................... 149
Meminta Alokasi Stencil Buffer .......................................................................... 150
Stencil Test ................................................................................................. 150
Mengkontrol Stencil Test ................................................................................. 150
Mengupdate stencil buffer................................................................................ 152
Stencil Write Mask ......................................................................................... 152
Aplikasi I : Mirror ................................................................................... 153
Metode Implementasi Mirror ............................................................................. 154
Implementasi Mirror dalam Code........................................................................ 155
4
Aplikasi II : Planar Shadow ........................................................................ 157
Shadow dari parallel light ................................................................................ 158
Shadow dari point light ................................................................................... 159
Shadow Matrix .............................................................................................. 159
Menggunakan stencil buffer untuk mengatasi double blending..................................... 160
Implementasi Shadow dalam Code ...................................................................... 161
ScreenShot Sample Code .......................................................................... 163
Mirror ........................................................................................................ 163
Shadow ...................................................................................................... 163
Shadow Mirror .............................................................................................. 164
Penutup .......................................................................................165
5
Pendahuluan
Pemrograman grafis merupakan pekerjaan yang unik, menyenangkan, sekaligus menantang dalam
dunia komputer sekarang ini. Hanya programmer grafis yang bisa merealisasikan dunia maya yang
mungkin hanya ada dalam mimpi ke dalam layar komputer Anda.
Perkembangan teknologi grafis yang begitu pesat mulai awal milenium ini menuntut upgrade
kemampuan dan pengetahuan. Dunia grafis yang dulu hanya berkutat pada dunia dua dimensi mulai
berlari melesat begitu cepat masuk ke dalam dunia 3 Dimensi. Dunia 3 dimensi yang pada mulanya
hanya dapat dinikmati oleh para ilmuwan di lab universitas atau di lab perusahaan besar kini dapat
dinikmati oleh sebagian besar pemilik PC rumahan biasa.
PC rumahan biasa kini bisa menampilkan visualisasi dan simulasi 3D, games 3D yang realistis berkat
perkembangan teknologi hardware maupun software 3 Dimensi. Dimulai dengan diperkenalkannya
hardware Graphic Accelerator yang memungkinkan ditampilkannya gambar 3 Dimensi pada PC
rumahan, kemudian dengan sangat cepat, perkembangan teknologi 3 Dimensi ini menjalar, tidak
hanya untuk keperluan pengetahuan, tp juga untuk hiburan, misalkan dalam game 3 Dimensi.
Microsoft Windows, sebuah sistem operasi yang paling banyak dipakai untuk PC rumahan pada
mulanya bukanlah suatu platform favorit untuk pengembangan aplikasi grafik. Sistem Operasi ini
tidak mengizinkan akses langsung ke hardware, sehingga untuk menggambar ke dalam layar relatif
lebih lambat dibandingkan dengan mengakses langsung hardware yang biasa dilakukan aplikasi grafik
yang berjalan di atas DOS.
Hal tersebut pelan2 mulai berganti dengan ditemukannya DirectX oleh Microsoft. DirectX
mengizinkan programmer untuk mengakses hardware secara langsung dengan cara yang seragam.
Sebelumnya, programmer harus melakukan perlakuan yg berbeda jika ingin mengakses hardware
video secara langsung bila berbeda produsen. Dengan DirectX, programmer dapat mengaksesnya
secara langsung dengan perintah yang sama karena operasi yang vendor-specific sudah ditangani
oleh driver.
Perkembangan Microsoft DirectX begitu cepat. Sampai saat ini DirectX sudah mencapai versinya
yang ke-10. Akan tetapi, DirectX 10 ini hanya bisa diakses pada Windows Vista. Untuk tutorial dalam
buku ini kesemuanya ditulis menggunakan DirectX 9 yang bisa berjalan di Windows 98 SE ke atas.
DirectX 9 sudah memuat teknologi yang amat mumpuni untuk aplikasi 3D. DirectX 9 sudah
mendukung vertex dan pixel shader yang memungkinkan programmer memrogram GPU untuk
menghasilkan suatu efek yang diinginkan seperti per-pixel lighting, distortion, dan lain sebagainya.
Asumsi
Penulis berasumsi bahwasanya pembaca sudah mengetahui dan menguasai bahasa yang akan
digunakan dalam seluruh buku ini, yaitu C++. Pengetahuan Aljabar Linear akan menjadi keuntungan
tersendiri, dan pengetahuan tentang Object-Oriented Programming akan semakin mempermudah
pembaca dalam memahami isi buku ini.
6
Development Tools
Development tools yang digunakan dalam buku ini adalah Microsoft Visual C++ 2005. Anda bisa
menggunakan versi apa saja, boleh versi Express yang gratis, maupun professional. Untuk
menggunakan Microsoft Visual C++ 2005 Express Edition, anda memerlukan Windows Platform SDK.
Untuk edisi Standard dan Professional, Platform SDK sudah terinstall bersama-sama dengan Visual
Studio. Selain itu, perlu juga Microsoft DirectX SDK yang terbaru, ketika buku ini ditulis, DXSDK
terbaru dirilis pada bulan Februari 2007. Jadi, untuk mengkompilasi seluruh sample di buku ini Anda
memerlukan :
Hardware
Hardware yang diperlukan supaya contoh-contoh dalam buku ini dapat tampil semuanya adalah
hardware yang mendukung Microsoft DirectX 9 secara penuh. Contohnya adalah GeForce FX 5200 ke
atas atau ATI Radeon 9700 ke atas. Disarankan yang mendukung hardware Pixel Shader dan Vertex
Shader 2.0.
Instalasi
Setelah menginstalasi Visual C++, Anda perlu menginstalasi Microsoft DirectX SDK dan mensetting
Visual C++ Anda supaya dapat menggunakan Microsoft DirectX SDK dalam program Anda. Untuk
menginstall Microsoft DirectX SDK, cukup dengan mengeksekusi file SETUP.EXE. Maka secara default
akan terinstall di c:\Program Files\Microsoft DirectX SDK (February 2007). Setelah Microsoft
DirectX SDK terinstall, kita perlu mensetting MSVC supaya dapat mengakses library DirectX tersebut.
Untuk mensettingnya, buka menu Tools > Options kemudian pilih Project and Solutions > VC++
Directories.
7
Pilih Include Files pada Show Directories For, kemudian click tombol untuk menambahkan
directory Include pada DirectX SDK. Jika DXSDK Anda terinstall di c:\Program Files\Microsoft
DirectX SDK (February 2007), maka tambahkan directory c:\Program Files\Microsoft DirectX SDK
(February 2007)\Include untuk Include Files.
Kemudian pindahkan pilihan ke LIBRARY FILES pada Show Directories For. Klik tombol
kemudian tambahkan directory C:\Program Files\Microsoft DirectX SDK (February 2007)\Lib\x86
jika DXSDK Anda terinstall di C:\Program Files\Microsoft DirectX SDK (February 2007)\.
Coding Convention
Saya menggunakan coding convention yang sama dengan Microsoft yaitu menggunakan Hungarian
Notation. Dengan prefix g_ untuk global variable, m_ untuk private member dalam class, dan p
untuk pointer. Untuk integer, saya memakai prefix n jika menyatakan suatu total jumlah dan i jika
menyatakan suatu index. Mari kita lihat coding convention yang saya pakai :
class nMyClass
{
public :
nMyClass ( Vector3D &v ) ;
~nMyClass ( void ) ;
private :
Vector3D m_Position ;
} ;
Kemudian untuk if dan for, saya menggunakan style spt ini
8
if ( x == y && i != 0 )
{
. . .
}
Saya selalu menyisipkan satu spasi setiap ada tanda spt kurung, sama dengan dan sebagainya. Dan
saya tidak pernah menggunakan style syntax if atau for one-liner spt ini :
if ( x == y )
dosomething( ) ;
Hal ini semata-mata supaya code saya mudah dibaca. Semua code yang saya tulis dalam buku ini
menekankan pada kejelasan bukan performa sehingga mungkin sebagian besar dari code yang saya
tulis dalam sample buku ini tidak optimal.
9
Tentang DirectX
Microsoft DirectX. Mungkin Anda sering mendengarnya dalam iklan graphics card atau dari teman
Anda yg faham tentang hardware. Tp sebenarnya apa DirectX itu? DirectX sebenarnya adalah sebuah
kumpulan API (Application Programming Interface) yang paling banyak digunakan dalam platform
Microsoft Windows untuk membuat game atau aplikasi multimedia lainnya. Bicara tentang DirectX,
tidak bisa dilepaskan dari game. Beberapa tahun sebelum adanya DirectX, para pembuat game repot
mengatasi kompatibilitas antara hardware satu dengan lainnya. Satu game mungkin bisa dipakai
dalam satu hardware tetapi tidak bisa dimainkan di hardware lainnya. Kemudian Microsoft
menemukan DirectX yang menyediakan sebuah API yang seragam yang dijalankan pada PC dengen
konfigurasi hardware yang berbeda-beda. Setelah DirectX direlease oleh Microsoft, jumlah game dan
aplikasi multimedia yang diproduksi untuk Windows jumlahnya semakin banyak.
Buzzword 'DirectX'
DirectX adalah sebuah kumpulan API dari Microsoft yang memungkinkan developer game untuk
mengakses hardware secara langsung dan berjalan di atas platform Microsoft Windows. Sampai pada
versinya yang ke-10 saat ini, DirectX API menyediakan akses ke beberapa bagian hardware seperti
grafik, suara, networking, input, menggunakan satu interface yang standard. Dengan interface ini,
developer dapat dengan mudah memrogram suatu aplikasi multimedia secara seragam, tidak peduli
merek dan jenis hardware yang dipakai.
• DirectX Graphics. Komponen ini adalah yang menangani output grafis baik 2D maupun 3D.
• DirectInput. Semua input ditangani oleh API ini. DirectInput mendukung berbagai macam
hardware dr keyboard, mouse, joystick, dan lain-lain.
• DirectSound. Jika Anda memerlukan suara sebagai background music atau sound effect,
maka API inilah yang dipakai. DirectSound memberikan kontrol penuh terhadap suara yg
dimainkan dalam bentuk Waveform.
• DirectMusic. API yang digunakan untuk memainkan soundtrack dinamis dari MIDI atau
synthesizer lain. Musik bisa dimainkan dengan mengubah pitch, tempo, dan lain sebagainya.
• DirectShow. Jika Anda ingin menambahkan cutscene ke dalam aplikasi atau game Anda,
DirectShow adalah API yang Anda gunakan. Komponen inilah yg merupakan komponen utama
yang digunakan dalam media player spt Windows Media Player.
10
Kenapa harus DirectX
Sebelum Windows direlease, developer membuat game untuk DOS. Developer langsung mengakses
hardware karena memang hal tersebut diizinkan oleh DOS. Keuntungannya adalah developer
memiliki akses penuh ke dalam hardware sehingga dapat mengkontrol dan menggunakan
kemampuan hardware secara penuh. Kekurangannya adalah setiap developer harus membuat driver
untuk hardware tersebut, bahkan untuk VGA dan sound card, developer harus berjuang membuat
gamenya untuk dapat berjalan di VGA dan sound card tertentu. Membuat aplikasi atau game yang
mendukung resolusi di atas 320x240, memerlukan developer untuk mengakses video register dan
menulis langsung ke video memory. Kesulitan ini ditambah dengan perbedaan antara hardware satu
dengan lainnya.
Windows 3.1. pun direlease oleh Microsoft. Windows berjalan di atas DOS, dan juga ada pembatasan
dalam mengakses hardware. Satu-satunya cara menggambar di Windows saat itu adalah dengan
menggunakan GDI (Generic Display Interface) yang jauh lebih lambat daripada menulis langsung ke
hardware sehingga pada waktu itu, game untuk Windows umumnya adalah game yang tidak
memerlukan 'gerak cepat' seperti game kartu sedangkan game action dan game-game yang
memerlukan gerak cepat dan dinamis tetap dibuat dalam DOS.
Pertama kali Microsoft me-release WinG, sebuah API untuk membuat game di Windows untuk
mengatasi kekurangan Windows 3.1. tersebut. Ini adalah mbahnya DirectX yang kita kenal sekarang
ini. Sampai saat ini, DirectX sudah mencapai versinya yang ke-10. Tiap versi mempunyai
penambahan feature dan penyempurnaan fungsi. Untuk DirectX 10 ini, penambahan fungsi yang
terlihat adalah penambahan Geometry Shader dan penghilangan Fixed Functionality dari DirectX.
DirectX 10 hanya bisa diakses di Windows Vista. Untuk aplikasi dalam buku ini keseluruhannya
dibuat dengan DirectX 9.
Arsitektur DirectX
DirectX membuat hidup developer game dan multimedia menjadi lebih mudah. Developer
mendapatkan kumpulan library dan support dari Microsoft. DirectX berevolusi dengan cepat,
menambah feature dan kecepatan untuk hardware terbaru. Microsoft ingin supaya tiap release dari
DirectX mempunyai backward compatibility sehingga aplikasi yang ditulis pada versi sebelumnya
berjalan tanpa masalah di versi yang terbaru. Oleh karena itu, Microsoft membuat DirectX berjalan
di atas teknologi COM (Component Object Model).
11
Arsitektur Global DirectX
Win32 Application
Direct3D API
HAL Layer
Hardware
DirectX dibangun dalam 2 layer, yaitu API layer dan HAL layer. API layer berkomunikasi dengan
hardware melalui HAL (Hardware Abstraction Layer). HAL menyediakan interface standard ke
DirectX dan juga berkomunikasi langsung dengan hardware melalui Device Driver. Karena HAL perlu
mengetahui komunikasi dengan Device Driver, pembuat hardware lah yang membuat HAL untuk
hardware yang diproduksinya. Developer mengakses HAL secara tidak langsung melalui API DirectX.
Gambar 1.1. menggambarkan bagaimana HAL dalam arsitektur DirectX.
Library D3DX
Sejak DirectX 7, Microsoft memasukkan D3DX (Direct3D eXtension) library. Library ini berisi fungsi-
fungsi, class, dan interface yang memudahkan operasi 3D yang umum dilakukan seperti operasi
matematik, texture, image, mesh, dan shader.
Kita menggunakan D3DX dalam seluruh buku ini untuk memfokuskan kita pada material yg lebih
menarik daripada sekedar mengimplementasikan hitungan matematis.
12
Math before you code
Sebelum menyelam ke dalam code, ada baiknya kita membahas dahulu sedikit tentang matematika.
Ya, matematika. Seorang programmer grafis tidak akan bisa mengcode tanpa mengetahui
matematika. Game tak akan bisa dibangun tanpa matematika. Selain itu, semua sample di buku ini
menggunakan dasar matematika. Oleh karena itulah, saya membahas matematika sebelum kita
masuk ke dalam code.
Vector 3 Dimensi
Dalam geometri, vector didefinisikan sebagai garis yang mempunyai arah seperti terlihat dalam
gambar 1.2.
a
Kepala
b
u
c
v
Ekor
Lokasi bukanlah properti dari suatu vector, sehingga vector dengan arah dan panjang yang sama
sehingga terlihat paralel satu sama lain, maka dua vector tersebut adalah sama. Contoh dalam
gambar 1.2. di atas adalah vector u dan v.
Selain itu, kita juga harus mengetahui tentang sistem koordinat dalam aplikasi 3D. Ada 2 sistem
koordinat yaitu left handed system dan right handed system, perbedaan keduanya adalah dalam
arah sumbu Z positif. Secara default, DirectX menggunakan Left-Handed System.
Y+ Y+
Z+
X+ X+
Z+
Gambar 1.3. Right Handed System (Kiri) dan Left Handed System (Kanan)
13
Karena lokasi vector tidak mengubah propertinya, kita bisa menempatkan vector sehingga ekornya
berimpit pada titik origin O (0,0) pada sistem koordinat. Posisi inilah yang disebut sebagai posisi
standard. Dalam posisi standard ini, kita bisa mendeskripsikan vector dengan menyebutkan posisi
koordinat dari kepala vector tersebut.
c
b
u
v=
Catatan Khusus :
Karena kita bisa mendeskripsikan vector dengan posisi koordinat kepala vector, sering terjadi
kesalahan dalam membedakan vector dan point. Point menunjukkan suatu lokasi, sedangkan
vector menunjukkan suatu arah dengan panjang tertentu.
Kita menyatakan vector dengan huruf tebal, terkadang huruf kecil, terkadang huruf besar,
tergantung kebutuhan. Kita tidak terpaku pada aturan notasi matematis di sini. Contoh untuk vector
2, 3, dan 4 dimensi :
Y+
j Z+
0 i X+
14
Perhatikan gambar 1.5! Pada gambar tersebut saya akan memperkenalkan vector spesial, yaitu unit
vector dan zero-vector. Zero vector adalah sebuah vector dengan seluruh komponennya adalah 0.
Dinyatakan dengan 0 = (0, 0, 0). Tiga vector selanjutnya disebut unit vector untuk ℜ3 yaitu vector i,
j, dan k. Vector-vector tersebut mempunyai panjang 1 unit. Vector i, j, dan k dinyatakan dengan i =
(1, 0, 0) ; j = (0, 1, 0) ; dan k = (0, 0, 1).
// casting
operator FLOAT* ( );
operator CONST FLOAT* ( ) const;
// assignment operators
D3DXVECTOR3& operator += ( CONST D3DXVECTOR3& );
D3DXVECTOR3& operator -= ( CONST D3DXVECTOR3& );
D3DXVECTOR3& operator *= ( FLOAT );
D3DXVECTOR3& operator /= ( FLOAT );
// unary operators
D3DXVECTOR3 operator + ( ) const;
D3DXVECTOR3 operator - ( ) const;
// binary operators
D3DXVECTOR3 operator + ( CONST D3DXVECTOR3& ) const;
D3DXVECTOR3 operator - ( CONST D3DXVECTOR3& ) const;
D3DXVECTOR3 operator * ( FLOAT ) const;
D3DXVECTOR3 operator / ( FLOAT ) const;
friend D3DXVECTOR3 operator * ( FLOAT,
CONST struct D3DXVECTOR3& );
} D3DXVECTOR3, *LPD3DXVECTOR3;
Structure di atas merupakan turunan dari D3DVECTOR yang didefinisikan sebagai berikut :
Seperti scalar, vector juga punya aritmetika khusus seperti yang terlihat dalam definisii
D3DXVECTOR3. Implementasi dari perhitungan tersebut sudah diimplementasikan dalam library
D3DX, kita tinggal memakainya.
15
Catatan Khusus :
Walaupun kita paling sering menggunakan vector 3D, terkadang kita memakai vector 2D dan 4D
(quarternions) dalam pemrograman grafis 3D. D3DX Library menyediakan class D3DXVECTOR2
dan D3DXVECTOR4 untuk menyatakan vector 2D dan 4D. Seluruh aritmetika untuk vector 3D
dapat dipakai dalam vector lain, kecuali cross product.
Kesamaan Vector
Secara geometri, dua vector dinyatakan sama jika mempunyai arah dan panjang yang sama. Secara
aljabar, kita katakan dua vector adalah sama jika mempunyai dimensi yang sama dan semua
komponennya sama. Contoh : (ux, uy, uz) = (vx, vy, vz) jika ux = vx, uy = vy, dan uz = vz.
Dalam code kita mengetest apakah dua vector itu sama dengan menggunakan tanda kesamaan (==)
yang sudah dioverload.
if ( u == v )
{
return true ;
}
Atau kita bisa mengetest apakah dua vector tidak sama dengen menggunakan tanda ketidaksamaan
(!=) yang sudah dioverload.
if ( u != v )
{
return true ;
}
Catatan Khusus :
Ketika membandingkan dua buah floating point (bilangan pecahan), harus berhati-hati, karena
dua buah bilangan float yang sama, bisa saja berbeda. Sehingga tidak benar-benar sama tetapi
hampir sama. Oleh karena itu, kitat mendefinisikan EPSILON sebagai referensi, yaitu bilangan
yang amat kecil. Kita bisa menganggap dua bilangan float sama jika selisihnya lebih kecil atau
sama dengan EPSILON. Lihat contoh berikut :
2 2 2
u = ux + uy + uz
16
Tanda ∥u∥ artinya adalah panjang dari vector u.
u = 4 2 + 7 2 + 3 2 = 74
v = 2 2 + 5 2 = 29
Menggunakan D3DX Library, kita bisa mencari magnitude dari suatu vector dengan fungsi sbb :
Normalize Vector
Normalize Vector adalah membuat panjang vector menjadi 1. Kita bisa menormalize vector dengan
membagi tiap komponen dengan panjangnya, rumusnya sbb :
u ⎛⎜ u x u y u z ⎞⎟
û= = , ,
u ⎜⎝ u u u ⎟⎠
u ⎛ 4 7 3 ⎞
û= = ⎜⎜ , , ⎟⎟
74 ⎝ 74 74 74 ⎠
menggunakan D3DX Library, kita bisa menghitung normalize vector dengan fungsi sbb :
D3DXVECTOR3 *D3DXVec3Normalize(
D3DXVECTOR3* pOut, // Result.
CONST D3DXVECTOR3* pV // The vector to normalize.
);
17
Penambahan vector
Kita dapat menambahkan dua vector atau lebih dengan menambahkan komponen pada masing-
masing vector untuk membentuk vector baru.
v
u+
u
Gambar di atas menunjukkan penambahan vector u dan vector v. Vector v digeser sehingga ekornya
berimpit dengan kepala vector v sehingga menghasilkan vector u + v seperti di atas. Dalam code,
untuk menambahkan vector bisa menggunakan operator + yang sudah dioverload.
Pengurangan vector
Vector dapat dikurangkan dengan mengurangkan masing-masing komponen dalam vector tersebut.
18
u-
v
u
u-
v
v
Vector (u-v) dapat dinyatakan dengan menarik garis dari kepala vector v ke kepala vector u. Garis
tersebut dapat digeser hingga vector dalam posisi standard. Jika kepala vector dianggap sebagai
point, maka kita bisa mendapatkan vector antara dua point tersebut dengan mengurangi dua point
tersebut.
Dalam code, pengurangan bisa dilakukan dengan menggunakan operator - yang sudah dioverload.
Perkalian skalar
Perkalian skalar akan mengubah panjang vector. Arah dari vector akan tetap seperti semula,
terkecuali faktor perkaliannya negatif, maka arah vector akan berbalik
3u
u
19
Perkalian tersebut dapat dirumuskan
dalam code, jika menggunakan D3DX, dapat menggunakan tanda * yang sudah dioverload.
Dot Products
Dot product adalah perkalian antara 2 vector yang menghasilkan sebuah scalar.
rumus di atas tidak menggambarkan suatu arti geometri khusus. Akan tetapi dengan menggunakan
hukum kosinus, kita bisa mendapatkan :
u•v
cos θ =
u v
Jadi dengan mengetahui dot product dan magnitude dari tiap vector, kita bisa menentukan sudut
antara dua buah vector. Jika u dan v adalah unit vector ( panjang u dan v = 1 ), maka u ● v adalah
cosinus dari dua vector tersebut.
• Jika u ● v = 0 maka u ⊥ v.
• Jika u ● v > 0 maka sudut antara dua vector tersebut < 90°
• Jika u ● v < 0 maka sudut antara dua vector tersebut > 90°
Dalam library D3DX, kita bisa menggunakan fungsi D3DXVec3Dot untuk mencari dot product antara
dua vector.
Cross Products
Selain dot product, ada satu lagi jenis perkalian antara 2 vector yaitu cross product. Tidak seperti
dot product yang menghasilkan scalar, cross product menghasilkan vector lain yang orthogonal
terhadap 2 vector yang lain.
20
Penyelesaian:
jx = (0(0) – 1(0)) = 0
jy = (1(1) – 0(0)) = 1
jz = (0(0) – 0(1)) = 0
j = ( 0, 1, 0 ).
dari dot product di atas dpt diketahui bahwasanya vector j tegak lurus i dan j tegak lurus k.
Menggunakan D3DX kita bisa menggunakan fungsi D3DXVec3Cross untuk mencari cross product
antara 2 vector.
D3DXVECTOR3 *D3DXVec3Cross(
D3DXVECTOR3* pOut, // Result.
CONST D3DXVECTOR3* pV1, // Left sided operand.
CONST D3DXVECTOR3* pV2 // Right sided operand.
);
D3DXVECTOR3 crosprod ;
v
u
Matrix
Selain berurusan dengan vector, kita juga akan berurusan dengan matrix. Matrix m×n adalah suatu
baris bilangan berupa kotak dengan m baris dan n kolom. Kita menyatakan matrix dengan
menggunakan dua buah index (subscript). Angka pertama menyatakan baris dan angka kedua
menyatakan kolom. Contoh Matrix M 3×3, A 2×3 dan B 4×2 seperti di bawah ini :
⎛ b11 b12 ⎞
⎛ m11 m12 m13 ⎞ ⎜ ⎟
⎜ ⎟ ⎛a a12 a13 ⎞ ⎜b b22 ⎟
M = ⎜ m21 m22 m23 ⎟ A = ⎜⎜ 11 ⎟ B = ⎜ 21
⎝ a 21 a 22 a 23 ⎟⎠ b b32 ⎟
⎜m
⎝ 31 m32 m33 ⎟⎠ ⎜ 31 ⎟
⎜b
⎝ 41 b42 ⎟⎠
21
Kita biasanya menggunakan huruf besar untuk menyatakan matrix.
Kadang-kadang matrix hanya berisi satu baris atau satu kolom. Untuk matrix ini kita menyebutkan
row vector dan column vector. Contoh row vector dan column vector sebagai berikut :
⎛ u11 ⎞
⎜ ⎟
v = (v11 v12 v13 v14 ) dan u = ⎜ u 21 ⎟
⎜u ⎟
⎝ 31 ⎠
⎛ 1 5 ⎞ ⎛ 6 − 1⎞ ⎛ 1 5 ⎞ ⎛ − 2 0 −1 3⎞
A = ⎜⎜ ⎟⎟ B = ⎜⎜ ⎟⎟ C = ⎜⎜ ⎟⎟ D = ⎜⎜ ⎟⎟
⎝ − 3 − 2⎠ ⎝− 2 3 ⎠ ⎝ − 3 − 2⎠ ⎝ 0 − 3 2 2⎠
• Dua matrix adalah sama jika mempunyai dimensi yang sama dan semua komponen matrix-
matrix tersebut sama. Contoh A = C karena dimensinya sama dan isinya juga sama.
Sedangkan A ≠ B dan C ≠ D karena dimensinya serta isinya tidak sama.
• Kita bisa mengalikan matrix dengan skalar contoh kitat kalikan matrix D dengan skalar k :
• Untuk menambahkan dua matrix, keduanya harus mempunyai dimensi yang sama.
⎛ 1 5 ⎞ ⎛ 6 − 1⎞ ⎛ 1 + 6 5 −1 ⎞ ⎛ 7 4⎞
A + B = ⎜⎜ ⎟⎟ + ⎜⎜ ⎟⎟ = ⎜⎜ ⎟⎟ = ⎜⎜ ⎟⎟
⎝ − 3 − 2 ⎠ ⎝ − 2 3 ⎠ ⎝ − 3 + (−2) − 2 + 3 ⎠ ⎝ − 5 1 ⎠
• Sedangkan untuk mengurangkan dua matrix, operasi yang dilakukan adalah spt berikut :
⎛ 1 5 ⎞ ⎛ 6 − 1⎞ ⎛ 1 5 ⎞ ⎛− 6 1 ⎞ ⎛ 1 − 6 5 +1 ⎞ ⎛− 5 6 ⎞
A − B = ⎜⎜ ⎟⎟ − ⎜⎜ ⎟⎟ = ⎜⎜ ⎟⎟ + ⎜⎜ ⎟⎟ = ⎜⎜ ⎟⎟ = ⎜⎜ ⎟⎟
⎝ − 3 − 2 ⎠ ⎝ − 2 3 ⎠ ⎝ − 3 − 2 ⎠ ⎝ 2 − 3⎠ ⎝ − 3 + 2 − 2 − 3⎠ ⎝ − 1 − 5 ⎠
Perkalian Matrix
Perkalian matrix merupakan operasi yang penting dalam 3D computer graphics. Dengan
menggunakan perkalian matrix, kita bisa mentransformasi dan mengkombinasikan beberapa
transformasi sekaligus.
Untuk membuat matrix AB, maka jumlah kolom matrix A harus sama dengan jumlah baris Matrix B.
Contoh berikut matrix A berukuran 2x2 dan matrix B berukuran 2x3.
22
Produk AB terdefinisi karena jumlah kolom matrix A sama dengan jumlah baris matrix B. Akan
tetapi, produk BA tidak terdefinisi karena jumlah kolom B tidak sama dengan jumlah baris A.
Dengan begitu hukum komutatif tidak berlaku untuk perkalian matrix. AB ≠ BA.
Jika suatu matrix A berukuran m × n dan matrix B berukuran n × p maka hasil perkalian AB adalah
matrix C dengan ukuran m × p dengan cij adalah dot product antara row vector A pada baris ke i
dengan column vector B pada kolom ke j.
cij = ai ● bj
Sebagai contoh. Mari kita kalikan 2 matrix berikut :
⎛ − 1 2 ⎞⎛ 1 2 ⎞ ⎛ a1 • b1 a1 • b2 ⎞ ⎛ 7 − 6 ⎞
AB = ⎜⎜ ⎟⎟⎜⎜ ⎟⎟ = ⎜⎜ ⎟=⎜ ⎟
⎝ 0 1 ⎠⎝ 4 − 2 ⎠ ⎝ a 2 • b1 a 2 • b2 ⎟⎠ ⎜⎝ 4 − 2 ⎟⎠
Matrix Identitas
Ada sebuah matrix khusus yang disebut sebagai Matrix Identitas (Identity Matrix). Matrix Identitas
adalah sebuah matrix yang berisi 0 di semua elemennya kecuali diagonalnya yang berisi 1. contoh
berikut adalah matrix identitas 2×2, 3×3, dan 4×4
⎛1 0 0 0⎞
⎛1 0 0⎞ ⎜ ⎟
⎛1 0⎞ ⎜ ⎟ ⎜0 1 0 0⎟
⎜⎜ ⎟⎟ ⎜0 1 0⎟ ⎜0
⎝0 1⎠ ⎜0 0 1⎟ 0 1 0⎟
⎝ ⎠ ⎜ ⎟
⎜0 0 0 1 ⎟⎠
⎝
Hukum MATRIX IDENTITAS :
IM = MI = M
I = matrix identitas.
Dengan begitu, mengalikan suatu matrix dengan matrix identitas, tidak mengganti nilai matrix
tersebut.
Inverse
Dalam aritmetika matrix, tidak ada analogi untuk pembagian matrix. Akan tetapi ada perkalian
inverse. Hal-hal berikut yang perlu diperhatikan dalam inverse matrix :
Matrix inverse sangat berguna untuk mencari matrix lain dalam persamaan matrix. Sebagai contoh,
sebuah persamaan p' = pR dan kita sudah mengetahui p' dan R, yang kita cari adalah p. Hal yang
23
pertama kali dilakukan adalah mencari R-1. Setelah R-1 diketahui, maka dengan mudah kita bisa
mencari p.
p'R-1= p (RR-1)
p'R-1 = pI
p'R-1 = p
(AB)-1 = B-1A-1
Dengan asumsi bahwa A dan B mempunyai inverse dan dimensi A dan B adalah sama.
Transpose Matrix
Matrix mempunyai transpose dengan menukar baris menjadi kolom. Dengan begitu, transpose dari
matrix m × n adalah matrix n × m. Kita nyatakan matrix transpose dari M dengan MT.
⎛a b c⎞
⎛−1 8 9 ⎞ ⎜ ⎟
A = ⎜⎜ ⎟⎟ B = ⎜d e f⎟
⎝ 3 2 − 5⎠ ⎜g h i ⎟⎠
⎝
⎛ − 1 3⎞ ⎛a d g⎞
⎜ ⎟ ⎜ ⎟
A = ⎜ 2 8⎟
T
B = ⎜b
T
e h⎟
⎜ − 5 9⎟ ⎜c f i ⎟⎠
⎝ ⎠ ⎝
Matrix D3DX
Ketika memrogram Direct3D, yang digunakan biasanya adalah matrix 4×4 dan row vector 1×4.
Dengan menggunakan 2 matrix tersebut maka perkalian berikut terdefinisi :
• Perkalian Vector dan Matrix. Jika v adalah row vector 1×4 dan T adalah matrix 4×4 maka
product vT terdefinisi yang menghasilkan row vector 1×4.
• Perkalian Matrix dan Matrix. Jika T adalah matrix 4×4 dan R adalah matrix 4×4 maka
product TR dan RT terdefinisi yang menghasilkan matrix 4×4. Karena perkalian matrix tidak
komutatif maka TR ≠ RT.
Untuk menyatakan 1×4 row vector dengan D3DX, kita biasa menggunakan class D3DXVECTOR3 dan
D3DXVECTOR4, tentu saja D3DXVECTOR3 hanya mempunyai 3 komponen, bukan 4. Akan tetapi,
komponen keempat berisi satu atau 0.
Untuk menyatakan matrix 4×4 class yang digunakan D3DXMATRIX didefinisikan sbb :
24
D3DXMATRIX( ) { } ;
D3DXMATRIX( CONST FLOAT* ) ;
D3DXMATRIX( CONST D3DMATRIX& ) ;
D3DXMATRIX( FLOAT _11, FLOAT _12, FLOAT _13, FLOAT _14,
FLOAT _21, FLOAT _22, FLOAT _23, FLOAT _24,
FLOAT _31, FLOAT _32, FLOAT _33, FLOAT _34,
FLOAT _41, FLOAT _42, FLOAT _43, FLOAT _44 ) ;
// access grants
FLOAT& operator ( ) ( UINT Row, UINT Col ) ;
FLOAT operator ( ) ( UINT Row, UINT Col ) const ;
// casting operators
operator FLOAT* ( );
operator CONST FLOAT* ( ) const;
// assignment operators
D3DXMATRIX& operator *= ( CONST D3DXMATRIX& ) ;
D3DXMATRIX& operator += ( CONST D3DXMATRIX& ) ;
D3DXMATRIX& operator -= ( CONST D3DXMATRIX& ) ;
D3DXMATRIX& operator *= ( FLOAT ) ;
D3DXMATRIX& operator /= ( FLOAT ) ;
// unary operators
D3DXMATRIX operator + ( ) const ;
D3DXMATRIX operator - ( ) const ;
// binary operators
D3DXMATRIX operator * ( CONST D3DXMATRIX& ) const ;
D3DXMATRIX operator + ( CONST D3DXMATRIX& ) const ;
D3DXMATRIX operator - ( CONST D3DXMATRIX& ) const ;
D3DXMATRIX operator * ( FLOAT ) const ;
D3DXMATRIX operator / ( FLOAT ) const ;
} D3DXMATRIX, *LPD3DXMATRIX;
float m[ 4 ][ 4 ] ;
}
} D3DXMATRIX;
Bisa dilihat bahwa D3DXMATRIX mempunyai banyak sekali operator yang berguna, seperti testing
untuk kesamaan, penambahan dan pengurangan matrix, casting, dan lebih penting lagi perkalian
25
dua D3DXMATRIX atau lebih. Karena perkalian matrix sangat penting, saya akan memberikan contoh
penggunaan D3DXMATRIX untuk perkalian.
D3DXMATRIX A( . . . ) ; // inisialisasi A
D3DXMATRIX B( . . . ) ; // inisialisasi B
D3DXMATRIX C = A * B ; // C = A B
Operator penting lainnya, adalah operator kurung ( ... ) yang sangat praktis digunakan untuk
mengeset entry dari sebuah matrix.
D3DXMATRIX mat ;
mat(0, 0) = 4.0f ; // mengeset entry ij = 11 ke 4.0f
Library D3DX juga menyediakan D3DXMATRIX untuk membuat matrix identitas, mencari transpose
matrix dan mencari inverse D3DXMATRIX.
D3DXMATRIX *D3DXMatrixIdentity(
D3DXMATRIX *pOut
) ; // matrix diset menjadi matrix identitas
Penggunaannya :
Mencari transpose
D3DXMATRIX *D3DXMatrixTranspose(
D3DXMATRIX *pOut,
D3DXMATRIX *pM
) ;
Penggunaannya :
Mencari inverse :
D3DXMATRIX *D3DXMatrixInverse(
D3DXMATRIX *pOut, // hasil
FLOAT *pDeterminant, // determinan, jika tidak ada, NULL
D3DXMATRIX *pM // matrix yg dicari inversenya
) ;
Penggunaannya :
26
Transformasi Dasar
Ketika menggunakan Direct3D untuk matrix T 4 × 4 untuk menyatakan transformasi dan row vector v
1 × 4 untuk menyatakan koordinat dan juga vector. vT adalah vektor hasil transformasi v terhadap T
yang bisa juga dinyatakan dengan v'. Jadi misalkan T menyatakan matrix translasi terhadap sumbu X
sebanyak 7 unit untuk vektor v = [ 3, -4, 2, 1 ], maka vT = v' = [ 10, -4, 2, 1 ].
Kenapa kita menggunakan matrix 4 × 4 untuk ruang 3 dimensi? Karena matrix dengan ukuran 4 × 4
lah yang bisa menyatakan seluruh transformasi yang mungkin dilakukan dalam ruangan 3 dimensi.
Mungkin matrix 3 × 3 terasa lebih sesuai untuk ruang 3 dimensi. Akan tetapi, ada beberapa
transformasi yang tidak bisa dinyatakan dalam matrix 3 × 3 seperti translasi, proyeksi perspective,
dan refleksi. Kita bekerja dengan product vector dan matrix, sehingga gerak kita terbatas pada
perkalian matrix dan vector untuk menyatakan transformasi, menggunakan matrix 4 × 4 membuat
kita menjadi mungkin untuk menyatakan lebih banyak transformasi.Karena kita menggunakan matrix
4 × 4, maka kita pun harus menggunakan row vector 1 × 4 supaya perkalian antara row vector dan
matrix terdefinisi.
Kemudian apa yang harus kita lakukan dengan elemen keempat? Untuk point kita mengeset elemen
keempat dengan 1, sehingga dapat dilakukan translasi. Akan tetapi untuk vector kita mengeset
elemen keempat dengan 0 karena posisi bukanlah properti vector, sehingga translasi tidak
mempunyai arti khusus pada vector.
Contoh :
Untuk menyatakan posisi p = (px, py, pz), kita nyatakan p dalam row vector [ px, py, pz, 1 ].
Sedangkan untuk menyatakan vector v = (vx, vy, vz), kita nyatakan v dalam row vector [ vx, vy, vz, 0 ].
Vector 4D seperti di atas disebut homogenous vector. Homogenous vector bisa digunakan untuk
menyatakan point dan vector. Untuk selanjutnya istilah vector dapat berupa point maupun vector
itu sendiri.
⎛1 0 0 0⎞
⎜ ⎟
⎜0 1 0 0⎟
(
p = px py pz 1)⎜
0 0 1 1⎟
= (px py pz p z ) dengan pz ≠ 0 dan pz ≠ 1
⎜ ⎟
⎜0 0 0 0 ⎟⎠
⎝
terlihat bahwa w = pz. Ketika kita melihat vector di mana w≠0 dan w≠1, maka kita namai vector
tersebut dalam homogenous space yang berbeda dengan vector dalam 3-D space. Jika kita
menemukan homogenous space kemudian ingin mengubahnya ke 3-D space, yang kita lakukan adalah
membagi tiap elemen dengan nilai w tersebut. Misalkan kita punya vector u = ( x, y, z, w) dan ingin
mengubahnya menjadi 3-D space, maka masing-masing elemen kita bagi dengan w.
G ⎛x y z w⎞ ⎛ x y z ⎞
u = (x y z w) = ⎜ ⎟=⎜ 1⎟ , sehingga untuk kasus p diatas bisa
⎝w w w w⎠ ⎝ w w w ⎠
kita dapatkan
27
⎛p py ⎞ ⎛ px py ⎞
p' = ( p x p z ) = ⎜⎜ x
pz pz
py pz ⎟⎟ = ⎜⎜ 1 1⎟⎟
⎝ pz pz pz pz ⎠ ⎝ pz pz ⎠
Matrix Translasi
Perhatikan gambar berikut ini :
(-2, 5)
(-5, 2) Translasi
(5, -1)
(2, -4)
Gambar di atas menunjukkan perpindahan (translasi) sebanyak 10 unit di sumbu x dan -6 unit di
sumbu y.
Kita bisa mentranslasi suatu vector (x, y, z, 1) sebanyak dx unit sepanjang sumbu x, dy unit
sepanjang sumbu y dan dz unit sepanjang sumbu z dengan mengalikan vector tersebut dengan
matrix berikut ini :
⎛1 0 0 0⎞
⎜ ⎟
⎜0 1 0 0⎟
T (d ) = ⎜
0 0 1 0⎟
⎜ ⎟
⎜d
⎝ x dy dz 1 ⎟⎠
D3DXMATRIX *D3DXMatrixTranslation (
D3DXMATRIX *pOut, // output
FLOAT x, // jarak translasi di sumbu x
FLOAT y, // jarak translasi di sumbu y
FLOAT z // jarak translasi di sumbu z
);
28
Untuk mencari Inverse dari matrix translasi tersebut cukup mudah yaitu tinggal menegatifkan
elemen translasi matrix tersebut :
⎛ 1 0 0 0⎞
⎜ ⎟
⎜ 0 1 0 0⎟
T −1 (d ) = ⎜
0 0 1 0⎟
⎜ ⎟
⎜− d
⎝ x − dy − dz 1 ⎟⎠
Matrix Rotasi
Rotasi
Kita bisa merotasi vektor sebanyak θ radian dengan sumbu x, y, dan z menggunakan matrix berikut.
Sudut positif adalah searah dengan jarum jam.
⎛1 0 0 0⎞
⎜ ⎟
⎜ 0 cos θ sin θ 0⎟
rotX (θ ) = ⎜
0 − sin θ cos θ 0⎟
⎜ ⎟
⎜0
⎝ 0 0 1 ⎟⎠
D3DXMATRIX *D3DXMatrixRotationX(
D3DXMATRIX* pOut, // Result.
FLOAT Angle // Sudut rotasi dalam radian
);
⎛ cos θ 0 − sin θ 0⎞
⎜ ⎟
⎜ 0 1 0 0⎟
rotY (θ ) = ⎜
sin θ 0 cos θ 0⎟
⎜ ⎟
⎜ 0
⎝ 0 0 1 ⎟⎠
29
D3DXMATRIX *D3DXMatrixRotationY(
D3DXMATRIX* pOut, // Result.
FLOAT Angle // Sudut rotasi dalam radian
);
⎛ cos θ sin θ 0 0⎞
⎜ ⎟
⎜ − sin θ cos θ 0 0⎟
rotZ (θ ) = ⎜
0 0 1 0⎟
⎜ ⎟
⎜ 0
⎝ 0 0 1 ⎟⎠
D3DXMATRIX *D3DXMatrixRotationZ(
D3DXMATRIX* pOut, // Result.
FLOAT Angle // Sudut rotasi dalam radian
);
Inverse dari matrix rotasi adalah transpose dari matrix tersebut. R-1 = RT.
Matrix Scaling
Scale
Gambar 1.12. Scaling 1,5 kali di sumbu X dan 2,5 kali di sumbu Y
Jika kita ingin men-scale suatu vector dengan faktor kx untuk sumbu x, ky untuk sumbu y, dan kz
untuk sumbu z dengan matrix berikut :
⎛kx 0 0 0⎞
⎜ ⎟
⎜0 ky 0 0⎟
S (k ) = ⎜
0 0 kz 0⎟
⎜ ⎟
⎜0
⎝ 0 0 1 ⎟⎠
30
D3DXMATRIX *D3DXMatrixScaling(
D3DXMATRIX* pOut, // Result.
FLOAT kx, // Faktor perkalian untuk elemen x.
FLOAT ky, // Faktor perkalian untuk elemen y.
FLOAT kz // Faktor perkalian untuk elemen z.
);
Inverse dari scaling matrix adalah matrix yang sama dengan faktor skala 1/k
⎛ 1 ⎞
⎜ 0 0 0⎟
⎜ kx ⎟
⎜ 1 ⎟
−1 ⎛1⎞ ⎜ 0 0 0⎟
S (k ) = S ⎜ ⎟ = ky
⎝k⎠ ⎜ ⎟
1
⎜ 0 0 0⎟
⎜ kz ⎟
⎜ 0
⎝ 0 0 1 ⎟⎠
Kombinasi Transformasi
Sering kita mengkombinasikan beberapa transformasi, misalkan, kita menscale vector,
merotasikannya kemudian mentranslasinya ke posisi yang kita inginkan.
Contoh : Scale vector u = [ 3, 6, 0, 1 ] sepertiga pada semua sumbu, rotasikan sebanyak π/3
dengan sumbu y sebagai poros kemudian translasikan sebanyak 2 unit di sumbu x, -1 di sumbu y, dan
4 unit di sumbu z.
⎛1 ⎞
⎜ 0 0 0⎟
⎜3 ⎟ ⎛ 0.5 0 − 0.866 0⎞
1 ⎜ ⎟
⎛1 1 1⎞ ⎜0 0 0⎟ ⎛π ⎞ ⎜ 0 1 0 0⎟
S⎜ , , ⎟ = ⎜ 3 ⎟ Ry ⎜ ⎟ = ⎜
⎝ 3 3 3⎠ ⎜ 1 ⎟ ⎝ 3 ⎠ ⎜ 0.866 0 0.5 0⎟
⎟
⎜0 0
3
0⎟ ⎜ 0
⎝ 0 0 1 ⎟⎠
⎜ ⎟
⎝0 0 0 1⎠
Matrix translasi
⎛1 0 0 0⎞
⎜ ⎟
⎜0 1 0 0⎟
T (2,−1,4 ) = ⎜
0 0 1 0⎟
⎜ ⎟
⎜2 −1
⎝ 4 1 ⎟⎠
31
G G
u ' = u SR y T
Jadi pertama kali kita kombinasikan matrix SRyT sbb :
⎛1 ⎞
⎜ 0 0 0⎟
⎜3 ⎟⎛⎜ 0.5 0 − 0.866 0 ⎞⎛ 1 0 00⎞
1 ⎟⎜ ⎟
⎜0 0 0 ⎟⎜ 0 1 0 0 ⎟⎜ 0 1 00⎟
SR y T = ⎜ 3 ⎟⎜
⎜ 1 ⎟⎜ 0.866 0 0 .5 0 ⎟⎜ 0 0 1 0⎟
⎟⎜ ⎟
⎜0 0 0 ⎟⎜ 0 0 0 1 ⎟⎠⎜⎝ 2 − 1 4 1 ⎟⎠
⎜ 3 ⎟⎝
⎝0 0 0 1⎠
⎛ 0.1665 0 − 0.288 0 ⎞⎛ 1 0 0 0 ⎞ ⎛ 0.1665 0 − 0.288 0⎞
⎜ ⎟⎜ ⎟ ⎜ ⎟
⎜ 0 0.333 0 0 ⎟⎜ 0 1 0 0⎟ ⎜ 0 0.333 0 0⎟
=⎜ =
0.288 0 0.1665 0 ⎟⎜ 0 0 1 0 ⎟ ⎜ 0.288 0 0.1665 0⎟
⎜ ⎟⎜ ⎟ ⎜ ⎟
⎜ 0
⎝ 0 0 1 ⎟⎠⎜⎝ 2 − 1 4 ⎟
1⎠ ⎝ 2⎜ −1 4 1 ⎟⎠
⎛ 0.1665 0 − 0.288 0⎞
⎜ ⎟
G G ⎜ 0 0.333 0 0⎟
u ' = u SR y T = (3 6 0 1)⎜ = (2.5 1 3,136 1)
0.288 0 0.1665 0⎟
⎜ ⎟
⎜ 2
⎝ −1 4 1 ⎟⎠
Mungkin perkalian di atas sedikit berbeda dengan apa yang diajarkan di sekolah atau kampus. Hal ini
terjadi karena matrix dan vector yang kita gunakan. Dalam matematika di buku-buku aljabar linear,
transformasi dikalikan dengan urutan terbalik. u' = TRySu. Hal ini terjadi karena pada buku
matematika yang digunakan adalah column vector, sedangkan kita menggunakan row vector. Jika
Anda pernah menggunakan OpenGL, maka perhitungan matrix dan vector dalam OpenGL sama
dengan buku-buku aljabar linear karena memakai column vector.
D3DXVECTOR3 *D3DXVec3TransformCoord(
D3DXVECTOR3* pOut, // Hasil.
CONST D3DXVECTOR3* pV, // Point yg ditransformasi.
CONST D3DXMATRIX* pM // Matrix Transformasi.
);
32
CONST D3DXVECTOR3 *pV, // vector yg ditransformasi.
CONST D3DXMATRIX *pM // matrix transformasi.
);
Plane (Bidang)
Plane atau bidang bisa dinyatakan dalam vector n dan sebuah point dalam plane p0. Vector n adalah
perpendicular (tegak lurus) dengan plane dan disebut sebagai normal vector.
p0
Gambar 1.13. Plane dinyatakan dengan vector normal n dan point p0.
Dari gambar 1.14 kita dapat mengetahui bahwa sebuah plane memuat semua titik p yang memenuhi
persamaan.
(p-p0)
p0 p
Gambar 1.14. jika p0 adalah point dalam plane dan p juga merupakan point dalam plane jika vector
yang terbentuk dari p0 dan p adalah orthogonal terhadap vector normal plane (n). Dengan begitu :
n ● (p-p0) = 0
Ketika menyatakan suatu plane, biasanya normal n diketahui dan p0 adalah konstan. Maka
persamaan di atas bisa juga ditulis :
n ● p + d = 0, dengan d = -n ● p0
Catatan Khusus :
Jika normal vector n adalah unit vector, d = -n ● p0 menghasilkan jarak terdekat dari titik
origin ke plane.
33
D3DXPLANE
Ketika menyatakan suatu plane terhadap code, normal vector n dan konstanta d sudah cukup.
Sangat praktis jika kita berpikir itu adalah vector 4D ( n, d ). D3DX Library menggunakan structure
di bawah ini untuk menyatakan plane :
// casting
operator FLOAT* ();
operator CONST FLOAT* () const;
// unary operators
D3DXPLANE operator + () const;
D3DXPLANE operator - () const;
// binary operators
BOOL operator == ( CONST D3DXPLANE& ) const;
BOOL operator != ( CONST D3DXPLANE& ) const;
#endif //__cplusplus
FLOAT a, b, c, d;
} D3DXPLANE, *LPD3DXPLANE;
Dengan a, b, dan c adalah elemen dari vector normal n dan d adalah konstanta d dari persamaan di
atas.
• Jika n ● p + d > 0 maka p berada di depan plane, dan di plane positive space.
• Jika n ● p + d < 0 maka p berada di belakang plane, dan di plane negative space.
Catatan Khusus :
Jika plane normal vector n adalah unit vector, maka n ● p + d menghasilkan jarak terdekat
antara plane ke point.
Fungsi D3DX di bawah ini mencari n ● p + d untuk suatu plane dan point.
FLOAT D3DXPlaneDotCoord(
CONST D3DXPLANE *pP, // plane.
CONST D3DXVECTOR3 *pV // point.
);
34
D3DXVECTOR3 v( 3.0f, 5.0f, 2.0f );
Catatan Khusus :
Saya menggunakan istilah mendekati untuk mengakomodasi kesalahan dalam presisi floating
point. Fungsi hampir sama D3DXPlaneDotCoord adalah D3DXPlaneDot dan
D3DXPlaneDotNormal, lihat dokumentasi DirectX untuk lebih jelas.
Konstruksi
Selain dengan menentukan normal(n) dan jarak (d) kita bisa mencari komponen tersebut dalam 2
cara. Dengan normal n dan sebuah point p0, kita bisa mencari d.
n ● p0 + d = 0
n ● p0 = -d
-n ● p0 = d
Library D3DX menyediakan fungsi di bawah ini untuk melakukan kalkulasi tersebut :
D3DXPLANE *D3DXPlaneFromPointNormal(
D3DXPLANE* pOut, // Hasil.
CONST D3DXVECTOR3* pPoint, // Point di plane.
CONST D3DXVECTOR3* pNormal // Normal dari plane.
);
Cara kedua adalah dengan mencari persamaan plane dari tiga titik kita bisa mencari 2 vector dalam
plane, p0, p1, dan p2.
u = p1 – p0
v = p2 – p0
Dari dua vector tersebut kita bisa mencari normal dari plane dengan mencari cross product dari dua
vector tersebut :
n=u×v
Fungsi dari library D3DX untuk mencari plane dari tiga point sebagai berikut:
D3DXPLANE *D3DXPlaneFromPoints(
D3DXPLANE* pOut, // Hasil.
CONST D3DXVECTOR3* pV1, // Point 1 di plane.
CONST D3DXVECTOR3* pV2, // Point 2 di plane.
CONST D3DXVECTOR3* pV3 // Point 3 di plane.
35
);
Me-normalize plane
Terkadang kita mempunyai plane dan ingin menormalkan plane tersebut. Mungkin Anda berpikir
bahwa kita tinggal menormalkan normal vector seperti vector lain. Akan tetapi mari kita lihat
persamaan plane berikut : d = -n ● p0 di n ● p + d = 0. Terlihat bahwa panjang normal vector
mempengaruhi constant d. Dengan begitu jika kita menormalkan normal vector, maka kita juga
harus menghitung d.
G
d n
G = − G • p0
n n
Dengan begitu kita mempunyai rumus berikut untuk menormalkan normal vector dari plane (n, d)
⎛ G
1 G n d ⎞⎟
G (n , d ) = ⎜⎜ G , G
n ⎝ n n ⎟⎠
Kita bisa menggunakan fungsi D3DX berikut ini untuk menormalkan sebuah plane:
D3DXPLANE *D3DXPlaneNormalize(
D3DXPLANE *pOut, // Plane yang ternormalisasi
CONST D3DXPLANE *pP // Input plane.
);
Transformasi Plane
Plan dapat ditransformasikan dengan menganggap plane (n, d) sebagai vector 4-D dan
mengalikannya dengan inverse-transpose dari matrix transformasi. Normal dari plane harus
dinormalkan dahulu sebelum ditransformasi.
D3DXPLANE *D3DXPlaneTransform(
D3DXPLANE *pOut, // Hasil
CONST D3DXPLANE *pP, // Input plane.
CONST D3DXMATRIX *pM // Matrix Transformasi.
);
Sample code :
36
Point terdekat dalam plane terhadap point lain
Anggap kita mempunyai point p di suatu ruang dan akan mencari point q di plane (n, d) dengan
asumsi normal vector adalah unit vector.
(n, d)
q n
-kn kn
Gambar 1.15. point dalam plane (q) yg paling dekat dengan point lain (p)
Dari gambar 1.15 kita dapatkan q = p + (-kn). Dengan k adalah jarak terdekat antara p dengan plane
yang merupakan jarak terdekat antara p dan q. Anggap normal n adalah unit vector, maka n ● p + d
memberikan jarak terdekat dari plane ke point p.
Rays
Bayangkan seorang player dalam game mengarahkan pistolnya dan menembakkan ke musuh.
Bagaimana kita menentukan apakah pelurunya mengenai target? Salah satu caranya adalah dengan
menganggap peluru tersebut adalah ray dan musuh dengan bounding sphere atau bounding box.
Bounding box/sphere adalah box/sphere terkecil yg bisa melingkupi suatu object. Kemudian kita
bisa menentukan apakah ray akan apakah mengenai bounding box/sphere tersebut dan dimana
tempat kenanya.
Pengertian Ray
Ray bisa dinyatakan dengan sebuah origin (titik asal) dan direction(arah). Persamaan ray adalah
sebagai berikut :
p(t) = p0 + tu
37
p0+3u
p0+2u
p0+u
p0
Gambar 1.16. Sebuah ray dinyatakan dengan sebuah origin (p0) dan vector arah (u) kita bisa
membuat point-point sepanjang ray dengan mengalikan vector arah dengan bilangan k yang lebih
besar atau sama dengan 0.
p0 adalah titik origin dari ray dan u adalah arah dari ray, dan t adalah parameternya. Dengan
memberikan nilai yang berbeda terhadap t kita bisa mencari point-point sepanjang ray. Parameter
t harus berada dalam interval [0, ∞). Nilai kurang dari 0 akan membuat point di belakang ray.
Sehingga jika kita biarkan t ∈ (-∞,∞) kita mempunyai sebuah garis dalam ruang 3-D.
38
Jika t berada pada interval [0, ∞) maka kita bisa mencari titik perpotongan antara ray dan plane
dengan memasukkan parameter t ke dalam persamaan ray
G G
⎛ − d − (n • p 0 ) ⎞ ⎛ − d − (n • p0 ) ⎞ G
p(t ) = p⎜ G G ⎟ = p0 + ⎜ G G ⎟u
⎝ n •u ⎠ ⎝ n •u ⎠
39
Program DirectX Pertama
Setelah membahas matematika, saatnya membuat aplikasi sebenarnya. Saya akan mengajarkan
langkah demi langkah dalam membuat aplikasi DirectX. Beberapa sample dari DirectX bergantung
pada DXUT atau DirectX Sample Framework. Saya tidak akan menggunakan framework tersebut,
tetapi akan memulai semuanya dari awal.
Karena DirectX berjalan di platform Microsoft Windows, saya akan mengajarkan dasar pemrograman
aplikasi Windows terlebih dahulu sebelum masuk ke dalam DirectX.
#include <cstdio>
int main( int argc, char ** argv )
{
printf( "Hilo Wurld! LynxLuna Ganteng!" ) ;
return 0 ;
}
Program di atas akan menampilkan tulisan Hilo Wurld! LynxLuna Ganteng! di layar. Struktur aplikasi
console cukup sederhana, minimal hanya satu fungsi bernama main( ). Secara global, struktur
aplikasi Windows hampir sama dengan aplikasi console. Perbedaannya, aplikasi Windows tidak
menggunakan fungsi main( ) sebagai entry point, tetapi WinMain( ) dengan parameter sebagai
berikut :
Sebelum membahas arti dari parameter fungsi WinMain( ) di atas. Pertama kali, kita buat project
Windows dahulu. Pertama kali buka Visual Studio. Saya menggunakan edisi professional dari
Microsoft Visual Studio.
40
• Pilih Visual C++ > Win32
• Klik OK
Setelah diklik OK, selanjutnya adalah Layar Win32 Application Wizard. Untuk layar ini klik pada
Application Settings dan kasih tanda check (centang) pada pilihan Empty Project kemudian click
Finish.
41
Setelah diclick finish maka akan tercipta sebuah project kosong dengan tiga folder yaitu Header
Files, Resource Files, dan Source Files seperti gambar di bawah ini :
Untuk menambahkan file, klik kanan pada project, kemudian Add New Item.
Setelah ditambah New Item akan keluar layar Add New Item. Pilih Code > C++ File (.cpp) kemudian
beri nama untuk file tersebut. Untuk kali ini saya memberi nama WinMain.cpp.
42
Gambar 2.5 Layar Add New Item
Setelah ditambah New Item akan keluar layar Add New Item. Pilih C++ File (.cpp) kemudian beri
nama untuk file tersebut. Untuk kali ini saya memberi nama WinMain.cpp.
Microsoft Visual Studio 2005 secara default mengeset aplikasi ke modus Unicode. Untuk saat ini,
kita tidak membutuhkan Unicode, oleh karena itu, kita ubah setting aplikasi supaya tidak Unicode.
• Klik Properties
• Cari Item Character Set dan set ke modus Not Set untuk mematikan support Unicode.
43
Setelah itu barulah kita menulis program HiloWorld untuk Window. Program kita amat sederhana,
hanya menampilkan Message Box ke layar.
#include <Windows.h>
return 0 ;
}
Tulis code di atas pada window code. Untuk mengcompilenya menjadi program, klik Build > Build
HiloWorld. Untuk mendebugnya, klik Debug > Start Debugging, maka akan keluar Message Box
seperti di bawah ini :
#include <Windows.h>
Ini adalah header yang harus dimasukkan ke dalam setiap aplikasi Windows.
Fungsi yang merupakan entry point program. Fungsi tersebut memiliki kembalian bertipe INT
(integer) dengan WINAPI sebagai calling conventionnya. Calling convention adalah suatu perintah
kepada compiler bagaimana program memasukkan parameter ke dalam stack.
Dalam program di atas kita menemui tipe data baru. Data tersebut akan diterangkan dalam tabel
berikut :
Merupakan tipe data sebagai handle dari instance aplikasi. Instance adalah
jumlah aplikasi yang dijalankan dari file yang sama. Misal kita punya file
HINSTANCE HiloWorld.exe dijalankan sekali maka aplikasi mempunyai 1 instance. Ketika
dijalankan, maka hInstance ini akan berisi suatu nilai yang merupakan
handle dari aplikasi tersebut.
LPSTR Merupakan pointer ke string ANSI, sama dengan char * pada C++ biasa.
44
Arti dari parameter WinMain adalah sebagai berikut :
Variabel ini merupakan peninggalan dari Windows 3.1. Pada Windows 3.1,
hPrevInstance variabel ini berisi instance sebelumnya dari aplikasi. Untuk Windows 95 ke
atas, variabel ini selalu akan berisi NULL.
lpCmdLine Variabel ini berisi string command line dari program yang dijalankan.
Baris selanjutnya :
Baris di atas akan menampilkan Message Box ke layar bertuliskan Hilo Wurld! LynxLuna Ganteng
dengan judul window Hilo dengan satu tombol OK.
Nama
Fungsi
Variabel
lpCaption Ini merupakan teks yang ditampilkan dalam title bar message box
Ini merupakan variabel bertipe UINT (unsigned int) yang menunjukkan tombol
uType serta icon yang ditampilkan dalam message box, untuk nilai yang bisa
digunakan silahkan membaca dokumentasi MessageBox di MSDN.
Tentunya aplikasi kita lebih kompleks daripada aplikasi di atas. Kita memerlukan sebuah window
untuk menampung gambar yang ditampilkan dalam window, tidak hanya message box seperti di atas.
Untuk aplikasi dengan Window, strukturnya agak lain. Akan saya bahas pada subbab berikut.
45
Window dan Message.
Aplikasi yang memiliki window berjalan dalam suatu loop yang disebut message loop atau message
pump. Message adalah suatu 'pesan' yang dikirimkan dari sistem atau ke sistem window. Message
digenerate jika terjadi 'sesuatu' terhadap window seperti mouse click, key press,
maximize/minimize dan lain sebagainya.
Aplikasi Windows
Window Object
Tiap kali Windows mengirimkan message ke aplikasi kita, kita bisa melakukan sesuatu berdasarkan
jenis message yang dikirimkan. Oleh karena itu, selain WinMain, yang kita perlukan adalah Window
Procedure. Window Procedure adalah suatu fungsi yang digunakan untuk menangani message yang
dirkirimkan dari Windows.
Istilah window dalam pemrograman window tidak terbatas pada window yang mempunyai title bar
dan menu. Seluruh objek visual yang berinteraksi dengan user adalah window. Button, CheckBox,
Combo Box, TextBox, dan lain-lain kesemuanya adalah window. Jadi object button, Combo Box,
CheckBox, TextBox dan lain-lain mempunyai tipe HWND yang berarti handle ke suatu window.
• Window Class. Window Class merupakan sebuah structure yang menggambarkan style
window yang akan dibuat.
• Message Structure. Merupakan suatu structure yang digunakan untuk menampung message
yang dikirimkan window ke aplikasi.
• Window Procedure. Merupakan suatu procedure yang harus dibuat untuk menangani
message yang dikirimkan ke window.
Window Class
Dalam Windows SDK, Window Class dinyatakan dalam structure WNDCLASSEX sebagai berikut :
typedef struct
{
UINT cbSize ;
46
UINT style ;
WNDPROC lpfnWndProc ;
int cbClsExtra ;
int cbWndExtra ;
HINSTANCE hInstance ;
HICON hIcon ;
HCURSOR hCursor ;
HBRUSH hbrBackground ;
LPCTSTR lpszMenuName ;
LPCTSTR lpszClassName ;
HICON hIconSm ;
} WNDCLASSEX, *PWNDCLASSEX;
Arti dari tiap member structure di atas adalah sebagai berikut :
Handle untuk icon yang digunakan dalam window, jika berisi NULL, akan
hIcon ditampilkan default icon.
Tipe dari variabel ini adalah HICON yang berarti handle dari icon aplikasi.
Handle dari cursor mouse yang digunakan dalam window, jika berisi NULL,
maka aplikasi harus secara eksplisit mengeset cursor yang digunakan ketika
hCursor mouse bergerak di dalam window.
Tipe variabel ini adalah HCURSOR yang berarti handle dari cursor mouse.
Warna brush yang digunakan dalam background window, bisa berupa
handle ke brush yang dibuat oleh user atau brush system.
hbrBackground
Tipe variabel ini adalah HBRUSH yang berarti handle ke warna brush. Untuk
dokumentasi mengenai brush bisa dilihat di MSDN.
Nama dari resource menu yang digunakan dalam window, jika window tidak
lpszMenuName
mempunyai menu maka nilainya adalah NULL.
Handle untuk icon berukuran kecil yang akan ditampilkan di window. Jika
hIconSm isinya adalah NULL, maka system akan mencari icon yang digunakan dalam
member hIcon dan mencari ukuran yang pas untuk icon yang kecil.
47
Message Structure
Message structure adalah suatu structure yang digunakan untuk menampung message yang dikirim
ke window. Message structure didefinisikan sebagai berikut :
typedef struct
{
HWND hwnd ;
UINT message ;
WPARAM wParam ;
LPARAM lParam ;
DWORD time ;
POINT pt ;
} MSG, *PMSG;
Window Procedure
Window Procedure adalah suatu fungsi yang digunakan untuk menangani message yang dikirim ke
window. Dalam window procedure ini kita bisa menentukan apa yang harus dilakukan ketika suatu
message dikirim ke window yang bersangkutan. Nama dari window procedure adalah terserah dari
user, tetapi harus memenuhi prototype sebagai berikut:
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM
lParam ) ;
Membuat Window
Untuk membuat window, kita menggunakan fungsi dari Win32API yaitu CreateWindowEx sebagai
berikut, untuk dokumentasi lengkap dari CreateWindowEx bisa dilihat di MSDN, untuk fungsi ini saya
hanya akan menampilkan keterangan dalam comment.
HWND CreateWindowEx(
DWORD dwExStyle, // ExStyle dari window
LPCTSTR lpClassName, // Nama dari window class
LPCTSTR lpWindowName, // Judul Window
DWORD dwStyle, // Style Window
int x, // Posisi window di layar (x)
48
int y, // Posisi window di layar (y)
int nWidth, // Lebar window dalam pixel
int nHeight, // Panjang window dalam pixel
HWND hWndParent, // Parent dari window
HMENU hMenu, // Handle ke menu, NULL jika tidak memakai menu
HINSTANCE hInstance, // handle ke instance aplikasi
LPVOID lpParam // parameter tambahan
);
Baiklah, sekarang saatnya kita masuk ke code untuk membuat aplikasi dengan window. Pertama kali
buat project kosong seperti di atas dengan satu file WinMain.cpp. Program kita kali ini akan terbagi
dalam 3 fungsi yaitu WinMain sebagai fungsi utama, InitWindow sebagai fungsi untuk membuat
window, dan WndProc sebagai Window Procedure. Kita akan menulis InitWindow dan WndProc di
bawah fungsi WinMain. Oleh karena itu dalam program kita kali ini kita akan menulis prototype
InitWindow dan WndProc di atas WinMain.
Untuk aplikasi kita yang sekarang, kita tidak akan mengubah character set. Kita akan menggunakan
header tchar.h yang memungkinkan kita menulis code yang sama baik untuk aplikasi UNICODE
maupun ANSI. Yang perlu diingat adalah selalu meletakkan macro TEXT() untuk setiap string literal
dan juga memperhatikan tipe-tipe data berikut ini :
Tipe Keterangan
Akan menjadi char bila UNICODE tidak terdefinisi dan menjadi wchar_t
TCHAR
jika UNICODE terdefinisi.
Unicode terdefinisi : LPWSTR atau wchar_t *
LPTSTR
Unicode tidak terdefinisi : LPSTR atau char *
Unicode terdefinisi : LPCWSTR atau const wchar_t *
LPCTSTR
Unicode tidak terdefinisi : LPCSTR atau const char *
Untungnya tchar.h sudah mendefiniskan macros untuk WinMain, sehingga dengan tchar.h kita hanya
perlu menulis WinMain untuk kedua versi tersebut sebagai berikut :
49
Ini adalah entry point untuk program, bagian ini berisi deklarasi include header, variabel global dan
kontanta global.
#include <Windows.h>
#include <tchar.h>
/************************************************************************/
/* Konstanta Global */
/************************************************************************/
/************************************************************************/
/* Global Variabel */
/************************************************************************/
/************************************************************************/
/* Prototype WndProc dan InitWindow supaya bisa digunakan di WinMain */
/************************************************************************/
g_hInstance = hInstance ;
// Inisialisasi Window
if ( !InitWindow( g_hInstance ) )
{
return 1 ;
}
// Masuk ke Message Loop
while ( ( bMsgRet = GetMessage(&msg, NULL, 0, 0) ) != 0 )
{
if ( bMsgRet == -1 )
{
MessageBox( NULL, TEXT( " Error in Message Loop" ),
TEXT( "Error!" ), MB_ICONSTOP ) ;
break ;
}
// Proses Message
TranslateMessage( &msg ) ;
DispatchMessage( &msg ) ;
}
DestroyWindow( g_hWindow ) ;
UnregisterClass( g_szClassName, g_hInstance ) ;
50
Bagian terpenting dari bagian ini adalah message loop bagian inilah yang menerima. Fungsi
GetMessage mengecek dalam antrian message pada aplikasi. Jika ada message maka fungsi
TranslateMessage dan DispatchMessage dijalankan untuk melempar kontrol ke WndProc.
Setelah menulis WinMain seperti di atas, sekarang kita mengimplementasikan InitWindow, yaitu
fungsi untuk membuat dan menampilkan window.
InitWindow
Sebelum window dibuat, aplikasi harus meregister class. Setelah class teregister, aplikasi membuat
window yang diperlukan. Code berikut ini membuat dan menampilkan window.
/************************************************************************/
/* Fungsi InitWindow( HINSTANCE hInstance ) */
/* Digunakan untuk membuat dan menampilkan window */
/* Return value: TRUE jika berhasil dan FALSE jika gagal membuat window */
/************************************************************************/
BOOL InitWindow( HINSTANCE hInstance )
{
// Deklarasi Window Class dan isi dengan 0
WNDCLASSEX wcex ;
ZeroMemory( &wcex, sizeof( WNDCLASSEX ) ) ;
// Register Class
if ( !RegisterClassEx( &wcex ) )
{
return FALSE ;
}
// Buat Window
g_hWindow = CreateWindowEx(
WS_EX_APPWINDOW,
g_szClassName,
g_szWindowTitle,
WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX ,
CW_USEDEFAULT,
CW_USEDEFAULT,
g_nWindowWidth,
g_nWindowHeight,
NULL,
NULL,
hInstance,
NULL);
51
ShowWindow(g_hWindow, SW_SHOWNORMAL);
UpdateWindow(g_hWindow) ;
return TRUE ;
}
Setiap aplikasi yang akan membuat window, pertama kali harus meregister window classnya dahulu,
window class mendeskripsikan karakteristik dari window yang akan dibuat. Seperti yang saya
terangkan sebelumnya window class ditampung dalam structure WNDCLASSEX. Pada Window di atas
saya menambahkan & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX dengan tujuan supaya window tidak
bisa di-resize dan dimaximize.
Fungsi WndProc digunakan untuk menentukan apa yang akan dilakukan aplikasi ketika suatu
message dikirimkan dari sistem ke aplikasi.
/************************************************************************/
/* Fungsi WndProc */
/* Befungsi untuk menangani message yg dikirim dr system */
/************************************************************************/
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_DESTROY :
PostQuitMessage( 0 ) ;
break ;
default :
return DefWindowProc( hWnd, uMsg, wParam, lParam ) ;
}
return FALSE ;
Ini adalah bagian terakhir yang diperlukan dalam suatu program window. Dalam fungsi ini kita
menggunakan statement switch untuk menentukan response terhadap suatu message. Dalam
program di atas yang kita tangani adalah message WM_DESTROY yang akan mengirimkan message
WM_QUIT dengan PostQuitMessage. Seluruh message akan menghasilkan nilai nonzero ketika
diambil oleh GetMessage kecuali WM_QUIT sehingga ketika WM_QUIT dikirimkan, message loop
yang mengandung GetMessage dalam fungsi WinMain di atas akan keluar dan akan mengakhiri
aplikasi.
Setelah dicompile, dan dijalankan, akan keluar window kosong seperti di bawah ini :
52
Gambar 2.9 Window Kosong hasil dari program di atas
Hasil dari code di atas adalah sebuah window kosong dengan warna warna background Application
Workspace seperti yang diset di Control Panel. Window tersebut mempunyai icon default berupa
icon untuk aplikasi, dengan tombol maximize terdisable dan window tidak bisa diresize, cursor yang
digunakan adalah berupa panah biasa.
Pengenalan Direct3D
Dalam DirectX 9, untuk memrogram 3D kita menggunakan Direct3D. Direct3D adalah suatu graphic
API bersifat low-level yang menjadikan programmer mampu merender dunia 3D ke dalam layar
komputer. Direct3D bisa diumpamakan seperti penghubung antara graphic hardware dan aplikasi.
Gambar berikut ini menunjukkan hubungan aplikasi, Direct3D, dan hardware
Direct3D menyediakan interface yang berguna untuk melakukan fungsi-fungsi tertentu terhadap
graphic device, contoh, bila programmer ingin melakukan clearing (pembersihan) pada layar, kita
menggunakan method IDirect3Ddevice9::Clear.
Pada gambar 2.10, terlihat setelah Direct3D ada HAL (Hardware Abstraction Layer). Direct3D tidak
bisa berinteraksi secara langsung dengan hardware karena ada banyak jenis hardware di pasaran
yang mempunyai kemampuan dan implementasi sendiri-sendiri, misal mungkin satu card dengan
card lain berbeda dalam implementasi clear screen. HAL adalah sebuah set fungsi-fungsi yang
tergantung pada produsen hardware untuk implementasinya.
53
Pembuat hardware memrogram seluruh kemampuan hardware ke dalam HAL, feature yang didukung
Direct3D tetapi tidak didukung hardware tidak diimplementasikan dalam HAL. Memanggil fungsi
Direct3D yang tidak didukung oleh HAL menghasilkan kegagalan. Kecuali vertex processing, yang
bisa diemulasi oleh Direct3D di dalam software, jika menggunakan feature yang hanya didukung
beberapa card di pasaran, pastikan sudah memverifikasi dukungan feature tersebut pada hardware.
REF device
Mungkin Anda ingin menulis code yang didukung oleh Direct3D tetapi tidak didukung oleh hardware
Anda, selain HAL, tersedia juga device REF yang mengemulasi seluruh Direct3D API secara software.
REF device hanya untuk keperluan development, REF device sangat lamban dibandingkan dengan
HAL device.
D3DDEVTYPE
Dalam code, HAL dinyatakan dengan konstanta D3DDEVTYPE_HAL, yang merupakan anggota dari
enumerasi D3DDEVTYPE. Untuk REF, yang digunakan adalah konstanta D3DDEVTYPE_REF.
Konstanta ini penting karena akan digunakan ketika kita membuat Direct3D Device.
Surface
Surface adalah barisan pixel yang digunakan Direct3D untuk menyimpan data image 2D yang
ditampilkan di layar. Walaupun data ditulis dalam bentuk matrix, dalam memory pixel disimpan
dalam barisan array.
Pitch
Area
Y+
54
Panjang dan lebar surface diukur dalam pixel. Pitch diukur dalam bytes. Lebih dari itu, pitch
mungkin saja lebih lebar daripada lebar, tergantung dari implementasi hardware. Jadi tidak bisa
kita menentukan bahwa pitch = width * sizeof(pixelformat).
Dalam code, kita mengakses surface melalui interface IDirect3DSurface9. Interface ini
menyediakan beberapa method untuk menulis dan membaca dari surface secara langsung. Method
yang paling penting dari IDirect3DSurface9 antara lain :
• UnlockRect, setelah kita selesai dengan LockRect, kita sudah selesai mengakses memory
dari surface yang bersangkutan, kita harus meng-unlock surface tersebut.
• GetDesc, method ini digunakan untuk mendapatkan deskripsi surface dengan mengisi
structure D3DSURFACE_DESC.
Contoh penggunaan interface ini ada dalam snippet code di bawah ini, yaitu dengan mengisi semua
surface dengan warna pixel biru.
D3DSURFACE_DESC SurfDesc ;
pSurface->GetDesc( &SurfDesc ) ;
pSurface->UnlockRect( ) ;
Multisample
Multisample adalah suatu teknik untuk menghaluskan image yang kasar dan blocky. Satu teknik yang
menggunakan multisampling adalah Full Screen Anti Aliasing (FSAA).
55
Gambar 2.11 Garis biasa (kiri) dan garis yang telah dianti-aliasing (kanan)
Selain itu ada juga nilai DWORD yang digunakan untuk menentukan kualitas multisample.
Untuk program-program dalam buku ini, kita sama sekali tidak menggunakna multisample karena
akan menjadikan aplikasi amat lambat. Jika ingin menggunakan, jangan lupa untuk mengeceknya
dulu dengan method IDirect3D9::CheckDeviceMultisampleType untuk mengecek apakah
device yang digunakan mendukung multisample dan juga kualitas multisample.
Pixel Format
Kita akan sering sekali menggunakan pixel format ketika membuat surface atau texture. Format dari
pixel ditentukan dengan nilai dari enumerasi D3DFORMAT. Beberapa format antara lain sebagai
berikut :
Nilai Keterangan
Merupakan pixel format 24 bit yang dimulai dari bit paling kiri 8
D3DFMT_R8G8B8 bit untuk warna merah, 8 bit untuk warna hijau, dan 8 bit untuk
warna biru.
Merupakan pixel format 32 bit yang dimulai dari bit paling kiri, 8
D3DFMT_X8R8G8B8 bit tidak dipakai, 8 bit untuk warna merah, 8 bit untuk warna
hijau, dan 8 bit untuk warna biru.
Merupakan pixel format 32 bit yang dimulai dari bit paling kiri, 8
D3DFMT_A8R8G8B8 bit untuk alpha, 8 bit untuk warna merah, 8 bit untuk warna
hijau, dan 8 bit untuk warna biru.
Merupakan pixel format floating point 64 bit. Dimulai dari bit
D3DFMT_A16R16G16B16F paling kiri, 16 bit untuk alpha, 16 bit untuk warna merah, 16 bit
untuk warna hijau, dan 16 bit untuk warna biru.
Merupakan pixel format floating point 128 bit. Dimulai dari bit
D3DFMT_A32R32G32B32F paling kiri, 32 bit untuk alpha, 32 bit untuk warna merah, 32 bit
untuk warna hijau, dan 32 bit untuk warna biru.
Untuk daftar Pixel Format yang lengkap bisa dilihat dalam dokumentasi DirectX SDK. Yang paling
sering digunakan adalah D3DFMT_R8G8B8, D3DFMT_X8R8G8B8,dan D3DFMT_A8R8G8B8. Ketiga
format tersebut didukung hampir di semua hardware. Sedangkan format lainnya tidak. Jika ingin
56
menggunakan pixel format yang tidak didukung di semua hardware pastikan mengeceknya dulu
sebelum memakai.
Memory Pool
Surface dan resource Direct3D lainnya dapat disimpan dalam banyak memory pool. Memory pool
ditentukan dengan salah satu nilai dari enumerasi D3DPOOL. Memory pool yang tersedia antara lain :
Nilai Keterangan
Swap chain, yang digunakan dalam page flipping adalah suatu teknik untuk menampilkan animasi
yang halus. Perhatikan gambar berikut yang menggambarkan swap chain dengan 2 surface.
Surface 1 Surface 2
Surface dalam front buffer berisi citra yang sedang ditampilkan di layar. Monitor tidak
menampilkan front buffer secara instan akan tetapi tergantung dari refresh rate monitor yang
bersangkutan, dan frame rate biasanya jauh lebih cepat daripada monitor refresh rate. Sebagai
contoh, jika monitor 80 Hz, maka monitor memerlukan waktu 1/80 second untuk menampilkan citra
57
ke layar monitor. Akan tetapi, kita tidak ingin mengupdate front buffer dengan frame selanjutnya
sebelum monitor selesai menampilkan frame yang sekarang sedangkan kita juga tidak ingin
menghentikan rendering ketika menunggu monitor selesai menampilkan frame. Itulah gunanya back
buffer yang menampung hasil rendering, ketika front buffer sudah selesai dirender maka surface
tersebut dipindah di belakang swap chain, dan back buffer dipindah menjadi front buffer. Hal ini
disebut presenting.
Surface 1 Surface 2
Gambar 2.13. menggambarkan bagaimana proses presenting dalam swap chain. Dari gambar
tersebut bisa kita dapatkan bahwa presenting adalah menukar dua surface.
Depth Buffer
Depth buffer adalah sebuah surface yang tidak berisi data citra, tetapi berisi informasi dari
kedalaman (depth) dari tiap pixel. Jika kita membuat buffer dengan ukuran 800x600 maka akan ada
depth buffer dengan ukuran 800x600 pula.
Pada gambar 2.14 terlihat object saling menutupi satu sama lain. Agar Direct3D menentukan pixel
mana yang menutupi pixel tertentu, maka diperlukan teknik depth buffering atau z buffering.
58
Depth buffering dilakukan dengan mencari nilai depth dari tiap pixel dan melakukan depth test.
Depth test secara ringkas adalah mengetest mana pixel yang menutupi pixel lain. Jika pixel lebih
dekat dengan kamera, maka pixel tersebut digambar, jika tidak maka tidak digambar.
Nilai Keterangan
Vertex Processing
Vertex adalah data dasar yang digunakan untuk membuat object 3D dan dapat diproses dengan
beberapa cara baik software maupun hardware. Software vertex processing selalu bisa digunakan
tetapi hardware vertex processing hanya bisa dilakukan oleh hardware yang mendukung.
Hardware vertex processing lebih diutamakan daripada software vertex processing. Dengan
memindah vertex processing ke hardware maka kerja CPU menjadi lebih sedikit, selain itu hardware
vertex processing lebih cepat karena dilakukan dalam satu hardware tersendiri yaitu VGA.
Device Capabilities
Setiap feature yang ada di Direct3D mempunyai member data yang berkaitan pada structure
D3DCAPS9. Guna dari structure ini adalah untuk menentukan feature apa saja yang didukung oleh
hardware kita. Kita bisa menentukan apakah suatu feature didukung dengan mengecek member dari
D3DCAPS9. Snippet di bawah ini menunjukkan bagaimana cara menentukan apakah suatu feature
hardware transform and lighting didukung atau tidak. Feature ini berguna untuk menentukan apakah
hardware mendukung hardware vertex processing atau tidak.
D3DCAPS9 Caps ;
bool isHardwareTNLSupported = false ;
ZeroMemory( &Caps, sizeof( D3DCAPS9 ) ) ;
59
Menambahkan Direct3D
Untuk menambahkan Direct3D ke dalam aplikasi kita langkah-langkah yang perlu dilakukan adalah
sebagai berikut :
2. Cek capabilitas device (D3DCAPS9) untuk mengecek apakah Primary Display Adapter
mendukung hardware vertex processing atau tidak. Kita memerlukan informasi ini untuk
membuat object IDirect3DDevice9.
Catatan Khusus :
Fungsi ini akan mengembalikan pointer ke interface IDirect3D9. Jika nilai yang dikembalikan
adalah NULL, berarti fungsi tersebut gagal dipanggil. Kita bisa mengenumerasi dan meng-query
kemampuan dari semua Direct3D device yang ada dalam komputer kita (walau umumnya hanya 1).
Untuk menentukan jumlah device adapter Direct3D yang ada dalam komputer kita, kita cukup
memanggil method GetAdapterCount setelah interface IDirect3D9 didapatkan. Prototype dari
method ini adalah :
Fungsi ini tidak memerlukan parameter apapun dan akan mengembalikan nilai UINT (unsigned
int) yang merupakan jumlah adapter Direct3D yang ada dalam komputer kita. Untuk buku ini kita
tidak akan melakukan enumerasi apapun, asumsi kita adalah bahwa kita bekerja pada adapter
default yaitu adapter yang sedang digunakan sekarang, adapter yang menampilkan desktop Anda.
60
tersebut, pertama kali yang kita lakukan adalah mencari tahu apakah primary display adapter kita
mendukung hardware vertex processing, jika iya digunakan, jika tidak maka kita menggunakan
software vertex processing. Untuk itu kita mengakses fungsi GetDeviceCaps untuk mendapatkan
informasi ini. Prototype dari method GetDeviceCaps adalah sebagai berikut :
HRESULT IDirect3D9::GetDeviceCaps(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DCAPS9 *pCaps
);
Parameter Keterangan
Adapter yang akan kita cari kapabilitasnya. Untuk adapter default kita
Adapter
gunakna D3DADAPTER_DEFAULT.
Device type yang digunakan hardware device (D3DDEVTYPE_HAL)
DeviceType
atau software (D3DDEVTYPE_REF)
Pointer ke structure D3DCAPS9 yang digunakan untuk mendapatkan
pCaps
hasil dari method GetDeviceCaps.
61
Parameter Keterangan
62
Contoh pengisian dari structure di atas adalah sebagai berikut :
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth = 640;
d3dpp.BackBufferHeight = 480;
d3dpp.BackBufferFormat = D3DFMT A8R8G8B8; //pixel format
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = g_hWindow;
d3dpp.Windowed = TRUE; // windowed
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT D24S8; // depth format
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
HRESULT IDirect3D9::CreateDevice (
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS *pPresentationParameters,
IDirect3DDevice9 **ppReturnedDeviceInterface
) ;
Parameter Keterangan
63
Contoh penggunaan :
hr = g_pD3D->CreateDevice (
D3DADAPTER_DEFAULT, // primary adapter
D3DDEVTYPE_HAL, // device type
g_hWindow, // focus window
D3DCREATE_HARDWARE_VERTEXPROCESSING, // vertex processing
&d3dpp, // present parameters
&g_pDevice // device object
) ;
// jika gagal
if( FAILED( hr ) )
{
MessageBox( NULL, TEXT( "CreateDevice() - FAILED" ), TEXT( "Error" ),
MB_OK | MB_ICONEXCLAMATION ) ;
return FALSE ;
}
Membersihkan layar
Setelah device berhasil dibuat, maka kita bisa merender ke layar. Sebelum menggunakan polygon
dan texture, yang pertama kali kita lakukan adalah membersihkan layar. Untuk membersihkan layar,
kita menggunakan method dari IDirect3DDevice9 sebagai berikut :
HRESULT IDirect3DDevice9::Clear(
DWORD Count,
CONST D3DRECT * pRects,
DWORD Flags,
D3DCOLOR Color,
float Z,
DWORD Stencil
);
Arti dari parameter dari fungsi di atas adalah sbb
Parameter Keterangan
Jumlah rectangle yang akan di-clear, jika ini berisi 0, maka parameter
kedua harus NULL. Dengan begitu seluruh area akan di-clear yang
Count
merupakan behavior yang umum. Jika nilainya lebih dari 0, maka
pRects harus berisi array dari Rectangle yang akan diclear.
pRects Array rectangle yang akan diclear. NULL jika parameter Count = 0 .
Flags menunjukkan buffer apa saja yang akan di-clear, bisa salah satu
atau kombinasi dari nilai D3DCLEAR berikut ini :
D3DCLEAR_STENCIL
Flags
Membersihkan stencil buffer
D3DCLEAR_TARGET
Membersihkan render target (buffer yang berisi tampilan
64
scene)
D3DCLEAR_ZBUFFER
Membersihkan depth buffer.
Warna yang digunakan untuk membersihkan layar. Bisa menggunakan
Color
Macro D3DCOLOR_XRGB atau yang lain.
Reset stencil buffer ke nilai antara 0 dan 2n-1 (n = jumlah bit stencil
Stencil
buffer)
Menampilkan ke layar
Setelah frame di-clear, saatnya menampilkannya ke layar. Direct3D menggunakan method Present
dari device object untuk melakukan ini.
Semua operasi menggambar seperti clear dan lain-lain disimpan dalam backbuffer sebelum
ditampilkan ke layar seperti yang saya terangkan pada swap chain di atas. method Present
digunakan untuk melakukan swap/page flipping. Prototype dari method ini adalah sebagai berikut :
HRESULT IDirect3DDevice9::Present(
CONST RECT * pSourceRect,
CONST RECT * pDestRect,
HWND hDestWindowOverride,
CONST RGNDATA * pDirtyRegion
) ;
Parameter Keterangan
65
Melepaskan object Direct3D
Dalam semua software DirectX, hal terakhir yang harus dilakukan adalah membersihkan dan
melepaskan (release) object DirectX. Ketika aplikasi berakhir, kita harus me-release semua object
yang kita pakai. Untuk itu kita harus memanggil method Release pada semua object yang kita buat.
if ( g_pDevice )
{
g_pDevice->Release( ) ;
g_pDevice = NULL ;
}
if ( g_pD3D )
{
g_pD3D->Release( ) ;
g_pD3D = NULL ;
}
Urutan dalam me-release object adalah berbalik dari urutan waktu meng-create object. Satu hal
yang penting lagi, selalu mengecek apakah object NULL atau tidak. Mencoba memanggil Release
pada object NULL menyebabkan aplikasi crash.
Pertama kali yang harus dilakukan adalah menambahkan header untuk Direct3D.
#include <d3d9.h>
Setelah itu kita deklarasi dua variabel global untuk menampung object Direct3D dan object Device.
Supaya terstruktur, kita melakukan inisialisasi Direct3D pada fungsi tersendiri, kita namai
InitDirect3D. Kita tulis dulu prototypenya sebelum WinMain.
if ( !InitDirect3D( g_hWindow ) )
{
return 1 ;
}
Selain InitDirect3D, kita mendeklarasikan juga fungsi DoFrame untuk melakukan operasi frame dan
Cleanup untuk melakukan operasi pembersihan terhadap Direct3D. Kita tulis prototypenya sebelum
WinMain.
66
Selain itu, kita pisahkan juga message loop ke dalam fungsi tersendiri, sehingga kita tulis
prototypenya juga sebelum WinMain. Variabel DisplayCB adalah CALLBACK function yang
merupakan pointer ke fungsi yg digunakan untuk render.
Sebagai fungsi utilitas, kita membuat fungsi juga untuk menampilkan error bila terjadi kesalahan
dan untuk menengahkan window di layar. Prototypenya sebagai berikut :
// Inisialisasi Window
if ( !InitWindow( g_hInstance ) )
{
ErrorMessage( TEXT( "InitWindow( ) - Error!" ) ) ;
return 1 ;
}
// Inisialsasi Direct3D
if ( !InitDirect3D( g_hWindow ) )
{
ErrorMessage( TEXT( "InitDirect3D( ) - Error!" ) ) ;
return 1 ;
}
// Bersih-bersih
Cleanup( ) ;
DestroyWindow( g_hWindow ) ;
return iExitCode ;
}
Implementasi InitDirect3D
InitDirect3D diimplementasikan dengan inisialisasi pointer g_pD3D dan g_pDevice sehingga
bisa dipakai untuk menggambar scene ke layar.
/************************************************************************/
/* Fungsi InitDirect3D */
/* Digunakan untuk inisialisasi direct3d ke window */
/************************************************************************/
BOOL InitDirect3D( HWND hWindow )
{
// Deklarasi variabel dan structure yang diperlukan
D3DCAPS9 Caps ;
D3DPRESENT_PARAMETERS d3dpp ;
DWORD dwVertexProcessing = 0 ;
67
HRESULT hr = 0 ;
// Bersihkan structure
ZeroMemory( &d3dpp, sizeof ( D3DPRESENT_PARAMETERS ) ) ;
// Buat Device
hr = g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
hWindow, dwVertexProcessing, &d3dpp, &g_pDevice ) ;
// Cek error
if ( FAILED ( hr ) || ( g_pDevice == NULL ) )
{
if ( g_pD3D )
{
g_pD3D->Release( ) ;
g_pD3D = NULL ;
}
Fungsi di atas akan membuat object direct3d, mengisi D3DPRESENT_PARAMETERS dan mebuat
device object.
68
Implementasi DoFrame
DoFrame digunakan untuk menggambar scene ke layar. Parameter DeltaTime digunakan untuk
mengukur kecepatan animasi yang bergantung pada jarak waktu render antar frame. Untuk saat ini
yang perlu dilakukan hanyalah membersihkan backbuffer dengan warna terentu dan mem-
presentnya ke layar.
/************************************************************************/
/* Fungsi Callback DoFrame */
/* Menggambar ke window, untuk saat ini yang dilakukan hanya membersih- */
/* kan layar ke warna biru */
/************************************************************************/
BOOL CALLBACK DoFrame( float DeltaTime )
{
// cek apakah device dan direct3d object tidak NULL
if ( !g_pD3D || !g_pDevice )
{
return FALSE ;
}
Kode di atas akan meng-clear render target, depth buffer, dan stencil buffer. Render target akan
di-clear dengan warna putih, untuk nilai depth buffer dan stencil buffer akan diterangkan dalam
bab selanjutnya. Setelah itu backbuffer di-present ke layar.
Implementasi Cleanup
Cleanup digunakan untuk melepaskan object device dan object Direct3D setelah window diclose.
/************************************************************************/
/* Fungsi Cleanup */
/* Membersihkan object Direct3D dari memory */
/************************************************************************/
Sebelum device atau Direct3D di release, pointernya dicek dulu, jika NULL, maka tidak dilakukan
apa2.
69
Implementasi EnterMessageLoop
EnterMessageLoop digunakan sebagai pengganti message loop yang ada dalam code sebelumnya
yang menggunakan GetMessage. GetMessage akan menunggu message yang ada sebelum keluar
fungsi, supaya tidak saling menunggu, kita menggunakan fungsi lain, yaitu PeekMessage untuk
mendapatkan message, jika tidak ada message maka kita merender scene kita ke layar dengan
memanggil callback yang dimasukkan dalam parameter. PeekMessage mempunyai prototype sebagai
berikut :
BOOL PeekMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
);
Parameter Keterangan
/************************************************************************/
/* Fungsi EnterMessageLoop */
/* Sebagai fungsi untuk melakukan loop message */
/************************************************************************/
WPARAM EnterMessageLoop( BOOL ( CALLBACK *lpfnDisplayCB ) ( float ) )
{
MSG msg ; // message structure
// apakah loop sudah selesai
BOOL bDone = FALSE ;
70
// time sekarang dan selisih (delta time)
float CurrentTime = 0.0f ;
float DeltaTime = 0.0f ;
// time terakhir
static float LastTime = static_cast<float>( timeGetTime( ) ) ;
return msg.wParam ;
}
Fungsi ini pertama kali adalah mengecek apakah ada message atau tidak, jika ada diproses, jika
tidak maka render scene, jika message = WM_QUIT maka akan keluar dari loop. Untuk merender
scene, perlu parameter DeltaTime. Untuk itu kita perlu menghitungnya. Untuk mendapatkan
system time, yaitu waktu yang berjalan sejak windows dijalankan (dalam ms) kita hanya perlu
memanggil fungsi timeGetTime. Fungsi ini tidak memerlukan parameter apapun dan akan
menghasilkan nilai system time. Selisih time dicari dengan mengurangkan waktu sekarang dan waktu
yang terakhir didapatkan ketika merender frame.
Implementasi ErrorMessage
ErrorMessage adalah fungsi sederhana yang menampilkan error message. Berguna jika terjadi
error.
/************************************************************************/
/* Fungsi ErrorMessage */
/* Menampilkan pesan error */
/************************************************************************/
71
void ErrorMessage( LPTSTR lpMessage )
{
if ( !lpMessage )
{
return ;
}
Implementasi CenterWindow
CenterWindow digunakan untuk menengahkan window di tengah layar. Kita memakai rumus
berikut untuk menengahkan window.
Untuk mendapatkan lebar window, pertama kali kita mencari RECT dari window tersebut, kemudian
mecari lebarnya dari rect tersebut. Untuk mendapatkan RECT dari window, kita mencarinya dengan
fungsi GetWindowRect.
BOOL GetWindowRect(
HWND hWnd,
LPRECT lpRect
);
Parameter pertama adalah handle ke window yang akan dicari RECT-nya kemudian parameter kedua
menampung hasil RECT-nya. Misalkan variabel RECT kita simpan dalam rcWindow. Maka kita
dapatkan rumus sbb:
/************************************************************************/
/* Fungsi Center Window */
/* Menengahkan Window */
/************************************************************************/
void CenterWindow( HWND hWindow )
{
POINT wndPos ;
RECT rcWindow ;
if ( !hWindow )
{
return ;
}
72
// hitung posisi window terhadap layar
wndPos.x = ( GetSystemMetrics( SM_CXSCREEN ) -
rcWindow.right + rcWindow.left ) / 2 ;
wndPos.y = ( GetSystemMetrics( SM_CYSCREEN) -
rcWindow.bottom + rcWindow.top ) / 2 ;
Fungsi SetWindowPos pada listing di atas adalah untuk memindahkan letak window ke suatu
tempat, flag SWP_NOSIZE, berguna untuk menyatakan bahwa fungsi tersebut hanya menentukan
lokasi window, tidak mengubah ukuran lebar dan panjang window.
Client area adalah tempat kita meletakkan scene, ketika kita menentukan panjang dan lebar
window dalam CreateWindowEx, sebenarnya kita mengeset panjang dan lebar window untuk
keseluruhan window, termasuk caption dan border. Sehingga misalkan kita menentukan bahwa
window berukuran 640x480. maka itu adalah ukuran window, ukuran client area lebih kecil
daripada itu. Jika kita ingin bahwa 640x480 adalah ukuran client area, maka kita harus menentukan
ukuran window adalah ukuran client area ditambah ukuran border dan ukuran caption bar. Untuk
mendapatkan ukuran border dan caption bar, kita menggunakan fungsi GetSystemMetrics :
73
Lebar Window = Lebar Client Area + 2 × lebar border (kanan dan kiri)
Panjang Window = Panjang Client Area + Panjang Title Bar + Panjang border (bottom)
Setelah window dibuat, maka kita panggil fungsi CenterWindow dengan memasukkan parameter
handle window yang sudah dibuat tadi untuk menengahkan window di layar. Sehingga kodenya
menjadi :
.
.
.
// Buat Window
g_hWindow = CreateWindowEx(
WS_EX_APPWINDOW,
g_szClassName,
g_szWindowTitle,
WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX ,
CW_USEDEFAULT,
CW_USEDEFAULT,
g_nWindowWidth +
2 * GetSystemMetrics( SM_CXBORDER ),
g_nWindowHeight +
GetSystemMetrics( SM_CYBORDER ) + GetSystemMetrics( SM_CYCAPTION ),
NULL,
NULL,
hInstance,
NULL);
if ( g_hWindow == NULL )
{
return FALSE ;
}
CenterWindow( g_hWindow ) ;
.
.
.
SM_CYBORDER adalah untuk mencari tinggi border, SM_CXBORDER adalah untuk mencari lebar
border, SM_CYCAPTION adalah untuk mencari tinggi caption bar. Untuk listing lengkap bisa dilihat
dalam CD yang diikutkan dalam buku ini.
/************************************************************************/
/* Fungsi WndProc */
/* Befungsi untuk menangani message yg dikirim dr system */
/************************************************************************/
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_CLOSE :
PostMessage( hWnd, WM_QUIT, 0, 0 ) ;
break ;
74
// handle jika tombol keyboard dilepaskan
case WM_KEYUP :
// jika tombolnya adalah [ESC]
// perintahkan window untuk menutup dirinya
if ( wParam == VK_ESCAPE )
{
CloseWindow( hWnd ) ;
}
break ;
default :
return DefWindowProc( hWnd, uMsg, wParam, lParam ) ;
}
return FALSE ;
}
Setelah melakukan perubahan seperti di atas, kita harus mengatur aplikasi supaya terkait dengan
library d3d9.dll dan winmm.dll dengan melakukan linking terhadap export library-nya yaitu
d3d9.lib dan winmm.lib.
Kenapa kita harus melink aplikasi kita dengan d3d9.lib dan winmm.lib? Ini disebabkan kita memakai
fungsi Direct3DCreate9 dari library d3d9.dll dan timeGetTime dari library winmm.dll. Jika
tidak dilink dengan export librarynya, maka aplikasi tidak bisa di-build, karena ada missing link
untuk fungsi Direct3DCreate9 dan timeGetTime.
Untuk menambahkan kedua export library tersebut, kita melakukan langkah-langkah sebagai berikut:
• Klik pada node Linker > Input pada treeview dan set Configuration ke All Configuration.
Supaya setting diterapkan baik untuk Debug maupun Release build.
• Klik OK
75
Gambar 2.16 Penambahan additional dependencies
Setelah selesai melakukan perubahan seperti di atas pada code window tadi, saatnya mem-build
aplikasi. Jika Anda mengikuti petunjuk saya, maka tidak akan ada error dalam building aplikasi. Jika
aplikasi di-run atau di-debug maka akan keluar sebuah window kosong dengan background warna
biru. Artinya kita berhasil memerintahkan Direct3D untuk meng-clear render target, dan kita siap
dengan langkah selanjutnya.
76
Rendering Pipeline
Rendering pipeline bertanggung jawab menampilkan citra 2D dari koordinat 3D dan kamera maya
yang menentukan bagaimana suatu object 3D terlihat.
Gambar 3.1. gambar sebelah kiri menunjukkan bagaimana object 3D diletakkan dalam koordinat 3D,
sedangkan gambar sebelah kanan menunjukkan apa yang terlihat dari kamera
Representasi model
Sebuah Scene adalah suatu kumpulan object atau model. Object ditampilkan sebagai triangle mesh.
Triangle (segitiga) adalah dasar untuk membuat bentuk-bentuk geometri 3D. Seperti terlihat pada
gambar berikut :
Point yang merupakan perpotongan dua edge dari polygon disebut vertex. Untuk menyatakan
segitiga, kita bisa menentukan 3 vertex yang berupa tiga lokasi.
77
Vertex
Edge
Vertex Format
Definisi vertex di atas secara matematis tidak ada masalah, tetapi untuk Direct3D, definisi tersebut
masih kurang lengkap. Karena selain posisi, vertex bisa mempunyai properti lain seperti warna atau
normal. Kita bisa dengan bebas menentukan bagaimana vertex format kita.
Untuk membuat vertex format, pertama kali kita membuat structure yang menampung vertex data
yang kita inginkan , contoh :
struct VertexColor
{
float x, y, z ; // posisi
DWORD Color ;
} ;
struct VertexNormalTex
{
float x, y, z ; // posisi
float nx, ny, nz ; // normal
float tu, tv ; // koordinat texture
} ;
Structure yang pertama berisi posisi dan warna dari vertex. Structure yang kedua berisi posisi,
normal, dan koordinat texture dari vertex.
Setelah kita mendefinisikan structure tersebut, kita harus menentukan bagaimana format vertex
tersebut dengan menggunakan kombinasi flag dari Flexible Vertex Format (FVF). Untuk structure
yang pertama di atas, kita memakai vertex format
78
Triangles
Triangle adalah building block untuk object 3D. Untuk membuat suatu object, kita membuat sebuah
triangle list yang membentuk suatu object. Triangle list berisi setiap data dari triangle yang akan
kita gambar di layar. Contoh, jika ingin membuat kotak(rectangle), kita bagi rectangle tersebut
dalam dua buah triangle.
v1 v2
v0 v3
Urutan dari vertex tersebut disebut winding. Keterangan lebih lanjut tentang winding bisa dilihat
pada subbab BackFace Culling.
Index
Seringkali untuk membuat satu object 3D banyak vertex yang sama yang dipakai oleh dua polygon.
Seperti pada gambar 3.4. di atas v1 dan v3 dipakai oleh dua buah segitiga. Mungkin karena masih
sedikit bisa kita abaikan, akan tetapi jika bentuk makin kompleks, maka akan semakin banyak
vertex yang dipakai bersama.
Gambar 3.5. Kubus yang dibuat dengan triangle mempunyai banyak bertex yang dipakai bersama
Untuk mengatasi ini, kita bisa menggunakan index. Kita membuat vertex list dan index list, vertex
list berisi semua vertex, dan index list berisi value yang merupakan index dari vertex list untuk
menyatakan urutan vertex.
79
Contoh dalam kasus kotak di atas :
Arti dari array index di atas adalah, membuat segitiga pertama dengan rectVerts [ 0 ],
kemudian rectVerts[ 1 ], kemudian rectVerts[ 3 ], lalu segitiga kedua dengan rectVerts
[ 1 ], kemudian rectVerts[ 2 ], kemudian rectVerts[ 3 ].
Virtual Camera
Camera memperlihatkan bagaimana kita melihat bagian dari dunia 3D di mana kita harus membuat
citra 2D. Camera ditempatkan dan diarahkan di world dan menampilkan volume space yang terlihat.
Perhatikan gambar berikut :
Projection window,
Direct3D
mendefinisikan dengan
plane Z=1
Volume di atas disebut dengan frustum yang didefinisikan dengan sudut penglihatan (field of view)
dan near plane serta far plane. Penggunaan frustum dikarenakan bentuk monitor yang kotak. Object
yang ada di dalam frustum ditampilkan dan yang di luar frustum akan diabaikan, teknik ini disebut
dengan clipping.
Projection Window adalah area 2D di mana geometri 3D yang berada di dalam frustum
diproyeksikan ke dalam citra 2D. Untuk menyederhanakan, kita mengasumsikan dan membuat
bahwa near plane dan projection window berimpit dan Direct3D mendefinisikan projection plane
sebagai z = 1.
80
Rendering Pipeline
Jika kita telah merepresentasikan model kita dalam 3D, kita mempunyai tugas untuk
menampilkannya dalam citra 2D yang bisa dilihat di layar. Proses menampilkan dari data 3D ke citra
2D dinamakan Rendering Pipeline. Gambar di bawah ini menggambarkan bagaimana rendering
pipeline itu.
Beberapa tahapan dalam pipeline mentranformasi geometri dari satu sistem koordinat ke koordinat
lain yang hanya bisa dilakukan dengan matrix. Direct3D akan melakukan transformasi sesuai dengan
matrix yang kita masukkan. Supaya Direct3D melakukan transformasi, yang harus kita lakukan
hanyalah memberikan matrix yang diperlukan untuk melakukan transformasi melalui method
IDirect3DDevice9::SetTransform. Method ini mempunyai 2 parameter, parameter pertama
adalah jenis transformasinya, parameter kedua adalah matrix transformasinya.
Local Space
Local space atau modelling space adalah sistem koordinat yang menentukan triangle list dari suatu
object. Membuat model dalam space localnya lebih mudah daripada membuat model langsung ke
world. Memodel dengan local space menjadikan kita dengan mudah menyatakan model tanpa
mengetahui posisinya, ukurannya, atau orientasinya relatif terhadap object lain di world.
81
World Space
Jika kita sudah membuat banyak model yang berada pada local space masing-masing, kita harus
menggambarnya semua dalam global scene (world). Object dari local space ditransformasi ke world
space dengan proses yang disebut world transform yang biasa mengandung translasi, rotasi, dan
scaling yang mengeset posisi, orientasi, dan ukuran dari model di world. World transformation
menampilkan letak object-object di dalam world dan hubungannya dengan object lain.
Contoh di atas adalah contoh yang disederhanakan. Hanya untuk menunjukkan bagaimana cara dan
penggunaan world transformation dalam aplikasi.
82
View Space
Pada world space, kamera dibuat relatif terhadap world. Akan tetapi hal ini menjadi tidak praktis
dan kurang efisien untuk perhitungan matematis jika kamera berada dalam posisi dan orientasi
tertentu dalam world. Untuk itulah kita mentransformasi kamera berimpit dengan origin kemudian
memutarnya sehingga kamera menghadap ke z+. Semua object dalam world ditransformasi
bersamaan dengan kamera dengan transformasi yang sama sehingga kita melihat hal yang sama.
Z+ Z+ Z+
B
A B
A B
C
C
D
D C
D
X+ X+ X+
Translasi Rotasi
Gambar 3.10. Transformasi World Space ke View Space pertama kamera diimpitkan dengan origin
kemudian dirotasikan supaya menghadap ke Z+
Transformasi untuk view space ini bisa dicari dengan menggunakan fungsi D3DX berikut :
D3DXMATRIX *D3DXMatrixLookAtLH(
D3DXMATRIX* pOut, // pointer untuk menerima matrix
CONST D3DXVECTOR3* pEye, // position kamera di world
CONST D3DXVECTOR3* pAt, // arah kamera 'melihat'
CONST D3DXVECTOR3* pUp // arah 'atas' dr kamera
);
Parameter pEye merupakan posisi kamera di world, pAt adalah sasaran ke mana kamera melihat,
dan pUp adalah vector untuk arah atas biasanya adalah arah Y positif (0, 1, 0) . Kita memakai LH
karena Direct3D memakai Left Handed System.
Contoh : kita akan membuat scene dengan kamera berada di posisi (2, -3, 1 ) dengan target adalah
(1, 0, -1 ) kita bisa membuat matrix proyeksi sebagai berikut :
D3DXMATRIX matProj ;
83
BackFace Culling
Polygon mempunyai dua sisi yaitu sisi yang menghadap kamera sering disebug front face dan sisi
yang membelakangi kamera yang disebut backface. Backface dari polygon tidak pernah terlihat
karena kebanyakan object adalah solid sehingga kamera tidak mungkin 'masuk' ke dalam object
tersebut dan melihat backfacenya. Lihat gambar berikut ini :
Back Face
Front Face
ViewPoint
Gambar 3.11. Transformasi World Space ke View Space pertama kamera diimpitkan dengan origin
kemudian dirotasikan supaya menghadap ke Z+
Backface tidak pernah terlihat, oleh karena itu Direct3D bisa memanfaatkan ini dengan teknik
culling yaitu mengabaikan backface sehingga tidak diproses lebih lanjut. Di kamera, object akan
terlihat sama, karena backface memang tidak pernah terlihat.
ViewPoint
Direct3D perlu mengetahui polygon mana yang frontface, mana yang backface dengan menggunakan
winding order. Secara default Direct3D menganggap polygon yang vertexnya digambar urut searah
jarum jam pada view space adalah sebagai frontface, dan yang berlawanan dengan arah jarum jam
adalah backface.
84
v0 v0
v1 v2
v2 v1
Gambar 3.13. Winding order dalam pembuatan polygon. Searah jarum jam (kiri) dan berlawanan
arah jarum jam (kanan).
Catatan Khusus :
Penekanan winding order dalam view space adalah karena mungkin satu polygon yang winding
ordernya dalam world space searah dengan jarum jam, pada view space akan berlawanan.
Misalnya jika suatu polygon dirotasi 180 derajat, maka polygon tersebut akan berbalik arah
winding ordernya.
Jika karena suatu alasan, kita perlu mengubah winding order, kita hanya perlu mengakses fungsi
IDirect3DDevice9::SetRenderState dengan parameter pertama adalah D3DRS_CULLMODE
dan parameter kedua adalah salah satu dari nilai berikut :
Nilai Keterangan
D3DCULL_CCW Segitiga yang digambar berlawanan arah jarum jam yang di-cull.
Lighting
Sumber cahaya ditentukan dalam world space dan ditransformasikan ke view space. Pada view
space, sumber cahaya ini diterapkan untuk memberikan efek pencahayaan pada object. Untuk
lighting akan dibahas pada bab tersendiri nanti.
Clipping
Pada tahap ini kita meng-cull geometri yang ada di luar frustum. Proses ini disebut clipping. Ada 3
kemungkinan yang terjadi :
• Ada di luar frustum sama sekali, jika suatu segitiga ada di luar frustum maka segitiga
tersebut diabaikan
• Ada di dalam frustum seluruhnya, jika suatu segitiga ada di dalam frustum seluruhnya, maka
segitiga tersebut diproses semuanya.
85
• Ada sebagian di dalam frustum, sebagian di luar frustum, jika begini keadaannya, maka
segitiga akan dibagi menjadi dua, yang ada di dalam frustum akan diproses yang di luar
frustum akan diabaikan.
Pada tahap ini kita meng-cull geometri yang ada di luar frustum. Proses ini disebut clipping. Ada 3
kemungkinan yang terjadi :
Z+
Di Dalam
Di Luar
Sebagian di
luar/dalam
X+
Proyeksi
Pada view space, kita harus mengubah representasi 3D menjadi citra 2D di layar yang disebut
dengan projection (proyeksi). Ada banyak sekali proyeksi, tetapi saya hanya akan bahas
perspective projection. Proyeksi ini menggambarkan seperti keadaan asli, objek yang jauh keliatan
kecil, sedang yang dekat akan keliatan besar.
q'
Center of Projection
86
Projection transformation mendefinisikan frustum kita dan bertanggung jawab memproyeksikan
geometri ke dalam window. Membuat projection matrix sangat kompleks, sehingga kita hindari dulu
pembuatan matrix projection secara manual. Kita pake saja fungsi dari D3DX di bawah ini :
D3DXMATRIX *D3DXMatrixPerspectiveFovLH(
D3DXMATRIX* pOut, // projection matrix
FLOAT fovY, // fov vertikal
FLOAT Aspect, // aspect ratio = width / height
FLOAT zn, // jarak z untuk near plane
FLOAT zf // jarak z untuk far plane
);
Sebelum saya terangkan silahkan perhatikan gambar berikut :
Z+
fov
n
X+
Aspect ratio diperlukan untuk mengkoreksi distorsi transformasi dari projection window ke layar.
Aspect ratio adalah perbandingan antara lebar dan panjang layar.
AspectRatio = ScreenWidth/ScreenHeight
Sama dengan transformasi yang lain, untuk projection transformation kita menggunakan
IDirect3DDevice9::SetTransform untuk menerapkan transformasi ke layar dengan parameter
pertama adalah D3DTS_PROJECTION.
D3DMATRIX matProj ;
D3DXMatrixPerspectiveFovLH (
&matProj,
D3DX_PI * 0.25f, // fovy = 1/4 pi rad = 45 deg
(float) g_nWindowWidth / (float) g_nWindowHeight,
1.0f, // plane near = 1.0f
10000.0f // plane far = 10000.0f
) ;
g_pDevice->SetTransform( D3DTS_PROJECTION, &matProj ) ;
Viewport transform
Viewport transform bertanggung jawab atas transformasi koordinat di projection window ke
rectangle dalam screen yang kita sebut viewport. Untuk games biasanya viewport adalah
87
keseluruhan window, tapi kita bisa mengaturnya agar hanya window tertentu. Viewport dinyatakan
secara relatif terhadap window
MinZ dan MaxZ adalah nilai depth yang kita spesifikasikan, Direct3D secara default menggunakan
depth buffer 0 sampai 1, biarkan seperti itu kecuali jika menginginkan efek khusus. Setelah kita
menspesfikasikan viewport, kita bisa mengeset viewport sebagai berikut :
Direct3D menangani viewport secara otomatis. Sebagai refernsi, matrix yang digunakan dalam
transformasi viewport adalah sebagai berikut :
⎛ width ⎞
⎜ 0 0 0⎟
2
⎜ ⎟
⎜ height
0 − 0 0⎟
⎜ 2 ⎟
⎜ ⎟
⎜ 0 0 MaxZ − MinZ 0⎟
⎜ Width Height ⎟
⎜X + Y + MinZ 1⎟
⎝ 2 2 ⎠
88
Rasterization
Rasterization adalah proses setelah vertex ditransform ke koordinat layar 2D, tahap rasterisasi ini
diproses dengan menghitung warna dari tiap pixel pada tiap triangle. Proses rasterisasi adalah
proses yang paling process-intensive, sehingga hanya dilakukan oleh hardware grafik.
Setelah dirasterisasi, scene siap ditampilkan di layar. Yaitu dengan page flipping yang sudah
diterangkan di bab sebelumnya.
89
Menggambar di DirectX
Bab ini akan membahas bagaimana kita menggambar scene di Direct3D bermodalkan pengetahuan
yang sudah kita bahas di bab-bab sebelumnya. Method-method dan interface yang digunakan dalam
buku ini merupakan method dan interface yang penting, karena dipakai di bab-bab selanjutnya.
Vertex Buffer dinyatakan dalam interface IDirect3DVertexBuffer9 dan index buffer dinyatakan
dalam interface IDirect3DindexBuffer9.
HRESULT IDirect3DDevice9::CreateVertexBuffer(
UINT Length,
DWORD Usage,
DWORD FVF,
D3DPOOL Pool,
IDirect3DVertexBuffer9 **ppVertexBuffer
HANDLE *pSharedHandle
) ;
HRESULT IDirect3DDevice9::CreateIndexBuffer(
UINT Length,
DWORD Usage,
DWORD Format,
D3DPOOL Pool,
IDirect3DVertexBuffer9 **ppIndexBuffer
HANDLE *pSharedHandle
) ;
Karena kedua method tersebut mempunyai parameter yang hampir sama, mari kita bahas
bersamaan :
Parameter Keterangan
Jumlah byte yang dialokasikan untuk buffer. Jika kita ingin sebuah
Length buffer yang dapat menyimpan 8 vertex, maka kita set parameter
ini menjadi 8 * sizeof(Vertex).
Properti tambahan yang menyatakan bagaimana buffer digunakan,
Usage nilainya bisa 0 yang berarti tidak ada properti baru atau kombinasi
dari flag-flag berikut :
90
D3DUSAGE_DYNAMIC
Menyetting flag ini menyebabkan buffer menjadi dinamis.
Lihat catatan khusus.
D3DUSAGE_POINTS
Digunakan jika buffer berisi point primitives, biasanya
untuk membuat particle system.
D3DUSAGE_SOFTWAREPROCESSING
Vertex Processing dilakukan di software
D3DUSAGE_WRITEONLY
Menunjukkan bahwa aplikasi hanya akan menulis ke buffer
sehingga buffer ditempatkan di memory yang paling tepat
digunakan untuk operasi tulis. Membaca dari buffer dengan
flag ini akan menyebabkan error.
Flexible Vertex Format, yaitu format vertex yang dimasukkan ke
FVF
buffer
Catatan Khusus :
Buffer yang dibuat tanpa D3DUSAGE_DYNAMIC disebut static buffer. Static buffer biasa
disimpan dalam video memory. Membaca dari static buffer sangat lambat, karena mengakses
video memory itu lambat. Untuk itu, kita hanya menggunakan static buffer untuk menyimpan
static data yaitu data yang tidak akan berubah sangat cepat, contoh yang cocok dipakai sebagai
static buffer adalah bangunan dan terrain.
Buffer yang dibuat dengan D3DUSAGE_DYNAMIC disebut dynamic buffer. Dynamic buffer biasa
disimpan dalam memory AGP untuk pemrosesan yang cepat. Dynamic buffer tidak diproses
seefisien static buffer, tetapi dapat diakses dengan cepat. Contoh yang cocok dipakai sebagai
dynamic buffer adalah particle system, karena berubah sangat sering.
Sebenarnya ada cara paling efisien dalam menggunakan kedua buffer tersebut yaitu dengan
menyimpan copy data di system memory, kemudian dicopy ke buffer yang bersangkutan jika
diperlukan. Karena membaca dari system memory oleh CPU jauh lebih cepat daripada
membaca dari video memory atau AGP memory.
91
Snippet di bawah ini adalah contoh penggunaan pembuatan vertex buffer yang menyimpan 8 vertex.
g_pDevice->CreateVertexBuffer (
VertexTotalSize,
0,
D3DFVF_XYZ,
D3DPOOL_MANAGED,
&pVB,
NULL ) ;
Contoh di bawah ini adalah penggunaan pembuatan index buffer sebanyak 36 dengan dynamic
buffer dan hanya digunakan untuk penulisan (write only) :
g_pDevice->CreateIndexBuffer (
IndexTotalSize,
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY,
D3DFMT_INDEX16,
D3DPOOL_MANAGED,
&pIB,
NULL ) ;
HRESULT IDirect3DVertexBuffer9::Lock(
UINT OffsetToLock,
UINT SizeToLock,
BYTE** ppbData,
DWORD Flags
);
HRESULT IDirect3DIndexBuffer9::Lock(
UINT OffsetToLock,
UINT SizeToLock,
BYTE** ppbData,
DWORD Flags
);
Parameter Keterangan
92
Buffer Memory
Size To Lock
Offset To Lock +
Offset To Lock
SizeToLock
Gambar 4.1.OffsetTo Lock dan SizeToLock
Contoh di bawah ini menunjukkan cara locking vertex buffer dan mengisi isinya :
pVB->Unlock( ) ;
93
Mendapatkan informasi Vertex Buffer dan Index Buffer
Kadang-kadang kita membutuhkan informasi tentang vertex buffer atau index buffer yang kita telah
buat, bisa dilakukan dengan memanggil method GetDesc dan disimpan ke structure
D3DVERTEXBUFFER_DESC atau D3DINDEXBUFFER_DESC. Snippet di bawah ini menunjukkan cara
menggunakannya :
D3DVERTEXBUFFER_DESC VBDesc ;
D3DINDEXBUFFER_DESC IBDesc ;
pVB->GetDesc( &VBDesc ) ;
pIB->GetDesc( &IBDesc ) ;
Arti dari member structure tersebut sama dengan arti parameter ketika membuat buffer.
Render States
Direct3D menyediakan banyak render state yang memungkinkan kita memodifikasinya yang akan
mempengaruhi bagaimana scene dirender. Render state mempunyai default values, sehingga kita
hanya menggantinya jika diperlukan. Nilai dari render state akan tetap dan akan mempengaruhi
proses render selanjutnya sebelum kita menggantinya dengan nilai baru. Method yang dipakai
adalah IDirect3DDevice9::SetRenderState.
HRESULT IDirect3DDevice9::SetRenderState(
D3DRENDERSTATETYPE State, // state yang akan diganti
DWORD Value // nilai state baru
);
Contoh penggunaan :
untuk dokumentasi lengkap render state silahkan baca di DirectX SDK dengan keyword
D3DRENDERSTATETYPE.
94
Persiapan untuk menggambar
Sekali kita membuat vertex buffer dan (opsional) index buffer, saatnya merender si dari buffer
tersebut ke layar. Tapi ada beberapa hal yang harus dilakukan dahulu
1. Mengeset stream source. Mengeset stream source mengkaitkan vertex buffer ke stream yang
akan mengalirkan data geometri ke rendering pipeline.
HRESULT IDirect3DDevice9::SetStreamSource(
UINT StreamNumber,
IDirect3DVertexBuffer9* pStreamData,
UINT OffsetInBytes,
UINT Stride
);
Parameter Keterangan
Offset dari awal stream dalam byte, menunjukkan awal dari vertex
yang akan dimasukkan ke rendering pipeline. Untuk mengeset
OffsetInBytes
parameter di sini selain 0, maka cek
D3DDEVCAPS2_STREAMOFFSET flag di structure D3DDEVCAPS9.
Ukuran tiap elemen dalam vertex buffer yang akan dimasukkan ke
Stride
stream.
Contoh misal pVB adalah vertex buffer yang sudah diisi dengan vertex dengan tipe Vertex.
2. Set Vertex Format. Di sinilah kita mengeset vertex format dari vertex yang akan digunakan
untuk perintah-perintah rendering di bawahnya.
3. Jika kita menggambar menggunakan index buffer, maka perlu mengeset index buffer. Index
buffer ini akan dipakai pada perintah drawing di bawahnya. Hanya bisa satu index buffer
aktif dalam satu waktu, jika ingin memakai index buffer lain harus diset lagi.
Misalkan index buffer adalah pIB, maka untuk mengesetnya perlu dilakukan perintah :
g_pDevice->SetIndices( pIB ) ;
95
DrawPrimitive atau DrawIndexedPrimitive. Method ini mengambil vertex info dari vertex
stream dan index buffer yang sudah diset sebelumnya
IDirect3DDevice9::DrawPrimitive
Method ini digunakan untuk menggambar primitive tanpa index buffer.
HRESULT IDirect3DDevice9::DrawPrimitive(
D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex,
UINT PrimitiveCount
);
Parameter Keterangan
g_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 4 ) ;
IDirect3DDevice9::DrawIndexedPrimitive
Digunakan untuk menggambar primitive menggunakan index info.
HRESULT IDirect3DDevice9::DrawIndexedPrimitive(
D3DPRIMITIVETYPE Type,
INT BaseVertexIndex,
UINT MinIndex,
UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount
) ;
Parameter Keterangan
96
Index dari element di index buffer yang menandai awal index
StartIndex
buffer yang akan dipergunakan.
Contoh :
g_pDevice->DrawIndexedPrimitive(
D3DPT_TRIANGLELIST,
0,
0,
8,
0,
12 ) ;
Catatan Khusus :
Vertex untuk tiga object disimpan dalam vertex buffer yang berbeda
Vertex untuk tiga object disimpan dalam satu vertex buffer global
Vertex offset
kubus
Vertex offset
sphere Vertex offset
cylinder
Gambar 4.2.Tiga vertex buffer digabung dalam satu global vertex buffer
Jika membuat tiga buah vertex buffer untuk masing-masing benda, kita dengan mudah mengeset
VertexBaseIndex dengan 0, tetapi bayangkan jika kita menggunakan satu vertex buffer global
untuk ketiga benda tersebut, maka kita harus mengeset VertexBaseIndex untuk masing-masing
benda. Jadi kita tidak perlu menghitung kembali nilai index jika kita menggabungkan ketiga buffer
tersebut. Nilai index tetap sama dengan jika satu-satu tetapi menggunakan VertexBaseIndex
sehingga Direct3D menghitung kembali nilai index sebenarnya dalam vertex buffer.
97
Begin dan End Scene
Menggambar primitive harus dilakukan di dalam pasangan IDirect3DDevice9::BeginScene dan
IDirect3DDevice9::EndScene.
if ( SUCCEEDED( g_pDevice->BeginScene( ) ) )
{
g_pDevice->DrawPrimitive( ... ) ;
g_pDevice->EndScene( ) ;
}
D3DX Geometry
Membuat geometri 3D dengan membuat triangle list sendiri amat melelahkan. Untungya D3DX
library menyediakan method untuk men-generate mesh data dari object object 3D sederhana untuk
kita.
• D3DXCreateBox
• D3DXCreateSphere
• D3DXCreateCylinder
• D3DXCreateTeapot
• D3DXCreatePolygon
• D3DXCreateTorus
Membuat geometri 3D dengan membuat triangle list sendiri amat melelahkan. Untungya D3DX
library menyediakan method untuk men-generate mesh data dari object object 3D sederhana untuk
kita.
Semua fungsi tersebut menggunakan D3DX mesh data structure yang disimpan dalam interface
ID3DXMesh.
HRESULT D3DXCreateTeapot(
LPDIRECT3DDEVICE9 pDevice, // device mesh
LPD3DXMESH* ppMesh, // pointer untuk menyimpan mesh
LPD3DXBUFFER* ppAdjacency // 0 untuk sekarang
);
98
Contoh penggunaan untuk membuat teapot :
Setelah mesh data dibuat, kita bisa menggambarnya dengan ID3DXMesh::DrawSubset. Method ini
hanya memakai satu parameter yang merupakan subset dari mesh yang bersangkutan. Mesh yang
dibuat dengan fungsi D3DXCreate* di atas hanya mempunyai satu subset. Contoh dalam
penggunaannya :
if ( SUCCEEDED( g_pDevice->BeginScene( ) ) )
{
pMesh->DrawSubset( 0 ) ;
g_pDevice->EndScene( ) ;
}
Seperti object COM lainnya, setelah dipakai maka mesh harus direlease jika sudah selesai dipakai :
if ( pMesh )
{
pMesh->Release( ) ;
pMesh = NULL ;
}
Let's Code!!
Pada code kali ini ada perombakan yang lumayan besar dalam struktur project kita. Kita akan mulai
menggunakan multiple files supaya project lebih terstruktur. Saya susun file sebagai berikut :
File Keterangan
Kode yang dipakai sama dengan kode untuk membuat window direct3d biru pada bab sebelumnya.
Hanya satu fungsi yang saya ubah namanya yaitu Cleanup, menjadi CleanupDirect3D. Supaya
lebih jelas bagaimana saya merubah struktur project, silahkan buka project skeleton pada directory
Code Samples\Chapter4\Skeleton.
Bila Anda melihat dengan seksama, ada beberapa perubahan dalam kode di skeleton ini antara lain :
99
• Clear color diganti dengan warna putih, supaya object wireframe yang digambar dengan
warna hitam bisa terlihat dengan jelas.
• Icon dan cursor yang dipakai bukan icon dan cursor default melainkan icon dan cursor saya
sendiri.
• Handling untuk WM_KEYUP untuk tombol ESC tidak meng-close window tetapi langsung
keluar aplikasi dengan mengirim WM_QUIT ke window yang bersangkutan.
• Mempunyai menu.
Perubahan-perubahan di atas bisa langsung dilihat di dalam code. Saya tidak akan menerangkan
lebih jauh karena hampir setiap baris dalam sample code sudah dicomment sehingga saya akan
fokus pada Direct3D saja. Jika ingin lebih jelas tentang pemrograman window bisa baca buku
Charles Petzold tentang Windows Programming.
• Cube, sedikit lebih kompleks daripada sample triangle, aplikasi ini menampilkan kubus yang
berputar. Pada aplikasi ini saya memakai index buffer.
• Objects, aplikasi yang merender banyak object dalam satu scene yang dibuat menggunakan
D3DXCreate *.
Untuk lebih jelas, mari kita bahas sample aplikasi yang membuat kubus yang mengajarkan cara
membuat vertex buffer dan index buffer secara lengkap. Untuk menggambar object ini, saya
menambahkan dua fungsi yaitu Setup untuk mempersiapkan seluruh resource yang dibutuhkan dan
cleanup untuk membersihkan resource. Kita hanya memodifikasi pada file Render.h dan
Render.cpp. Kita mendeklarasikan dua fungsi di atas dalam Render.h.
Pada Render.cpp kita deklarasikan vertex buffer, index buffer, structure Vertex , dan vertex format
yang diperlukan. Semua variabel ini adalah static, supaya hanya bisa diakses pada module yang
bersangkutan saja, tidak pada module yang lain :
100
// Vertex Buffer
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL ;
// Index Buffer
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL ;
// Vertex Format
#define VERTEXFORMAT ( D3DFVF_XYZ )
Setelah itu, kita mendefinisikan sebuah array yang berisi vertex dan index dari kubus yang
bersangkutan. Sebenarnya kita bisa langsung menulis di vertex dan index buffer, tetapi resikonya
adalah jika kita memodifikasi kedua vertex tersebut akan sangat lambat sehingga kita buat dulu
sebuah array di system memory, kemudian akan dicopy-kan langsung nanti.
#define VERTICESNUMBER 8
#define INDICESNUMBER 36
#define PRIMITIVESNUMBER 12
const Vertex pVertCube [ VERTICESNUMBER ] =
{
{ -1.0f, -1.0f, -1.0f },
{ -1.0f, 1.0f, -1.0f },
{ 1.0f, 1.0f, -1.0f },
{ 1.0f, -1.0f, -1.0f },
{ -1.0f, -1.0f, 1.0f },
{ -1.0f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f },
} ;
Fungsi Setup menyiapkan dan mengeset matrix view dan projection dan menyiapkan dan mengisi
vertex buffer dan index buffer dengan data vertex dan index di atas, perhatikan codenya secara
lengkap di bawah ini, hampir tiap code mempunyai comment
BOOL Setup()
{
// Matrix
D3DXMATRIX matView ;
D3DXMATRIX matProj ;
101
D3DXVECTOR3 vTarget( 0.0f, 0.0f, 0.0f ) ;
D3DXVECTOR3 vUp( 0.0f, 1.0f, 0.0f ) ;
if ( !g_pDevice )
{
ErrorMessage( TEXT ( "Cannot Create Buffer from NULL Device" ) ) ;
return FALSE ;
}
if ( FAILED ( hr ) )
{
ErrorMessage( TEXT ( "Error Locking Vertex Buffer" ) ) ;
return FALSE ;
}
// Copy data ke Vertex Buffer
memcpy( pVertices, pVertCube, nTotalVertSize ) ;
if ( FAILED ( hr ) )
{
ErrorMessage( TEXT ( "Error Unlocking Vertex Buffer" ) ) ;
return FALSE ;
}
if ( FAILED( hr ) )
{
ErrorMessage( TEXT ( "Error Locking Index Buffer" ) ) ;
}
102
if ( FAILED ( hr ) )
{
ErrorMessage( TEXT ( "Error Unlocking Index Buffer" ) ) ;
return FALSE ;
}
D3DXMatrixPerspectiveFovLH(
&matProj, // projection
D3DX_PI * 0.25f, // 45 derajat
static_cast<float>(g_nWindowWidth) / g_nWindowHeight, // Aspect
1.0f,
1000.0f ) ;
return TRUE ;
}
Pada code yang saya beri warna lebih gelap di situlah proses locking, memcopy, dan unlocking untuk
vertex dan index buffer terjadi. Kode di atas juga mengubah fill mode menjadi wireframe, artinya
digambar kerangkanya saja, tanpa warna, dan tanpa shading.
Untuk DoFrame, tugasnya adalah mengupdate dan menggambar scene ke layar. Kita ingin memutar
kubusnya sehingga kita perlu mengeset world matrix untuk memutar kubus tersebut.Kubus akan
diputar dengan cara menambah sudut perputaran setiap frame.
/************************************************************************/
/* Fungsi Callback DoFrame */
/* Menggambar ke window, untuk saat ini yang dilakukan hanya membersih- */
/* kan layar ke warna putih */
/************************************************************************/
BOOL CALLBACK DoFrame( float DeltaTime )
{
// matrix untuk rotasi
D3DXMATRIX matRotX, matRotY, matRotAll ;
static float rotY = 0.0f ;
// cek apakah device dan direct3d object tidak NULL
if ( !g_pD3D || !g_pDevice )
{
return FALSE ;
}
103
// kombinasikan kedua rotasi tersebut
// Begin Scene
g_pDevice->BeginScene( ) ;
// End Scene
g_pDevice->EndScene( ) ;
return TRUE ;
}
Setelah itu, barulah kita bersihkan index buffer dan vertex buffer di fungsi cleanup.
BOOL Cleanup()
{
// Lepaskan Vertex Buffer
if ( g_pVB )
{
g_pVB->Release( ) ;
g_pVB = NULL ;
}
return TRUE ;
}
Untuk screenshot dari masing-masing sample code bisa dilihat di halaman selanjutnya.
104
Sample Code Screenshot
Segitiga
Kubus
105
Teapot
Bentuk-bentuk
106
Warna
Pada bab sebelumnya kita belajar menggambar object ke layar. Pada bab ini kita akan belajar
mewarnai object ke layar.
Representasi warna
Dalam Direct3D, warna dinyatakan dalam triplet RGB. Kita menentukan nilai untuk warna merah,
hijau, dan biru untuk warna yang bersangkutan.
Ada dua tipe yang dipakai untuk menyimpan data RGB. Pertama adalah D3DCOLOR, yang sebenarnya
merupakan nilai DWORD 32 bit yang dibagi dalam komponen warna masing-masing 1 byte (8 bit).
32 bit
Karena tiap komponen warna berukuran 1 byte maka, range dari tiap warna adalah 0-255. 0 adalah
warna dengan intensitas paling rendah dan 255 adalah intensitas warna paling tinggi.
Menspesifikasikan tiap komponen untuk membentuk warna yang diinginkan membutuhkan operasi
bit. Direct3D menyediakan macro D3DCOLOR_ARGB( a, r, g, b ) untuk membuat nilai 32 bit
dari warna yang bersangkutan. Tiap parameter harus 0-255, contoh :
Mungkin kita tidak memakai komponen alpha, untuk itu kita selalu mengesetnya ke nilai 255. Jika
kita yakin tidak akan memakai alpha, kita bisa memakai macro D3DCOLOR_XRGB( r, g, b )
untuk membentuk warna yang kita inginkan hanya dengan elemen merah, hijau, dan biru. Macro ini
merupakan bentuk singkat dari D3DCOLOR_ARGB( 255, r, g, b ) sebab macro ini didefinisikan
sebagai berikut :
Cara lain untuk merepresentasikan warna adalah dengan struct D3DCOLORVALUE dengan anggota
adalah 4 bilangan float yang berkisar antara 0.0f – 1.0f dengan 1.0f adalah intensitas penuh.
107
Ada juga structure D3DXCOLOR yang isinya sama dengan D3DCOLOR tetapi dengan operator sehingga
untuk mengubahnya amat mudah. Karena mempunyai member yang sama, kita bisa meng-cast
antara D3DCOLOR dan D3DXCOLOR.
Kedua structure di atas mempunyai 4 komponen yaitu r, g, b, dan a. Sehingga analog seperti vector
4D. Vector warna dapat ditambahkan, dikurangkan, dan discaling seperti vector lain. Tetapi dot
product dan cross product tidak berarti apa-apa untuk warna. Sehingga operasi perkalian dalam
anggota class D3DXCOLOR melakukan perkalian komponen. Perkalian komponen mempunyai arti
khusus dalam vector warna. Symbol ⊗ menunjukkan perkalian komponen dari warna sehingga
Mari kita ubah globals.h dan direct3dutil.cpp dengan konstan warna yang umum digunakan.
Globals.h
Direct3Dutil.cpp
Vertex Color
Warna dihitung pervertex, sehingga kita memerlukan nilai warna dalam vertex structure kita.
D3DXCOLORVALUE tidak bisa digunakan di sini karena Direct3D menggunakan nilai 32 bit untuk
menentukan warna dari suatu vertex. ( Sebenarnya dengan vertex shader kita bisa mendapatkan
warna dari 4D color vector dan dengan begitu mendapatkan warna 128 bit tetapi tidak akan di bahas
pada bab ini )
108
Shading
Shading dijalankan saat rasterisasi, dan menunjukkan bagaimana vertex color digunakan untuk
mencari warna semua pixel yang membentuk primitive. Ada dua buah shading mode yaitu flat
shading dan gouroaud shading.
Dengan flat shading, pixel dalam primitive diwarnai menurut pixel pertama dalam primitive
tersebut, jadi misalkan ada segitiga dengan vertexnya sebagai berikut :
Dengan flat shading, segitiga di atas akan diwarnai dengan warna merah, karena itulah warna vertex
pertama. Dalam gouraud shading, segitiga akan diwarnai dengan gradien untuk tiap vertex. Warna
dari pixel2 di dalam primitive diinterpolasi dari warna vertex bersebut. Berikut ini adalah gambaran
flat shading dan gouraud shading dalam aplikasi.
109
D3DCOLOR color ;
} Vertex ;
Kemudian kita deskripsikan segitiga dengan array vertex seperti di bawah ini :
Fungsi Setup() masih seperti pada sample triangle sebelumnya yaitu mempersiapkan vertex buffer
dan projection matrix :
BOOL Setup()
{
if ( !g_pDevice || !g_pD3D )
{
return FALSE ;
}
// Matrix transformasi
D3DXMATRIX matView ;
D3DXMATRIX matProj ;
const D3DXVECTOR3 vEye( 0.0f, 0.0f, -4.5f ) ;
const D3DXVECTOR3 vTarget( 0.0f, 0.0f, 0.0f ) ;
const D3DXVECTOR3 vUp( 0.0f, 1.0f, 0.0f ) ;
// buat VertexBuffer
if ( FAILED( g_pDevice->CreateVertexBuffer( g_nTotalVertSize,
D3DUSAGE_WRITEONLY,
VERTEXFORMAT, D3DPOOL_MANAGED, &g_pVB, NULL ) ) )
{
ErrorMessage( TEXT( "Kesalahan saat membuat Vertex Buffer" ) ) ;
return FALSE ;
}
if ( FAILED ( g_pVB->Unlock( ) ) )
{
ErrorMessage( TEXT( "Error saat unlock buffer" ) ) ;
if ( g_pVB )
{
g_pVB->Release( ) ;
}
110
return FALSE ;
}
// Matikan lighting
// Jika tidak dimatikan, maka akan terlihat hitam
// karena Direct3D mengira tidak ada cahaya
return TRUE ;
}
Kemudian fungsi DoFrame() menggambar triangle dua kali pada posisi yang berbeda dengan shade
mode yang berbeda. Posisi tiap triangle diatur oleh world matrix.
g_pDevice->BeginScene( ) ;
g_pDevice->EndScene( ) ;
111
Sample Code Screenshot
Segitiga
Kubus
Sample ini merupakan sample tambahan yang merupakan modifikasi dari sample kubus pada bab
selanjutnya tetapi dirender menggunakan warna. Ada pilihan untuk mengganti shade mode dan fill
mode. Untuk lebih jelasnya silahkan baca sample code yang disertakan.
112
Lighting/Pencahayaan
Untuk meningkatkan realitas object 3D, tambahkan lighting. Lighting juga menunjukkan object
terlihat pejal/tidak dan volume dari object. Ketika menggunakan lighting, kita tidak menentukan
vertex color sendiri, tetapi membiarkan Direct3D menghitung vertex color berdasarkan light sources,
material, dan normal dari polygon.
Komponen warna
Dalam Direct3D, cahaya yang dikeluarkan dari light source (sumber cahaya) mempunyai tiga
komponen penting.
• Ambient, komponen ini adalah cahaya yang dipantulkan oleh suatu permukaan sehingga
menyebabkan warnanya mempengaruhi warna dari tiap scene.
• Diffuse, komponen ini adalah cahaya yang mengenai suatu permukaan sehingga
memantulkan warna. Cahaya ini dipantulkan ke arah mana saja secara sama. Oleh karena
itu, diffuse tidak mempedulikan tempat dan posisi kamera.
• Specular, komponen ini adalah cahaya yang datang dari sudut tertentu, ketika mengenai
suatu permukaan, cahaya ini dipantulkan hanya ke satu arah. Oleh karena itu, hanya bisa
dilihat di sisi tertentu sehingga posisi kamera penting dalam pencahayaan ini. Contoh
cahaya ini adalah cahaya yang dipantulkan dari permukaan yang mengkilap.
Specular lighting memerlukan komputasi yang lebih berat daripada tipe pencahayaan yang lain.
Secara default, specular dimatikan oleh Direct3D. Jika kita ingin menggunakannya, cukup dengan
menghidupkannya dalam render state.
Keseluruhan nilai dari cahaya, dinyatakan dengan D3DCOLORVALUE atau D3DXCOLOR yang
menentukan warna dari cahaya yang bersangkutan.
Material
Warna dari object yang kita lihat sehari-hari adalah warna yang dipantulkan oleh suatu object.
Contoh kotak yang berwarna merah menyerap semua komponen cahaya kecuali komponen merah.
Komponen merah dipantulkan ke mata sehingga kita bisa melihat bahwa kotak tersebut berwarna
merah. Direct3D mendeskripsikan warna ini sebagai material. Dengan material, kita bisa
menentukan persentase dari warna yang dipantulkan oleh suatu permukaan. Material dinyatakan
dalam structure D3DMATERIAL9.
113
Parameter Keterangan
Sebagai contoh, misalkan kita mempunyai kotak warna merah, kita mendefinisikan kotak untuk
menyerap semua warna kecuali merah.
D3DMATERIAL9 RedBoxMaterial ;
Kita mengeset nilai komponen merah menjadi 1 yang berarti object memantulkan 100% warna
merah. Kemudian warna biru dan hijau masing-masing 0 yang berarti object tidak memantulkan
warna biru dan hijau karena nilainya 0%.
Sebagai catatan, jika kita mendefinisikan light source yang hanya mengeluarkan cahaya biru,
lighting akan gagal karena object sama sekali tidak memantulkan cahaya biru, semua cahaya biru
akan terserap dan dia memantulkan cahaya merah yang bernilai 0, sehingga tidak ada cahaya yang
memantul, dan object terlihat hitam. Object akan terlihat putih jika memantulkan 100% warna
merah, hijau, dan biru.
Karena mengisi structure material amat merepotkan maka kita membuat fungsi di D3DUtil.h untuk
membantu kita menginisialisasi structure ini :
return Material ;
}
Kemudian kita deklarasikan material yang mempunyai warna umum di fungsi Setup() :
114
g_cWhite, g_cBlack, 8.0f ) ;
const D3DMATERIAL9 g_RedMaterial = InitMaterial( g_cRed, g_cRed,
g_cRed, g_cBlack, 8.0f ) ;
const D3DMATERIAL9 g_GreenMaterial = InitMaterial( g_cGreen, g_cGreen,
g_cGreen, g_cBlack, 8.0f ) ;
const D3DMATERIAL9 g_BlueMaterial = InitMaterial( g_cBlue, g_cBlue,
g_cBlue, g_cBlack, 8.0f ) ;
const D3DMATERIAL9 g_YellowMaterial = InitMaterial( g_cYellow, g_cYellow,
g_cYellow, g_cBlack, 8.0f ) ;
Vertex structure tidak mengandung material sehingga material harus diset untuk keseluruhan object.
Saat material diset maka object yang dirender mengikuti material yang telah diset tersebut.
Vertex Normal
Face Normal adalah vector yang menyatakan ke arah mana suatu polygon menghadap.
Vertex Normal hampir sama dengan face normal, akan tetapi berlaku untuk tiap vertex, sedangkan
face normal adalah untuk tiap polygon.
Direct3D perlu mengetahui vertex normal, sehingga bisa menghitung sudut dari mana sebuah cahaya
memantul pada suatu permukaan. Karena lighting dilakukan per vertex, Direct3D memerlukan
informasi vertex normal. Vertex normal tidak harus sama dengan face normal. Pada penggambaran
polygon untuk bola(sphere) dan lingkaran (circle) adalah contoh di mana vertex normal tidak sama
denga face normal.
115
Gambar 6.3. Contoh bentuk yang berbeda vertex normalnya dan face normalnya, yang digambar
dengan warna abu-abu adalah face normal, sedangkan yang warna hitam adalah vertex normal
Untuk mendeskripsikan normal pada vertex, kita harus mengubah vertex structure kita menjadi :
Kita menghapus Vertex color karena warna akan dihitung oleh Direct3D dengan lighting.
Untuk object sederhana seperti kubus dan sphere, kita bisa mencari vertex normal dengan
melihatnya saja. Sedangkan untuk object yang lebih kompleks, kita perlu cara yang lebih kompleks
pula.
Misal kita punya segitiga dengan 3 vertex p0, p1, dan p2. Dan kita perlu menghitung vertex normal
untuk tiap vertex n0, n1, dan n2.
Cara yang paling mudah adalah dengan mencari face normal untuk tiap vertex, dan menggunakan
face normal tersebut sebagai vertex normal. Pertama kita mencari dua vector yang ada di segitiga
tersebut.
G
p1 − p0 = u
G
p2 − p0 = v
n1 = n2 = n3 = n
116
Di bawah ini adalah fungsi dalam bahasa C untuk mencari face normal dari triangle. Fungsi ini
berasumsi bahwa vertex yang dimasukkan diurutkan searah jarum jam, jika tidak, maka normalnya
akan mengarah ke arah yang berlawanan.
Menggunakan face normal sebagai vertex normal tidak menghasilkan pencahayaan yang halus untuk
permukaan melengkung. Cara yang lebih baik adalah menggunakan normal averaging, mencari rata-
rata normal dari segitiga yang menggunakan vertex itu bersama-sama. Jadi kita mencari dulu face
normal dari segitiga-segitiga yang menggunakan vertex yang sama, kemudian membuat rata-rata
normal dari tiap vertex tersebut
Sebagai contoh, misalkan ada 4 segitiga menggunakan vertex yang sama dengan n0, n1, n2, dan n3
adalah normal dari ketiga vertex tersebut, maka rumusnya menjadi :
Light Sources
Direct3D mendukung beberapa tipe light sources sebagai berikut :
• Point Lights, light source ini merupakan light dengan source yang berasa dari satu titik dan
menyebar ke segala arah
• Directional Lights, light source ini tidak mempunyai posisi. Cahayanya paralel.
117
Gambar 6.5. Directional Light
• Spot Lights, sama dengan senter, dengan dua kerucut dengan sudut θ dan φ. θ merupakan
sudut pada kerucut dalam dan φ adalah sudut pada kerucut luar.
Member Keterangan
118
Vector yang menyatakan arah dari light source bersinar. Diabaikan
Direction
untuk point lights
Range maximum untuk cahaya sebelum cahaya mati. Tidak bisa lebih
Range besar dari FLT _ MAX dan tidak mempunyai efek dengan
directional lights.
Hanya digunakan untuk spotlight. Menyatakan bagaimana intensitas
Falloff cahaya melemah dari kerucut dalam ke kerucut luar. Biasanya diset
ke 1.0f.
Menyatakan bagaimana cahaya melemah seiring dengan jarak. Hanya
digunakan untuk point dan spot lights. Attenuation0 adalah falloff
constant, Attenuation1 adalah linear falloff, dan Attenuation2
Attenuation0, adalah quadratic falloff. Dengan rumus ini, jika D adalah jarak dari
Attenuation1, light source dan A0, A1, dan A2 adalah nilai attenuation. Maka
Attenuation2 1
Attenuation =
A0 + A1.D + A2 .D 2
Seperti inisialisasi material, menginisialisasi structure D3DLIGHT9 akan sangat merepotkan jika kita
hanya ingin pencahayaan sederhana, untuk itu kita buat fungsi untuk menginisialisasinya.
Implementasinya cukup mudah, saya hanya akan memperlihatkan untuk implementasi directional
light.
light.Type = D3DCOLOR_DIRECTIONAL ;
light.Ambient = color * 0.4f ;
light.Diffuse = color ;
light.Specular = color * 0.6f ;
light.Direction = direction ;
return light ;
}
Sehingga jika kita ingin membuat directional light yang paralel dengan sumbu x positif, maka kita
akan memanggil fungsinya sebagai berikut :
119
Setelah kita menginisialisasi instance D3DLIGHT9, kita daftarkan ke Direct3D.
1. Hidupkan Lighting
2. Buat material untuk setiap object dan set material sebelum object dirender
3. Buat satu atau lebih light sources, set mereka, dan hidupkan
Seperti biasa, kita menyimpan vertex dari pyramid ke vertex buffer g_pVB. Lighting dienable secara
default oleh Direct3D, akan tetapi untuk lebih memastikan kita tambahkan method untuk
mengaktifkan lighting sebagai berikut pada fungsi Setup() :
kemudian kita deklarasikan vertex posisi dan normal dari piramid pada variabel global
120
// Structure dari vertex buffer
struct Vertex
{
float x, y, z ;
float nx, ny, nz ;
};
// Vertex Format
#define VERTEXFORMAT ( D3DFVF_XYZ | D3DFVF_NORMAL )
Baru kita membuat vertex buffer dan mengcopy datanya ke vertex buffer
if ( FAILED ( hr ) )
{
ErrorMessage( TEXT ( "Error Locking Vertex Buffer" ) ) ;
return FALSE ;
}
// Copy data ke Vertex Buffer
memcpy( pVertices, pVertPyramid, nTotalVertSize ) ;
if ( FAILED ( hr ) )
{
ErrorMessage( TEXT ( "Error Unlocking Vertex Buffer" ) ) ;
return FALSE ;
}
Kemudian kita set material ke putih (material putih sudah kita deklarasikan) :
// set material
g_pDevice->SetMaterial( &g_WhiteMaterial ) ;
121
dan langkah terakhir adalah mengeset dan mengaktifkan directional light :
// directional light
lightDir = InitDirectionalLight( D3DXVECTOR3( 1.0f, 0.0f, 0.0f ),
g_cWhite ) ;
// hidupkan lighting
g_pDevice->SetRenderState( D3DRS_LIGHTING, TRUE ) ;
g_pDevice->SetLight( 0, &lightDir ) ;
g_pDevice->LightEnable( 0, TRUE ) ;
Untuk fungsi DoFrame() isinya hanya menggambar vertex buffer ke layar. Yang berubah hanyalah
background saja menjadi hitam supaya efek lighting terlihat lebih jelas.
Untuk sample yang lain ada penambahan pada fungsi DoFrame() yaitu penanganan keyboard.
Kamera bisa diputar dengan tombol panah dengan mendeklarasikan sudut dan tinggi kemudian
mengubahnya ketika tombol dipencet lalu diterapkan dalam view matrix. Seperti ini :
if ( !g_pDevice || !g_pD3D )
{
return FALSE ;
}
//
// Terapkan dalam view matrix
//
D3DXMATRIX matView ;
D3DXMatrixLookAtLH( &matView, &vPos, &vTarget, &vUp ) ;
122
Sample Code Screenshot
Pyramid
Directional Lights
123
Point Lights
Spot Lights
124
Texturing
Texture mapping adalah teknik yang menempelkan image ke segitiga untuk meningkatkan scene
supaya lebih realistis.Contoh nyata adalah pada kotak beton sebagai berikut :
Texture Coordinate
Texture coordinate dalam Direct3D dinyatakan dengan vector u horizontal dan v yang vertikal. u
positif ke kanan, dan v positif ke bawah :
Interval antara 0.0f dan 1.0f menjadikan Direct3D dapat bekerja pada texture dengan ukuran
bermacam-macam. Mari kita lihat cara mapping suatu texture ke dalam suatu triangle.
125
Gambar 7.3. Texture Mapping, segitiga 3 dimensi di-map ke dalam texture.
// Vertex Format
#define VERTEXFORMAT ( D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 )
Kita menambahkan D3DFVF_TEX1 yang berarti structure vertex kita mempunyai sepasang texture
coordinate.
HRESULT D3DXCreateTextureFromFile(
LPDIRECT3DDEVICE9 pDevice,
LPCTSTR pSrcFile,
LPDIRECT3DTEXTURE9 * ppTexture
);
Fungsi di atas bisa me-load file BMP, DDS, DIB, JPG, PNG, dan TGA. Contoh, untuk me-load file
Metal.dds sebagai texture :
126
HRESULT IDirect3DDevice9::SetTexture(
DWORD Stage, // nilai antara 0-7 sebagai identifikasi texture stage
IDirect3DBaseTexture9* pTexture // pointer ke texture )
;
Contoh :
g_pDevice->SetTexture( 0, g_pMetalTex ) ;
Untuk mematikan texture pada texturing stage, tinggal kita set ke NULL :
g_pDevice->SetTexture( 0, NULL ) ;
RenderObjectTanpaTexture( ) ;
Jika kita ingin men-texture triangle dengan texture yang berbeda-beda kita tinggal mengganti
texture sebelum menggambar object :
g_pDevice->SetTexture( 0, g_pMetalTex ) ;
RenderTrianglesWithMetals( ) ;
g_pDevice->SetTexture( 0, g_pGrassTex ) ;
RenderTrianglesWithGrass( ) ;
Filters
Texture di-mapping pada screen space, sehingga ukuran texture triangle tentu tidak akan sama
dengan screen triangle. Jika texture triangle lebih kecil texture dimagnified supaya pas. Jika
texture triangle lebih besar, texture diminified supaya pas. Pada bebarapa kasus, akan terjadi
distorsi. Untuk mengatasi distorsi, Direct3D menggunakan teknik filtering.
Ada 3 buah jenis filtering yang disediakan oleh Direct3D, kualitas selalu berbanding terbalik dengan
performa, semakin bagus kualitas, semakin lambatlah performa aplikasinya, sehingga perlu
dipikirkan keseimbangan antara kualitas dan performa. Texture filters diset menggunakan method
IDirect3DDevice9::SetSamplerState.
• Nearest-point sampling, merupakan filtering paling jelek dan merupakan default dari
Direct3D akan tetapi paling cepat dihitung oleh hardware. Kode di bawah ini mengeset filter
ke nearest point untunk magnification dan minification.
• Linear Filtering, filtering dengan hasil lumayan bagus, dan bisa dilakukan dengan cepat
pada hardware zaman sekarang. Disarankan minimal menggunakan linear fitering. Kode di
bawah ini mengeset ke linear filtering untuk magnification dan minification.
• Anisotropic Filtering, filtering dengan hasil paling bagus, tetapi juga palin berat
komputasinya. Kode di bawah ini mengeset ke anisotropic filtering untuk magnification dan
minification.
127
g_pDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC ) ;
g_pDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC ) ;
g_pDevice->SetSamplerState( 0, D3DSAMP_MAXANISOTROPY, 4 ) ;
Mipmap
Seperti yang telah dibahas bahwa screen triangle beda ukuran dengan texture triangle. Untuk
membuat supaya beda ukuran tidak begitu besar, kita harus membuat mipmap. Mipmap adalah
texture dengan ukuran yang berbeda-beda (biasanya square) pada tiap levelnya, sehingga kita bisa
menerapkan filtering pada tiap level.
Gambar 7.4. Contoh mipmapping, perhatikan ukuran dari tiap mip level adalah separuh dari ukuran
level sebelumnya.
Mipmap filter
Mipmap filter digunakan untuk mengkontrol Direct3D untuk membuat mipmap. Cukup menggunakan
SetSamplerState dengan D3DSAMP_MIPFILTER sebagai state yang diset. Contoh jika
menggunakan nearest point sebagai filter mipmapnya :
Nilai Keterangan
Dengan filter ini Direct3D akan memilih mipmap level yang paling
D3DTEXF_POINT dekat dengan ukuran screen triangle, begitu dipilih, Direct3D akan
memfilter tergantung dari min dan mag-nya.
Dengan filter ini, Direct3D memilih dua buah mipmap yang paling
D3DTEXF_LINEAR
dekat, memfilternya kemudian mengkombinasikan keduanya
128
Menggunakan mipmap dalam Direct3D
Menggunakan mipmap dalam Direct3D sangat mudah. Dengan memanggil
D3DXCreateTextureFromFile akan membuat mipmap secara otomatis IDirect3DTexture9
akan memilih texture level yang pas secara otomatis.
Address Mode
Pada bab di atas kita mengeset koordinat texture antara 0 sampai dengan 1. Tidak selamanya begitu,
koordinat texture bisa lebih dari itu. Hasil dari koordinat yang lebih dari 1.0f tersebut diatur oleh
Direct3D dalam Address Mode. Ada 4 buah address mode ditampilkan dalam screenshot sebagai
berikut :
129
Gambar 7.7. Clamp Address Mode
Untuk beralih dari satu address mode ke address mode lain cukup mudah, yaitu dengan memanggil
SetSamplerState, dengan mengubah state untuk D3DSAMP_ADDRESSU untuk addressing mode
horizontal, dan D3DSAMP_ADDRESSV untuk addressing mode vertikal. Contoh :
130
Let's Code The Textured Quad
Ada banyak hal yang berubah dalam program, terutama pada fungsi tambahan GetExePath yang
berguna untuk mendapatkan path dari file .exe untuk mengambil texture agar texture diletakkan
satu directory dengan file .exe yang dikompilasi. Untuk listing penuh, silahkan baca di GetExePath.
Untuk mendapatkan Exe path ini, kita memerlukan satu dependensi lagi yaitu shlwapi.lib.
tambahkan di bagian input linker.
Textured quad terdiri dari 2 segitiga dan 4 vertex. Oleh karena itu, untuk menghemat tempat, saya
memakai vertex dan index buffer, sehingga tidak memerlukan buffer vertex yang besar. Seperti
biasa, jumlah primitif, fvf, dan ukuran-ukuran saya simpan di konstanta atau macros :
// Vertex Buffer
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL ;
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL ;
static LPDIRECT3DTEXTURE9 g_pTex = NULL ;
static TCHAR g_szExePath[ MAX_PATH ] ;
// Structure dari vertex buffer
struct Vertex
{
float x, y, z ;
float nx, ny, nz ;
float u, v ;
};
// Vertex Format
#define VERTEXFORMAT ( D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 )
Perhatikan vertex structure, normal semuanya mengarah ke Z negatif (keluar layar), dan sepasang
angka terakhir adalah texture coordinates. Setelah disiapkan seperti di atas, saatnya membuat
vertex dan index buffer kemudian menampung seluruh mesh quad dan juga sekalian mengeset
matrix view dan projection di fungsi Setup( ). Fungsi ini selain menyiapkan mesh, juga
menyiapkan texture yang akan dipakai. Me-load, sekaligus menerapkan texture yang dipakai. Karena
hanya satu texture dan tidak berganti-ganti, saya meletakkan fungsi pembuatan texture di sini.
131
BOOL Setup()
{
//
// Set Matrix View dan Projection
//
D3DXMATRIX matView, matProj ;
const D3DXVECTOR3 vEye ( 0.0f, 0.0f, -3.0f ) ;
const D3DXVECTOR3 vUp ( 0.0f, 1.0f, 0.0f ) ;
const D3DXVECTOR3 vTarget( 0.0f, 0.0f, 0.0f ) ;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI * .25f,
static_cast<float>(g_nWindowWidth)/g_nWindowHeight, 1.0f, 1000.0f ) ;
D3DXMatrixLookAtLH( &matView, &vEye, &vTarget, &vUp ) ;
g_pVB->Unlock( ) ;
g_pIB->Unlock( ) ;
GetExePath( g_szExePath ) ;
_tcscat( g_szExePath, TEXT( "Kyoko.dds" ) ) ;
//
// Make Texture for first time
//
132
g_pDevice->SetTexture( 0, g_pTex ) ;
g_pDevice->SetIndices( g_pIB ) ;
return TRUE ;
}
Jangan lupa mematikan lighting. Kita hanya ingin melihat kotak ditexture saja, tanpa lighting.
Untuk itu, kita matikan lighting. Untuk fungsi DoFrame(), hanya berisi fungsi untuk membersihkan
dan menampilkan quad.
g_pDevice->Clear( 0, NULL,
D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
D3DCOLOR_XRGB( 0xff, 0xff, 0xff ), 1.0f, 0 ) ;
if ( FAILED( g_pDevice->BeginScene( ) ) )
{
return FALSE ;
}
g_pDevice->EndScene( ) ;
g_pDevice->Present( NULL, NULL, NULL, NULL ) ;
return TRUE ;
}
Untuk fungsi cleanup, hanya menambahkan pemanggilan method Release() untuk g_pTex.
BOOL Cleanup()
{
//lepaskan index
if ( g_pIB )
{
g_pIB->Release( ) ;
g_pIB = NULL ;
}
// Lepaskan Vertex Buffer
if ( g_pVB )
{
g_pVB->Release( ) ;
g_pVB = NULL ;
}
// Lepaskan texture
if ( g_pTex )
{
g_pTex->Release( ) ;
g_pTex = NULL ;
133
}
return TRUE ;
}
134
Samples ScreenShot
Quad Plain
Plain Quad.. dengan texture saja.
TexQuad
Textured perspective quad dengan pilihan texturing :
135
CubeTex
Textured perspective cube
136
Multitexturing
Multitexturing adalah kemampuan untuk menerapkan lebih dari 1 texture pada satu triangle. Tidak
semua hardware mendukung fasilitas ini. Untuk itulah perlu mengecek structure D3DCAPS9 pada
saat inisialisasi Direct3D. Berapa maximum texture yang bisa dipakai bersamaan, dan berapa
maximum blending yang bisa dipakai. Umumnya pada hardware modern support hingga 8 texture
sekaligus.
Yang perlu diperhatikan dalam rendering multitexture adalah color operation. Pada contoh di sub-
bab ini saya memakai 3 buah texture :
2. Texture serat dengan alpha channel masing-masing 50% dimodulasi dengan texture 1.
3. Texture yang hanya berupa alpha channel saja untuk dimodulasi dengan texture 1 dan 2.
Untuk membuat dan mengaktifkan ketiga texture tersebut, kita masukkan masing-masing dalam
stage 0, 1, dan 2.
//
// Create First Texture
if ( FAILED( D3DXCreateTextureFromFile( g_pDevice,
g_szTexName, &g_pTex ) ) )
{
ErrorMessage( TEXT( "Error Creating Texture! " ) ) ;
return FALSE ;
}
//
// Create Second Texture
if ( FAILED( D3DXCreateTextureFromFile( g_pDevice,
g_szTexDetailName, &g_pTexDetail ) ) )
{
ErrorMessage( TEXT( "Error Creating Detail Texture " ) ) ;
return FALSE ;
}
//
// Create Third Texture
if ( FAILED( D3DXCreateTextureFromFile( g_pDevice,
g_szTexAlphaName, &g_pTexAlpha ) ) )
{
ErrorMessage( TEXT( "Error Creating Alpha Channel! " ) ) ;
return FALSE ;
}
g_pDevice->SetTexture( 0, g_pTex ) ;
g_pDevice->SetTexture( 1, g_pTexDetail ) ;
g_pDevice->SetTexture( 2, g_pTexAlpha ) ;
Kemudian supaya device melakukan operasi modulasi, maka kita perlu menghidupkan Stage
Statesnya dengan memanggil IDirect3DDevice9::SetTextureStageState :
Dengan begitu maka stage 1 yang berisi detail akan dimodulasi dengan texture 0, kemudian hasil
tersebut akan di-blend dengan alpha dari texture 2.
137
Untuk Argumen dari SetTextureStageState dapat dilihat di dokumentasi DirectX SDK Anda, termasuk
rumus-rumus modulasi tersebut. Kode lengkap bisa dilihat di sample.
138
Blending
Pada bab ini, kita akan membahas tentang blending yaitu mengkombinasikan pixel yang sudah
diraster dengan pixel yang datang. Dengan blending, membuat kita bisa mengaplikasikan efek
misalnya transparansi.
Persamaan Blending
Mari kita lihat dua buah gambar, yang satu adalah torus digambar di atas background quad dengan
texture kyoko :
139
Bagaimana cara mendapatkan hasil tersebut? Kita meraster torus setelah menggambar background.
Kita mengkombinasikan warna dari torus dengan warna background sehingga warna background
dapat terlihat tembus pada torus. Kombinasi dari source pixel (pixel yang sedang dihitung) dengan
destination pixel (pixel yang sudah ada di back buffer) disebut blending. Blending tidak hanya
dengan transparansi seperti di atas, tetapi masih banyak lagi.
Yang terpenting adalah mengingat bahwa triangle yang sedang diraster di-blend dengan pixel yang
sudah ada di backbuffer. Pada gambar contoh, gambar kyoko digambar dulu, baru kemudian torus.
Jadi aturan di bawah ini berlaku jika ingin menggambar dengan blending :
Gambar object yang tidak menggunakan blending dahulu, kemudian urutkan penggambaran dari
yang terjauh dari camera. Dapat dengan mudah dilakukan di view space, jadi tinggal
mengurutkan menurut komponen Z. Kemudian gambar object yang memerlukan blending dari
belakang ke depan.
Tiap pixel di atas dinyatakan dalam vector 4D (r, g, b, a ) dan simbol ⊗ adalah simbol perkalian tiap
komponen.
Variabel Keterangan
Pixel yang sedang dihitung (pixel yang datang) yang akan di-blend
SourcePixel
dengan backbuffer nanti.
Nilai dalam interval [0, 1] yang menyatakan persentase source pixel
SourceBlendFactor
yang diblend.
Dengan memodifikasi SourceBlendFactor dan DestBlendFactor dengan suatu nilai tertentu, maka
kita dapat memperoleh banyak efek.
Blending adalah mati secara default, sehingga perlu dihidupkan dengan SetRenderState :
Catatan Khusus :
Blending merupakan suatu operasi yang cukup memakan performa, oleh karena itu,
hidupkanlah hanya untuk geometri yang membutuhkan, jadikan satu segitiga-segitiga yang
membutuhkan blending, sehingga tidak menghidupkan dan mematikan blending berkali-kali
dalam satu frame.
140
Blend Factor
Dengan menyetting kombinasi yang berbeda antara source dan destination factor, kita bisa
mendapatkan berbagai macam efek blending. Cukup dengan memanggil SetRenderState sbb :
g_pDevice->SetRenderState(D3DRS_SRCBLEND, SourceFactor);
g_pDevice->SetRenderState(D3DRS_DESTBLEND, DestinationFactor);
Factor Keterangan
D3DBLEND_ZERO Blendfactor = ( 0, 0, 0, 0 )
D3DBLEND_ONE Blendfactor = ( 1, 1, 1, 1 )
Transparansi
Pada bab-bab sebelumnya kita mengabaikan komponen alpha (alpha channel). Komponen alpha ini
paling banyak digunakan untuk blending. Akan tetapi komponen alpha ini juga diinterpolasi saat
rasterisasi seperti warna.
Komponen alpha paling banyak digunakan untuk menyatakan transparansi suatu pixel. Misal kita
mengalokasikan 8 bit untuk alpha, maka nilai yang valid adalah pada interval [0, 255]. Dengan [0,
255] adalah ke-opaque-an [0%,100%]. Jadi pixel dengan nilai alpha 0 adalah 100% transparan, dan
dengan nilai nilai 255 adalah 100% opaque.
141
Supaya komponen alpha menjadi nilai yang menyatakan transparansi dari suatu pixel, kita mengeset
source blend ke D3DBLEND_SRCALPHA dan destination blend ke D3DBLEND_INVSRCALPHA. Kedua
value tersebut adalah default blend factor.
Alpha Channels
Daripada menggunakan nilai alpha yang diinterpolasi, kita bisa mendapatkan alpha dari texture
alpha channels. Alpha channels adalah sebuah bit yang dimasukkan di tiap pixel yang menyimpan
komponen alpha. Ketika texture di-mapping, maka alpha juga dimap pada pixel yang bersangkutan.
Gambar di bawah ini menunjukkan hasil rendering RGB Channel ditambah alpha channel.
142
Menggunakan DirectX Texture Tool untuk Alpha Channel
Format image yang umum biasanya tidak menyimpan informasi alpha. Pada seksi ini akan
diterangkan cara menggunakan alpha channel pada file DDS. File DDS adalah singkatan dari
DirectDraw Surface dan biasa digunakan untuk texture dalam aplikasi DirectX, file DDS ini bisa
diload dengan D3DXLoadTextureFromFile.
Buka DirectX Texture Tool dan buka texture kyoko.jpg. Kemudian ubah formatnya dari X8R8G8B8
menjadi A8R8G8B8 dengan memilih menu Format > Change Surface Format, pilih Unsigned
32bit : A8R8G8B8 kemudian klik OK.
Ini akan membuat image dengan 8 bit alpha 8 bit red 8 bit green dan 8 bit blue. Simpan dengan
nama kyoko.dds. Untuk menerapkan alpha channel, pilih menu File > Open Onto Alpha Channel of
This Surface. Kemudian pilih image hitam putih tadi yang merupakan alpha channel kita. Setelah
di-save, kemudian dibuka lagi maka texture akan berubah menjadi seperti di bawah ini :
143
Gambar 8.6. texture dengan alpha channel
Pertama-tama kita siapkan vertex dan index buffer untuk background dan mesh buffer untuk torus.
// Vertex Buffer
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL ;
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL ;
static LPDIRECT3DTEXTURE9 g_pTex = NULL ;
static LPD3DXMESH g_pMeshTorus = NULL ;
static D3DMATERIAL9 g_MaterialTorus = g_BlueMaterial ;
static D3DMATERIAL9 g_MaterialBack = g_WhiteMaterial ;
Pada fungsi Setup(), kita buat torus dan mengeset diffuse color sebagai asal alpha :
BOOL Setup() {
// -- setting projection, view, lighting, texture,
// -- vertex dan index buffer untuk background dihilangkan
//
// Creating Torus
//
if ( FAILED( D3DXCreateTorus( g_pDevice, 0.5f, 1.0f,
144
32,32, &g_pMeshTorus, NULL ) ) )
{
return FALSE ;
}
g_MaterialTorus.Diffuse.a = 0.5f ;
//
// Setting Torus Blending Mode (using diffuse alpha as argument)
//
g_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ) ;
g_pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ) ;
return TRUE ;
}
Pada fungsi menggambar, kita menggambar background dahulu dengan matrix world identitas,
kemudian menggambar torus dengan memutarnya.
//
// Cek key to increase alpha [A] or decrease alpha [S]
//
//
// World Transformation Matrices
//
D3DXMatrixIdentity( &matWorld ) ;
145
//
// Clear Target, depth, and stencil
//
g_pDevice->Clear( 0, NULL,
D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
D3DCOLOR_XRGB( 0xff, 0xff, 0xff ), 1.0f, 0 ) ;
if ( FAILED( g_pDevice->BeginScene( ) ) )
{
return FALSE ;
}
//
// Draw The KYOKO Background
//
g_pDevice->SetStreamSource( 0, g_pVB, 0, sizeof( Vertex ) ) ;
g_pDevice->SetIndices( g_pIB ) ;
g_pDevice->SetFVF( VERTEXFORMAT ) ;
g_pDevice->SetTexture( 0, g_pTex ) ;
g_pDevice->SetMaterial( &g_MaterialBack ) ;
g_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0,
VERTICESNUMBER, 0, PRIMITIVESNUMBER ) ;
//
// Draw and rotate The Transparent Torus
//
D3DXMatrixRotationX( &matRotX, D3DX_PI * 0.15f ) ;
D3DXMatrixRotationY( &matRotY, rotY ) ;
matWorld = matRotX * matRotY ;
g_pDevice->SetTransform( D3DTS_WORLD, &matWorld ) ;
g_pMeshTorus->DrawSubset( 0 ) ;
g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ) ;
g_pDevice->EndScene( ) ;
return TRUE ;
}
146
Sample ScreenShot
Material Blend
Mengeblend backdrop dengan torus transparan.
Gambar 8.7. Blending backdrop dengan texture yang sudah diberi alpha channel
147
Stenciling
Stencil buffer adalah sebuah off-screen buffer seperti depth buffer yang digunakan untuk
mendapatkan efek tertentu. Stencil buffer mempunyai resolusi yang sama dengan back buffer
sehingga pixel ij di dalam stencil buffer sama dengan pixel ij di back buffer. Seperti namanya,
stencil buffer bekerja layaknya stencil yaitu memblok rendering pada area tertentu dalam back
buffer.
Contoh, misal kita ingin mengimplementasikan kaca/mirror. Kita hanya memerlukan refleksi pada
kaca saja, bukan pada bagian lain. Kita bisa menggunakan stencil buffer untuk memblok rendering
refleksi benda kecuali ke kaca.
Untuk lebih jelasnya silahkan lihat pada perbedaan dua gambar berikut ini, yang satu menggambar
refleksi tanpa stencil, yang kedual menggambar refleksi dengan stencil, sehingga hanya yang di kaca
yang digambar.
148
Gambar 9.2. Mirror dengan Stencil Dihidupkan
Stencil buffer adalah seperti depth buffer, dapat dikontrol dengan interface tertentu. Seperti
blending, interface tersebut menawarkan kemampuan yang fleksibel dan sangat powerful.
Menggunakan stencil buffer paling gampang adalah dengan mempelajari aplikasi yang sudah ada.
Oleh karena itu, saya menekankan pada contoh dua implementasi stencil buffer yaitu mirror
(refleksi) dan bayangan (shadow).
g_pDevice->Clear( 0, NULL,
D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
0xff000000, 1.0f, 0U ) ;
149
Kita memasukkan D3DCLEAR_STENCIL dan untuk argumen keenam artinya kita bersihkan stencil
buffer ke nilai 0.
Format Keterangan
Stencil Test
Seperti yang dibahas sebelumnya, kita bisa menggunakan stencil buffer untuk mencegah rendering
pada area tertentu. Caranya adalah dengan stencil test. Yang dinyatakan dengan :
Stencil test dilakukan di setiap pixel. Dengan asumsi stencil diaktifkan, dengan dua operands :
• Nilai di sebelah kiri ( LHS ) ditentukan dengan meng-AND-kan stencil reference value yang
kita tentukan dengan mask yang kita tentukan.
• Nilai di sebelah kanan ( RHS ) ditentukan dengan meng-AND-kan nilai di stencil buffer
dengan mask yang kita tentukan.
Stencil test akan membandingkan LHS dan RHS dengan suatu comparation operation. Ekspresi di
atas akan menghasilkan nilai true atau false. Kita menggambar pixel di backbuffer jika hasilnya
adalah true (stencil pass) dan tidak menggambar pixel di backbuffer jika hasilnya adalah false
(stencil fail). Karena pixel tidak ditulis di backbuffer, maka tidak ditulis pula di depth buffer.
Secara default stencil reference value diset ke 0. akan tetapi kita bisa menggantinya dengan
mengubah render state D3DRS_STENCILREF.
kita memakai bilangan hexadecimal supaya mudah dalam melihatkan bit alignment dan hasil and.
150
Stencil Mask
Stencil masking value adalah suatu nilai yang digunakan untuk me-masking ref dan value. Mask
default adalah 0xffffffff yang berarti tidak me-mask bit manapun. Kita bisa menggantinya
dengan mengganti render state D3DRS_STENCILMASK. Contoh di bawah ini me-mask 16 bit
pertama.
Stencil Value
Stencil value adalah nilai yang ada dalam stencil buffer yang akan kita test. Contoh, jika kita
melakukan testing pada pixel ij, maka value pada ekspresi di atas adalah nilai stencil buffer untuk
pixel ij. Kita tidak bisa mengeset nilai stencil satu persatu, akan tetapi kita bisa meng-clear stencil
buffer seluruhnya ke nilai tertentu.
Comparison Operation
Kita bisa mengeset comparison operation dari stencil test dengan mengubah
D3DRS_STENCILFUNC. Nilainya bisa salah satu dari enumerasi D3DCMPFUNC berikut ini :
151
Mengupdate stencil buffer
Selain menentukan apakah akan menggambar dalam pixel tertentu atau tidak, kita bisa menentukan
bagaimana nilai dalam stencil buffer diupdate dengan mengeset tiga buah kemungkinan
• Jika stencil test fail di pixel ij. Kita bisa menentukan bagaimana nilai stencil buffer diubah
dengan mengeset nilai D3DRS_STENCILFAIL.
• Jika depth test fail di pixel ij. Kita bisa menentukan bagaimana nilai stencil buffer diubah
dengan mengeset nilai D3DRS_STENCILZFAIL.
• Jika depth test dan stencil test fail di pixel ij, kita bisa menentukan bagaimana nilai stencil
buffer diubah dengan mengeset nilai D3DRS_STENCILPASS
dengan StencilOperation adalah salah satu dari nilai konstanta di bawah ini
Nilai Keterangan
152
Aplikasi I : Mirror
Banyak permukaan di dunia nyata yang bisa memantulkan bayangan benda. Akan tetapi kita di sini
hanya membahas permukaan yang merupakan bidang yang memantulkan bayangan benda, bukan
permukaan semacam mobil atau kendaraan yang mengkilap yang memantulkan bayangan. Oleh
karena itu, aplikasi kita disebut planar reflection, karena refleksi terjadi pada bidang (plane).
Sekarang kita akan menunjukkan bagaimana cara merefleksikan point v' = ( v'x, v'y, v'z ) dari v = ( vx,
ˆ •p + d = 0
vy, vz ) terhadap bidang n
V’
-2kn n
q kn
G
n̂ • p + d
ˆ • p + d = 0 menghasilkan v'
Gambar 9.3. Refleksi v terhadap bidang n
G
ˆ dengan k adalah jarak terdekat antara v
Dari materi bidang di bab I, kita dapatkan q = v -kn
( )
ˆ, d didapat dengan persamaan berikut ini :
dengan bidang. Dengan begitu, refleksi v pada bidang n
v ' = v − 2knˆ
= v − 2(nˆ • v + d )nˆ
= v − 2[(nˆ • v )nˆ + dn
ˆ]
Dengan begitu kita bisa mendapatkan matrix transformasi untuk refleksi R sebagai berikut :
D3DXMATRIX *D3DXMatrixReflect(
D3DXMATRIX *pOut, // Matrix hasil
CONST D3DXPLANE *pPlane // Bidang untuk merefleksikan
);
153
Berikut ini adalah matrix refleksi spesial yang merefleksikan pada bidang yz, xz, dan xy dengan
matrixnya seperti di bawah ini :
⎛ −1 0 0 0⎞ ⎛1 0 0 0⎞ ⎛1 0 0 0⎞
⎜ ⎟ ⎜ ⎟ ⎜ ⎟
0 1 0 0⎟ 0 −1 0 0⎟ 0 1 0 0⎟
Ryz =⎜ R xz = ⎜ R xy = ⎜
⎜0 0 1 0⎟ ⎜0 0 1 0⎟ ⎜ 0 0 −1 0⎟
⎜ ⎟ ⎜ ⎟ ⎜ ⎟
⎝0 0 0 1⎠ ⎝0 0 0 1⎠ ⎝0 0 0 1⎠
1. Render scene normal, seluruhnya, kecuali bayangan object. Langkah ini tidak mengubah
stencil buffer.
2. Hapus stencil buffer ke 0. Gambar di bawah ini menggambarkan keadaan backbuffer dan
stencil buffer :
Gambar 9.4. Keadaan back buffer dan stencil buffer setelah stencil buffer diclear menjadi 0
3. Gambar primitives yang merupakan mirror hanya ke stencil buffer. Set stencil test supaya
selalu sukses dan set isi stencil buffer (replace) menjadi nilai ref yaitu 1 jika stencil pass.
Karena kita hanya menggambar mirror saja, maka seluruh isi stencil buffer berisi 0 kecuali
stencil buffer sehingga kira-kira menjadi sebagai berikut :
Gambar 9.5. Keadaan back buffer dan stencil buffer setelah render mirror ke stencil
154
4. Sekarang kita gambar refleksi dari object ke back buffer dan stencil buffer. Akan tetapi,
kita hanya akan merender ke backbuffer jika stencil pass. Sekarang kita set supaya
Bagian 1
Bagian ini adalah mengaktifkan stencil buffer dan render state yang dibutuhkan :
penjelasannya cukup sederhana, kita aktifkan Stencil Buffer, yang berarti stencil test selalu pass.
Jika depth fail kita keep nilai value, karena kita tidak ingin merender pixel yang tertutup karena z
fail. Kita juga menggunakan D3DSTENCILOP_KEEP untuk stencil fail. State ini tidak pernah terjadi
karena kita mengeset D3DCMP_ALWAYS untuk stencil function.
Jika depth dan stencil pass kita gunakan D3DSTENCILOP_REPLACE untuk mengganti nilai stencil ke
nilai reference yaitu 0x1.
Bagian 2
Bagian selanjutnya merender mirror, tetapi hanya ke stencil buffer. Kita hentikan menulis ke depth
buffer dengan mengeset D3DRS_ZWRITENABLE ke FALSE. Kita bisa menghentikan update ke
backbuffer dengan mengeset source blend ke D3DBLEND_ZERO dan destination ke D3DBLEND_ONE.
Dengan menggunakan factor ke blending kita dapatkan persamaan berikut :
155
D3DXMATRIX matIdentity ;
D3DXMatrixIdentity( &matIdentity ) ;
g_pDevice->SetTransform( D3DTS_WORLD, &matIdentity ) ;
g_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 12, 0, 4, 0, 2 ) ;
Bagian 3
Pada bagian ini, pixel dalam stencil buffer yang merupakan pixel yang terlihat diset ke 0x1 sehingga
menandai itu adalah area tempat mirror berada. Sekarang kita merender bayangan refleksi dari
object teapot, kita bisa melakukannya dengan mudah dengan bantuan stencil buffer.
dengan begitu kita mempunyai persamaan dan operasi yang baru sebagai berikut :
Ini menunjukkan bahwa stencil akan pass jika value = 0x1 karena nilai 0x1 hanya berada pada
area di mana mirror dirender, maka test hanya akan sukses di area itu. Dengan begitu, pada
rendering pass berikut kita tidak mengganti nilai stencil buffer.
Bagian 4
Bagian selanjutnya dari RenderMirror adalah menghitung matrix posisi dan refleksi dari object
teapot :
perhatikan, bahwa kita pertama kali mentranslasi teapot menjadi teapot normal, kemudian baru
kita me-reflect terhadap bidang XY. Urutan transformasi ini harus sama dengan urutan perkalian.
Bagian 5
Pada bagian ini kita sudah siap merender refleksi dari teapot. Akan tetapi jika kita render sekarang,
tidak akan ditampilkan. Kenapa? Karena teapot refleksi mempunyai depth lebih besar daripada
mirror, sehingga akan tertutup oleh kaca. Untuk itu, kita clear dulu depth buffer.
156
tidak semua masalah teratasi, jika kita hanya membersihkan depth buffer, maka refleksi teapot
akan digambar di depan mirror, dan tidak terlihat 'benar'. Supaya object teapot terlihat berada 'di
dalam' mirror, maka kita blend teapot dengan mirror dengan persamaan berikut :
Kita mengganti cull mode karena ketika suatu object di-reflect maka arah windingnya pun terbalik,
jadi backface terbalik menjadi frontface, kita ingin melihat backface, sehingga yang kita cull adalah
front-facenya.
Sesudah itu kita matikan stencil dan blending dan mengembalikan cull mode
157
Sebagai catatan, shadow di sini adalah sebuah cara cepat membuat shadow. Tidak sehebat dan
sebagus jika menggunakan shadow volume. Shadow volume tidak dibahas dalam buku perkenalan
seperti ini. Akan tetapi, DirectX SDK menyediakan sample untuk shadow volume.
Untuk mengimplementasikan planar shadow, kita harus mencari di mana shadow yang dihasilkan
oleh object di plane dan merendernya. Bisa dicari dengan sedikit 3D math. Kemudian kita render
polygonnya dengan material hitam dengan transparansi 50%. Merender shadow sering menimbulkan
artefak beruba "double blending" untuk itu kita memerlukan stencil buffer untuk mencegah double
blending.
ˆ •p + d = 0
n
s
Gambar di atas memperlihatkan shadow yang dicast oleh parallel light. Light ray dari light source,
dengan arah L, melewati sembarang vertex p dengan r(t) = p + tL. Perpotongan antara ray r(t)
ˆ •p + d = 0 menghasilkan vertex s. Perpotongan dapat dengan mudah dicari
dengan plane n
dengan ray/plane intersection test.
G G
ˆ • p + d = 0 dan r (t ) = p + tL . Kita masukkan r(t) ke persamaan bidang :
n
G
ˆ • (p + tL ) + d = 0
n
G
⇔n ˆ • p + t (n
ˆ • L ) = −d
G
⇔ t (nˆ • L ) = −d − nˆ • p
−d − nˆ • p
⇔t = G
(nˆ • L )
Sehingga
⎛ −d − nˆ • p ⎞
s =p+⎜ G ⎟
⎝ (nˆ • L ) ⎠
158
Shadow dari point light
Lihat gambar berikut :
nˆ •p + d = 0
Gambar di atas memperlihatkan shadow yang dicast oleh point light. L adalah posisi light ray datang
G
dari point light melalui sebarang titik p dinyatakan dengan persamaan r (t ) = p + t (p − L ) .
Shadow Matrix
Dari gambar 9.7 dapat kita simpulkan bahwa shadow adalah parallel projection dari object ke
ˆ •p + d = 0 menurut arah paralel light. Sedangkan gambar 9.8. memperlihatkan bahwa
bidang n
ˆ •p + d = 0 dengan
Kita bisa menyatakan transformasi vertex p ke proyeksinya s di bidang n
matrix. Selain itu, kita bisa merepresentasikan orthogonal projection dan perspective projection
dengan matrix dengan ingenuitas.
Misal (nx, ny, nz, d) adalah 4D vector yang menyatakan koefisien dari bidang di mana kita ingin
menempatkan shadow. Kemudian L = ( Lx, Ly, Lz, Lw ) merupakan 4D vector yang menyatakan arah
parallel light maupun posisi point light. Kita menggunakan koordinat W yang menyatakan :
Dengan asumsi bahwa normal dari bidang telah dinormalisasi, maka k = (nx, ny, nz, d) ● ( Lx, Ly, Lz,
Lw ) = nxLx + nyLy + nzLz + dLw. Kemudian kita nyatakan transformasi vertex p ke s dengan matrix
berikut :
159
⎛ nx Lx + k nx Ly nx Lz nx Lw ⎞
⎜
ny Lx ny Ly + k ny Lz ny Lw ⎟⎟
S=⎜
⎜ ny Lx nz Ly nz Lz + k nz Lw ⎟
⎜⎜ ⎟
⎝ dLx dLy dLz dLw + k ⎟⎠
Karena sudah dilakukan di suatu tempat, kita tidak perlu mengetahui dari mana matrix ini
diturunkan. Bagi yang tertarik silahkan baca buku Jim Binn's Corner : A Trip Down to Graphics
Pipeline bagian Me and My (Fake) Shadow.
Library D3DX memberikan fungsi berikut ini untuk membuat shadow matrix dengan memberikan
bidang dan vector yang menyatakan parallel light ( w = 0 ) atau point light ( w = 1 )
D3DXMATRIX *D3DXMatrixShadow(
D3DXMATRIX *pOut,
CONST D3DXVECTOR4 *pLight, // L
CONST D3DXPLANE *pPlane // plane tempat shadow berada
);
Gambar 9.9. Shadow dengan double blending, terlihat segitiga yang overlap saling bertumpukan
160
Gambar 9.10. shadow dengan double blending dimatikan
Untuk mengatasinya kita bisa meggunakan stencil buffer, dengan memerintahkan supaya hanya
merender pixel yang pertama, pixel selanjutnya tidak digambar. Ketika pertama kali merender di
backbuffer, kita tandai di stencil buffer bahwa kita sudah menggambar pixel tersebut. Jika ada
pixel dirender di tempat yang sama maka akan fail dalam stencil test.
Stencil sudah diset ke zero (0x0) maka pada saat pertama kali digambar akan selalu bernilai true,
dan pixel digambar. Akan tetapi kita mengeset stencil operation untuk D3DRS_STENCILPASS ke
D3DSTENCILOP_INCR, sehingga setelah pass, nilainya menjadi 0x1 sehingga stencil test akan fail
jika kita mencoba merender pixel di tempat yang sama sehingga mencegah double blending.
kemudian kita cari shadow transformation dan mentranslate shadow ke tempat yang benar di scene.
161
D3DXVECTOR4 LightDirection( 0.707f, -0.707f, 0.707f, 0.0f ) ;
D3DXPLANE GroundPlane( 0.0f, -1.0f, 0.0f, 0.0f ) ;
D3DXMATRIX matShadow ;
D3DXMatrixShadow( &matShadow, &LightDirection, &GroundPlane ) ;
D3DXMATRIX matTrans ;
D3DXMatrixTranslation( &matTrans, g_vTeapotPosition.x,
g_vTeapotPosition.y, g_vTeapotPosition.z ) ;
Terakhir, kita set material shadow ke transparansi 50%, mendisable depth test, render shadownya,
kemudian meng-enable lagi depth buffer dan mematikan alpha blending dan stencil test. Kita
mematikan z-buffer untuk mencegah Z-Fighting yaitu flicker yang terjadi ketika suatu scene
mempunyai nilai depth buffer yang sama sehingga depth buffer tidak tahu harus menggambar yang
mana dahulu. Karena shadow berada pada tempat yang sama dengan bidang, maka z-fighting akan
terjadi. Dengan menggambar floornya dahulu kemudian menggambar shadownya kita menjamin
bahwa shadow berada di atas floor.
g_pDevice->SetMaterial( &Material ) ;
g_pDevice->SetTexture( 0, NULL ) ;
g_pTeapot->DrawSubset( 0 ) ;
162
ScreenShot Sample Code
Mirror
Mirror dengan stencil buffer :
Shadow
Shadow dengan stencil buffer :
163
Shadow Mirror
Gabungan dua sample di atas :
164
Penutup
Demikian sekelumit tutorial pemrograman 3D yang amat dasar. Seluruh sample code ada dalam
paket buku ini. Diharapkan dengan tutorial yang hanya sekelumit ini, pembaca dapat meningkatkan
atau belajar kemampuan pemrograman 3D. Pada akhirnya tidak ada gading yang tak retak, penulis
mengucapkan maaf jika ada kalimat yang salah. Dan tentunya penulis sangat terbuka akan kritik
membangun terhadap buku ini.
Terima kasih.
165