EC5130 – Grafika Komputer dan Pemrograman GPU Suplemen Diktat Kuliah OpenGL Tutorial dengan GLUT: Fixed Pipeline
Ary Setijadi Prihatmanto
Sekolah Teknik Elektro & Informatika Institut Teknologi Bandung 2007
1/117
Table of Content Table of Content ................................................................................................................. 1 Tutorial 01. “Hello World” dengan GLUT ......................................................................... 2 Tutorial 02. OpenGL Primitives ......................................................................................... 5 Tutorial 03. 3D Vertex ...................................................................................................... 10 Tutorial 04. Callback Reshape .......................................................................................... 11 Tutorial 04. Modeling & Transformasi Proyeksi .............................................................. 13 Transformasi Proyeksi .............................................................................................. 15 Transformasi Viewing .............................................................................................. 17 Transformasi Modeling ............................................................................................. 17 Tutorial 05. Urutan Transformasi ..................................................................................... 20 Tutorial 06. Texture Mapping & Blending ....................................................................... 23 Tutorial 07. Transparency ................................................................................................. 32 Tutorial 08. Fog ................................................................................................................ 35 Tutorial 09. Obyek Kompleks........................................................................................... 41 Tutorial 10. Particle System .............................................................................................. 62 Tutorial 11. Lighting ......................................................................................................... 69 Tutorial 12.Vertex Animation ......................................................................................... 112
2/117
Tutorial 01. “Hello World” dengan GLUT Tujuan dari bagian ini adalah untuk membuat OpenGL-based window. Ada banyak cara untuk membuat dan memperlihatkan suatu window dalam berbagai sistem window. Salah satunya adalah dengan menggunakan OpenGL Utility Toolkit. OpenGL UtilityToolkit (GLUT) menyediakan banyak fungsi yang dapat membuat window dengan cara yang independen terhadap sistem operasinya. Hal ini berarti program yang dibuat dengan GLUT dapat beroperasi pada sistem windowing yang berbeda tanpa merubah code secara manual. GLUT adalah API (Application Programming Interface) dengan binding ANSI C untuk penulisan Sistem Windows program OpenGL. GLUT adalah buatan Mark J. Killgard ketika bekerja di Silicon Graphics Inc. Walaupun dirancang untuk digunakan bersamasama dengan kode OpenGL, GLUT dapat digunakan dengan atau tanpa OpenGL. Toolkit ini mendukung fungsionalitas sebagai berikut: • Multiplewindows untuk rendering OpenGL. • Callback driven event processing. • Sophisticated input devices. • An “idle” routine and timers. • A simple, cascading pop-up menu facility. • Utility routines to generate various solid and wire frame objects. • Support for bitmap and stroke fonts. • Miscellaneous window management functions, including managing overlays. Walaupun secara fungsional mungkin jauh tertinggal dari multiplatform window system yang lain seperti Qt, namun kesederhanaan penggunaan serta hubungannya dengan API grafika komputer OpenGL membuat glut masih banyak digunakan terutama sebagai alat bantu pendidikan Grafika Komputer. GLUT library dapat didownload dari http://www.glut.org. Setelah library GLUT diinstall, GLUT dapat digunakan dengan mengacu ke header file glut.h. File tersebut selain mendefinisikan beberapa hal-hal yang terkait dengan GLUT, mengacu pada opengl.h dan glu.h juga memberikan arahan kepada compiler untuk me-link secara automatis dengan library-library yang dibutuhkan contohnya opengl.lib(opengl32.lib), glu.lib(glu32.lib) dan glut.lib(glut32.lib). Selain itu, untuk aplikasi di atas sistem operasi windows memerlukan glut32.dll dapat terlihat oleh aplikasi. Struktur dari aplikasi berbasis GLUT akan terdiri atas beberapa langkah berikut, yaitu: • Menetapkan konfigurasi windows, dan membuka windows • Inisialisasi status OpenGL • Registrasi callback functions (jika dibutuhkan) o Render o Resize o Input o Timer
3/117
•
o Idle Enter event processing loop. Gambar 1 mengilustrasikan loop tersebut. Gambar 1. GLUT Event Processing Loop yang disederhanakan
Fraksi kode berikut adalah contoh bagian main() dari suatu program GLUT: void main( int argc, char** argv ) { // Konfigurasi dan Menampilkan Window int mode = GLUT_RGB|GLUT_DOUBLE; glutInitDisplayMode( mode ); glutCreateWindow( argv[0] ); // Fungsi untuk melakukan initialisasi init(); // Registrasi Callback Function glutDisplayFunc( display ); glutReshapeFunc( resize ); glutKeyboardFunc( key ); glutIdleFunc( idle ); // Event Processing Loop glutMainLoop();
4/117 } Pada kode diatas, status OpenGL diinisialisasi di fungsi init(), sedangkan kode-kode yang mengandung fungsi-fungsi rendering OpenGL biasanya merupakan bagian dari fungsi callback display. Program 01. GLUT “Hello World” & Gambar Segiempat #include #include #include #include #include
<stdio.h> <stdlib.h> <string.h> <stdarg.h>
void mydisplay(){ glClear(GL_COLOR_BUFFER_BIT); // Menghapus layar glBegin(GL_POLYGON); glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f(0.5, 0.5); glVertex2f(0.5, -0.5); glEnd(); glFlush(); } int main(int argc, char** argv) { printf(“Hello World… this is 2D Rectangle”); glutCreateWindow("simple"); glutDisplayFunc(mydisplay); glutMainLoop(); }
mydisplay() diregistrasi oleh glutDisplayFunc() sebagai fungsi yang dilaksanakan saat window digambar, yang biasanya adalah isi dari gambarnya.. Hasilnya akan tampak seperti gambar berikut:
Coba periksa dimensi window seperti koordinat titik tengah, titik kiri atas, titik kiri bawah, titik kanan atas dan titik kanan bawah dengan merubah-rubah parameter fungsi glVertex2f(). Diperoleh Koordinat: - Titik Tengah ( ___, ___) - Titik Kiri Atas ( ___, ___), Titik Kiri Bawah ( ___, ___) - Titik Kanan Atas ( ___, ___), Titik Kanan Bawah ( ___, ___)
5/117
Tutorial 02. OpenGL Primitives Pada tutorial 01 telah diberikan contoh program untuk merepresentasikan model obyek segiempat 2D. OpenGL memiliki beberapa komponen dasar untuk merepresentasikan suatu obyek. Komponen dasar tersebut disebut OpenGL Geometric primitives. Gambar 2 menggambarkan semua OpenGL Geometric primitives yang mungkin. Gambar 2. OpenGL Geometric Primitives
GL_LINES GL_LINE_STRIP
GL_POINTS
GL_LINE_LOOP
GL_POLYGON
GL_TRIANGLES GL_QUADS GL_QUAD_STRIP GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
Setiap obyek harus dimodelkan sebagai kombinasi dari komponen-komponen dasar tersebut. Sebagai contoh, obyek segiempat pada tutorial 01 tersebut dimodelkan dengan menggunakan komponen dasar GL_POLYGON. Obyek tersebut dapat pula dimodelkan dengan komponen dasar GL_TRIANGLES atau pun GL_QUAD. Hingga saat ini kita belum menerangkan secara detil masing-masing fungsi OpenGL. Secara umum perintah-perintah dalam OpenGL memenuhi aturan sebagai berikut:
6/117
glVertex3fv( v )
Data Type
Number of components
2 - (x,y) 3 - (x,y,z) 4 - (x,y,z,w)
b ub s us i ui f d
-
byte unsigned byte short unsigned short int unsigned int float double
Vector
omit “v” for scalar form glVertex2f( x, y )
Dalam OpenGL, menggambar geometric primitives selalu dilakukan di antara fungsi glBegin(PRIMITIVES) // Fungsi Menggambar Primitives di sini glEnd() Setiap OpenGL geometric primitive dispesifikasi oleh urutan vertex-vertex-nya dalam bentuk urutan koordinat homogenous. Koordinat homogenous adalah koordinat dalam bentuk ( x, y, z, w ). Setiap primitive memiliki standar tentang bagaimana vertex-vertex diorganisasikan. Program 02 dan Program 03 memberikan contoh bagaimana memodelkan primitive segitiga dan segidelapan. Program 02. Segitiga #include #include #include #include #include
<stdio.h> <stdlib.h> <string.h> <stdarg.h>
void mydisplay(){ glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_TRIANGLES); glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f(0.5, 0.5); glEnd(); glFlush(); } int main(int argc, char** argv){ glutCreateWindow("simple"); glutDisplayFunc(mydisplay); glutMainLoop();
7/117 }
Program 03. Polygon Segi Delapan #include #include #include #include #include
<stdio.h> <stdlib.h> <string.h> <stdarg.h>
void mydisplay(){ glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POLYGON); glColor3f(0, 1, 0); glVertex2f(-0.5, -0.5); glVertex2f(-0.75, 0); glVertex2f(-0.5, 0.5); glVertex2f(0, 0.75); glVertex2f(0.5, 0.5); glVertex2f(0.75, 0); glVertex2f(0.5, -0.5); glVertex2f(0,-0.75); glEnd(); glFlush(); } int main(int argc, char** argv){ glutCreateWindow("simple"); glutDisplayFunc(mydisplay); glutMainLoop(); }
Perhatikan urutan dari vertex untuk setiap jenis OpenGL Geometric Primitive. Tugas: Buat Program untuk menggambar jenis OpenGL Geometric Primitive yang lain. Bonus: glColor3f() adalah fungsi untuk menentukan warna yang berlaku hingga fungsi berikutnya. Program 03 berikut adalah program yang sama dengan Program 02 hanya di sini setiap vertex diberi warna yang berbeda.
8/117 Program 04. Polygon Segi Delapan dengan warna #include #include #include #include #include
<stdio.h> <stdlib.h> <string.h> <stdarg.h>
void mydisplay(){ glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POLYGON); glColor3f(0, 1, 0); glVertex2f(-0.5, -0.5); glColor3f(0, 0, 1); glVertex2f(-0.75, 0); glColor3f(1, 0, 0); glVertex2f(-0.5, 0.5); glColor3f(0, 1, 0); glVertex2f(0, 0.75); glColor3f(0, 0, 1); glVertex2f(0.5, 0.5); glColor3f(1, 0, 0); glVertex2f(0.75, 0); glColor3f(0, 1, 0); glVertex2f(0.5, -0.5); glColor3f(0, 0, 1); glVertex2f(0,-0.75); glEnd(); glFlush(); } int main(int argc, char** argv){ glutCreateWindow("simple"); glutDisplayFunc(mydisplay); glutMainLoop(); }
Hasilnya diilustrasikan pada gambar berikut:
9/117
10/117
Tutorial 03. 3D Vertex Semua vertex dari obyek yang dimodelkan di atas masih berada pada satu bidang z=0, atau obyek yang dimodelkan masih berupa model 2D karena kita hanya memberikan vertex properti koordinat x dan y dengan menggunakan fungsi glVertex2f(). Untuk memodelkan obyek dalam 3D kita perlu memberi properti koordinat z dengan menggunakan fungsi glVertex3f(). Program 05 memberikan ilustrasi penggunaan fungsi tersebut. Program 05. 3 Dimensional Vertex #include #include #include #include #include
<stdio.h> <stdlib.h> <string.h> <stdarg.h>
void mydisplay(){ glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POLYGON); glColor3f(0, 1, 0); glVertex3f(-0.5, -0.5, 1); glColor3f(0, 0, 1); glVertex3f(-0.75, 0, 1); glColor3f(1, 0, 0); glVertex3f(-0.5, 0.5, 1); glColor3f(0, 1, 0); glVertex3f(0, 0.75, 1); glColor3f(0, 0, 1); glVertex3f(0.5, 0.5, -1); glColor3f(1, 0, 0); glVertex3f(0.75, 0, -1); glColor3f(0, 1, 0); glVertex3f(0.5, -0.5, -1); glColor3f(0, 0, 1); glVertex3f(0,-0.75, -1); glEnd(); glFlush(); } int main(int argc, char** argv){ glutCreateWindow("simple"); glutDisplayFunc(mydisplay); glutMainLoop(); }
11/117
Tutorial 04. Callback Reshape Ambil Program 01 yang menggambar bujur sangkat sebagai dasar. Jika kita drag ujung windows sehingga window tidak lagi berupa bujursangkar, bujursangkar-nya juga berubah bentuk. Gambar berikut mengilustrasikan situasinya.
Agar gambar tetap berada pada proporsi yang tepat, maka perlu digunakan callback reshape yang dipanggil setiap kali window berubah ukuran. Untuk itu perlu lakukan dua langkah berikut: - membuat fungsi yang akan dipanggil saat rehape, di sini fungsinya adalah void reshape(int width, int height) - melakukan registrasi callback reshape dengan fungsi glutReshapeFunc(.) Program 06. ReShape Callback Function #include #include #include #include #include
<stdio.h> <stdlib.h> <string.h> <stdarg.h>
void mydisplay(){ glClear(GL_COLOR_BUFFER_BIT); // Menghapus layar glBegin(GL_POLYGON); glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f(0.5, 0.5); glVertex2f(0.5, -0.5); glEnd(); glFlush(); } void resize( int w, int h ) { if (w >= h) glViewport(0, 0, (GLsizei)h, (GLsizei)h) ; else glViewport(0, 0, (GLsizei)w, (GLsizei)w) ;
} int main(int argc, char** argv) { printf(“Hello World… this is 2D Rectangle”);
12/117 glutCreateWindow("simple"); glutDisplayFunc(mydisplay); glutReshapeFunc(reshape); glutMainLoop(); }
Hasilnya diilustrasikan pada gambar berikut:
glViewport(x_left, x_top, x_right, y_right) bertanggung jawab untuk melakukan setting viewport dari suatu window, yaitu bagian dari window yang digunakan untuk menggambar. Selain setting glViewport() biasanya Reshape callback function juga digunakan untuk mengatur Transformasi Proyeksi. Tutorial berikut akan memberi gambaran penggunaannya.
13/117
Tutorial 04. Modeling & Transformasi Proyeksi Secara substansi, Grafika Komputer adalah proses transformasi dari model 3D obyek berupa informasi geometri bentuk, informasi pose, warna, texture, dan pencahayaan menjadi citra 2D (cf. Gambar 3). Gambar 3. Grafika Komputer: Transformasi dari Model 3D Obyek menjadi Citra
Model 3D Obyek - Bentuk - Pose (Posisi & Orientasi) - Warna - Texture - Pencahayaan
T
Citra 2D Obyek
Jika dilihat secara analogi, hal di atas mirip dengan cara kerja kamera dalam mengambil foto dalam bidang fotografi (cf. Gambar 4). Model ini disebut model sintesis kamera. Gambar 4. Analogi Pengambilan Gambar oleh Kamera
viewing volume camera
tripod
model
14/117
Untuk menghasilkan gambar dari obyek dengan skenario tertentu kita harus melakukan beberapa proses, yaitu: - melakukan pengesetan kamera dalam bentuk setting lensa kamera (Transformasi Proyeksi), - mengarah kamera dengan mengatur letak tripod (Transformasi Viewing), - mengatur letak obyek (Transformasi Modeling), dan - mengatur skala dan layout dari foto (Transformasi Viewport) Kita telah mempelajari Transformasi Viewport pada tutorial sebelumnya dengan menggunakan perintah glViewport(). Pada tutorial ini, kita akan mempelajari transformasi-transformasi lainnya.
15/117
Transformasi Proyeksi Lensa kamera dan mata manusia memiliki daerah penglihatan (viewing volume) yang berbentuk kerucut, namun karena bentuk display yang biasanya berbentuk segiempat membuat OpenGL (dan hampir semua API grafika komputer lain) lebih efisien memodelkan daerah penglihatan sebagai volume berbentuk piramida. Tipe transformasi proyeksi ada dua macam, bergantung pada parameter dan bentuk piramidanya. Dua tipe transformasi tersebut adalah Transformasi Ortogonal/Paralel (Orthogonal Transformation) dan Transformasi Perspektif(Perspective Transformation) (cf. Gambar 5). Gambar 5. Transformasi Ortogonal dan Transformasi Proyektif.
Transformasi Ortogonal/Paralel
Transformasi Perspektif Pada tutorial sebelumnya digunakan transformasi orthogonal dengan parameter default. Transformasi ini membuat jarak benda relatif terhadap kamera tidak berpengaruh pada
16/117 citra benda tersebut. Biasanya transformasi ini digunakan pada aplikasi-aplikasi teknik seperti gambar teknik (cf. Gambar 6). Untuk merubah parameter transformasi ortogonal dapat menggunakan perintah glOrtho() dengan didahului proses merubah status OpenGL ke mode proyeksi dengan perintah glMatrixMode(GL_PROJECTION). Gambar 6. Contoh Transformasi Ortogonal/Paralel
Section AA
A
side elevation view
Front elevation view Pada tutorial ini dan selanjutnya, kita akan memfokuskan diri pada transformasi yang banyak digunakan yaitu transformasi perspektif. Pada transformasi jenis ini jarak benda akan mempengaruhi gambar yang di buat. Parameter transformasi jenis ini dapat dirubah dengan menggunakan gluPerspective()/glFrustum() , juga dengan didahului proses merubah status OpenGL ke mode proyeksi dengan perintah glMatrixMode(GL_PROJECTION). glMatrixMode(GL_PROJECTION); glLoadIdentity( ); gluPerspective(fovy, aspect, near, far);
17/117
fovy adalah sudut antara bidang bottom dan up.
Transformasi Viewing Untuk menghasilkan gambar, kamera perlu diletakkan pada posisi yang tepat didepan pemandangan yang diinginkan. Secara default, dalam OpenGL kemera akan berada pada posisi (0,0,0) dengan menghadap ke arah z = -1 dengan sumbu y mengarah ke atas kamera. Hal ini dapat dilakukan dengan menggunakan perintah gluLookAt() dengan didahului proses merubah status OpenGL ke mode proyeksi dengan perintah glMatrixMode(GL_MODELVIEW).
Transformasi Modeling Selain posisi dan orientasi kamera yang dapat dirubah-rubah, secara natural obyek juga dapat berpindah posisi dan orientasi relatif terhadap yang lain.Transformasi obyek dapat direpresentasikan dengan dua cara, yaitu: - menggunakan matriks transformasi (glLoadMatrix) - menggunakan operasi transformasi (glRotate, glTranslate) dengan didahului proses merubah status OpenGL ke mode proyeksi dengan perintah glMatrixMode(GL_MODELVIEW). Program dibawah memberi ilustrasi tentang bagaimana transformasi di atas diimplementasikan. Sebagai tambahan juga diberikan tentang callback keyboard untuk menangani input keyboard. Obyek ditranslasikan pada sumbu z dengan menggunakan tombol keyboard “,” dan “.”.Callback timer digunakan untuk timer yang di sini digunakan untuk animasi berputar. Program 07. Proyeksi Perspektif // - Viewing Volume of Perspective Projection // - Try the keyboard callback // - Reshape callback // - Timer //
18/117 #include #include #include #include #include
<stdio.h> <stdlib.h> <string.h> <stdarg.h>
float z_pos=0.0f; float rot=0.0f; void mydisplay(){ glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0,0.0f,z_pos); glRotatef(rot, 0, 0, 1); glBegin(GL_POLYGON); glColor3f(0, 1, 0); glVertex3f(-0.5, -0.5, -5); glColor3f(0, 0, 1); glVertex3f(-0.75, 0, -5); glColor3f(1, 0, 0); glVertex3f(-0.5, 0.5, -5); glColor3f(0, 1, 0); glVertex3f(0, 0.75, -5); glColor3f(0, 0, 1); glVertex3f(0.5, 0.5, -5); glColor3f(1, 0, 0); glVertex3f(0.75, 0, -5); glColor3f(0, 1, 0); glVertex3f(0.5, -0.5, -5); glColor3f(0, 0, 1); glVertex3f(0,-0.75, -5); glEnd(); glFlush(); glutSwapBuffers(); } void init( void ) { glClearColor( 1.0, 0.0, 0.0, 1.0 ); // A Background Clear Color glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, (GLdouble)500.0/(GLdouble)500.0, 0, 100); glMatrixMode(GL_MODELVIEW); } void resize( int w, int h ) { glViewport( 0, 0, (GLsizei) w, (GLsizei) h ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective(45, (GLdouble)w/(GLdouble)h, 0, 100); glMatrixMode( GL_MODELVIEW ); }
void myTimeOut(int id)
19/117 { // called if timer event // ...advance the state of animation incrementally... rot+=10; glutPostRedisplay(); // request redisplay glutTimerFunc(100, myTimeOut, 0); // request next timer event } void myKeyboard(unsigned char key,int x, int y) { if((key=='<')||(key==',')) z_pos-=0.1f; if((key=='>')||(key=='.')) z_pos+=0.1f; } int main(int argc, char** argv) { glutInit(&argc,argv); //glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB); glutInitWindowSize(500,500); glutInitWindowPosition(0,0); glutCreateWindow("simple"); // callbacks glutDisplayFunc(mydisplay); glutKeyboardFunc(myKeyboard); glutTimerFunc(100, myTimeOut, 0); glutReshapeFunc(resize); init(); glutMainLoop(); }
Tambahan: Konsep Double Buffer. Pada program di atas mode display menggunakan tipe GLUT_DOUBLE yang diikuti oleh glutSwapBuffers(). Hal ini merupakan teknik yang disebut Double Buffer untuk menghindari flicker. Untuk mengetahui apa itu flicker, ubah mode display menjadi GLUT_SINGLE dan hapus/commented perintah glutSwapBuffer().
20/117
Tutorial 05. Urutan Transformasi Transformasi dapat dilakukan pada level vertex, level surface, maupun level obyek bergantung dimana transformasi diletakkan dalam program. Operasi transformasi merupakan operasi yang tidak bersifat komutatif, artinya, urutan transformasi juga sangat berpengaruh pada hasilnya. Gambar 7 memberi ilustrasi akibat urutan transformasi yang berbeda, yaitu hasil operasi “rotasi kemudian di translasi” berbeda dengan operasi “translasi baru dirotasi”. Gambar 7. Pengaruh urutan transformasi
Program 08 di bawah ini mirip dengan Program 07, hanya sekarang obyeknya sudah berupa obyek 3D berupa kubus. Perhatikan bagaimana kubus dibentuk dari vertex dan surface. Selain dengan mendefinisikan obyeknya sendiri, GLUT telah menyediakan beberapa fungsi untuk menggambar standard obyek, yaitu kubus, bola, dan poci teh. Perhatikan apa yang terjadi bila glTranslate() dan glRotate() di fungsi mydisplay() ditukar posisinya atau diletakkan didalam salah satu glBegin()..glEnd() // // // // // // // // //
OpenGL - Complex Object - Notice: 1. There are surfaces that are not correctly rendered in order. uncommented the GL_DEPTH 2. Flicker can be eliminated by using GL_DOUBLE Rubah rendering algoritma dengan menggunakan data struktur
#include #include #include #include #include
<stdio.h> <stdlib.h> <string.h> <stdarg.h>
float z_pos=-10.0f; float rot=0.0f; void resize(int width, int height) { glViewport(0, 0, width, height);
21/117 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (float)width/(float)height, 1.0, 300.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void myTimeOut(int id) { // called if timer event // ...advance the state of animation incrementally... rot+=10; glutPostRedisplay(); // request redisplay glutTimerFunc(100, myTimeOut, 0); // request next timer event } void myKeyboard(unsigned char key,int x, int y) { if((key=='<')||(key==',')) z_pos-=0.1f; if((key=='>')||(key=='.')) z_pos+=0.1f; } void mydisplay(void) { glClear(GL_COLOR_BUFFER_BIT ); //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0,0.0f,z_pos); glRotatef(rot, 0, 1, 0); glBegin(GL_QUADS); // Front Face, red glColor3f(1.0,0.0,0.0); glVertex3f(-1.0f, -1.0f, glVertex3f( 1.0f, -1.0f, glVertex3f( 1.0f, 1.0f, glVertex3f(-1.0f, 1.0f, // Back Face, green glColor3f(0.0,1.0,0.0); glVertex3f( 1.0f, -1.0f, glVertex3f( 1.0f, 1.0f, glVertex3f(-1.0f, 1.0f, glVertex3f(-1.0f, -1.0f, // Top Face, blue glColor3f(0.0,0.0,1.0); glVertex3f(-1.0f, 1.0f, glVertex3f(-1.0f, 1.0f, glVertex3f( 1.0f, 1.0f, glVertex3f( 1.0f, 1.0f, // Bottom Face, yellow glColor3f(1.0,1.0,0.0); glVertex3f(-1.0f, -1.0f, glVertex3f( 1.0f, -1.0f, glVertex3f( 1.0f, -1.0f, glVertex3f(-1.0f, -1.0f, // Right face, cyan glColor3f(0.0,1.0,1.0); glVertex3f( 1.0f, -1.0f, glVertex3f( 1.0f, 1.0f, glVertex3f( 1.0f, 1.0f, glVertex3f( 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);
22/117 // Left Face, magenta glColor3f(1.0,0.0,1.0); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glEnd();
glFlush(); glutSwapBuffers(); } void init() { glEnable(GL_DEPTH_TEST); glClearColor( 0.0, 0.0, 0.0, 1.0 ); // A Background Clear Color glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, (GLdouble)500.0/(GLdouble)500.0, 0, 100); glMatrixMode(GL_MODELVIEW); return; } int main(int argc, char** argv) { glutInit(&argc,argv); //glutInitDisplayMode( GLUT_DOUBLE /*| GLUT_DEPTH*/ ); glutInitDisplayMode( GLUT_DOUBLE | GLUT_DEPTH ); glutInitWindowSize(500,500); glutInitWindowPosition(0,0); glutCreateWindow("simple"); // callbacks glutDisplayFunc(mydisplay); glutKeyboardFunc(myKeyboard); glutTimerFunc(100, myTimeOut, 0); glutReshapeFunc(resize); init(); glutMainLoop(); return 0; }
Tambahan: Konsep Depth Buffer. Mode display pada program di atas diberi tambahan mode GLUT_DEPTH dan perintah glEnable(GL_DEPTH_TEST). Hal ini untuk memastikan bahwa surface digambar sesuai dengan urutan penampakkan yang logis. Teknik ini merupakan salah satu algoritma HIDDEN SURFACE REMOVAL. Untuk melihat apa yang terjadi bila teknik ini tidak dilakukan, hapus/commented moda GLUT_DEPTH dan glEnable(GL_DEPTH_TEST).
23/117
Tutorial 06. Texture Mapping & Blending Hingga tahap ini, geometric primitive digambar dengan warna solid atau warna hasil interpolasi warna-warna vertex-nya. Texture mapping memungkinkan untuk menaruh gambar pada geometric primitive tersebut dan sekaligus mengikuti transformasi yang diterapkan kepada polygon tersebut (cf. Gambar 8). Gambar 8. Konsep Texture Mapping
y geometry z
x
screen
(0.0,1.0)
t
(0.0,0.0)
image
s
(1.0,0.0)
Texture merupakan data segi-empat sederhana yang berada pada bidang texture. Bidang texture diwakili oleh dua sumbu koordinat yaitu sumbu s dan sumbu t. Setiap texture akan memenuhi bidang koordinat (0.0,0.0) sd. (1.0,1.0). Nilai individual dari array texture biasanya dikenal dengan istilah texels (texture pixels). Yang membuat texture mapping sedikit rumit adalah bagaimana proses pemetaan antara bentuk segi-empat texture ke polygon menginngat secara umum bentuk poligon biasanya non-rectangular. Beberapa contoh penggunaan texture mapping antara lain: • mensimulasikan aspek visual dari material seperti tampakan kayu, batu bata, atau granit • mengurangi kompleksitas (jumlah polygon yang dibutuhkan) dari suatu obyek geometri. • teknik pemrosesan citra seperti image warping dan rectification, rotation dan scaling
24/117 •
mensimulasikan berbagai efek permukaan seperti efek reflektif seperti cermin atau lantai yang telah digosok mengkilat, efek tonjolan dll.
Salah satu keuntungan dari texture mapping adalah bahwa detail visual itu berada di citra bukan di geometri. Dan sekompleks apapun citra, selama tidak merubah ukuran citra, tidak berpengaruh pada kinerja keseluruhan, yaitu kompleksitas cari citra tidak berpengaruh kepada pipeline geometric (transformasi, clipping) dari OpenGL. Texture ditambahkan saat rasterisasi ketika geometric pipeline dan pixel pipeline bertemu seperti diilustrasikan pada Gambar berikut.
vertices
geometry pipeline rasterizer
image
pixel pipeline
Secara konseptual ada tiga langkah dalam melakukan texture mapping, yaitu: - Penentuan texture o Baca image dari file o Generate texture id untuk image tersebut glGenTextures(3, &texture[0]) - Pemberian koordinat texture ke vertex - Penentuan parameter texture (wrapping / filtering) Program 09 memberi ilustrasi tentang proses di atas. Perhatikan: - baris TextureImage[0]=LoadBMP("Crate.bmp"). Ubah sesuai file citra bmp yang anda punya. - prosedur int LoadGLTexture(). Perhatikan bagaimana menggunakan citra bmp yang telah diload ke memory dalam bentuk struktur data AUX_RGBImageRec* menjadi tiga buah texture dalam OpenGL dg metoda filter pembesaran texture yang berbeda. Metoda itu adalah yaitu metoda Nearest Filtered Texture, metoda Linear Interpolation Texture dan metoda Mipmapped Texture. Perhatikan bedanya dengan merubah-rubah filternya menggunakan tombol keyboard “f”.
Filter pembesaran texture berpengaruh pada bagaimana OpenGL melakukan proses rasterisasi texture saat texture ditampilkan pada jumlah pixel yang lebih besar atau lebih kecil dari ukuran sebenarnya. Pada Nearest Filtered Texture, texture yang ditampilkan merupakan hasil pemilihan nilai pixel pada posisi terdekat. Sedangkan dengan Linear Interpolation Texture (LPT), texture yang ditampilkan merupakan hasil interpolasi linear antara pixel-pixel disekitarnya. Pada Mipmapped Texture(MPT), interpolasi linear dilakukan pada awal secara offline sehingga dihasilkan banyak texture dengan ukuran dari yang kecil hingga yang besar. LPT dan MPT akan menghasilkan kira-kira
25/117 hasil yang sama dengan LPT akan sedikit lebih lambat dari MPT walaupun memori yang digunakan jauh lebih kecil. Program 09 Bmp.h #include AUX_RGBImageRec *LoadBMP(char *Filename);
Bmp.cpp #include <windows.h> #include <stdio.h> Input/Output #include #include #include #include "bmp.h"
// Header File For Standard // Header File For The OpenGL32 Library // Header File For The GLu32 Library // Header File For The Glaux Library
AUX_RGBImageRec *LoadBMP(char *Filename) Bitmap Image { FILE *File=NULL; File Handle
// Loads A
//
if (!Filename) // Make Sure A Filename Was Given { return NULL; // If Not Return NULL } File=fopen(Filename,"r"); Check To See If The File Exists if (File) // Does The File Exist? { fclose(File); // Close The Handle return auxDIBImageLoad(Filename); The Bitmap And Return A Pointer } return NULL; // If Load Failed Return NULL }
Program.cpp // OpenGL // - Function to load bitmap
//
// Load
26/117 // - Texture Mapping Magnification Filter // filter=0 --> Nearest Filtered Texture // filter=1 --> Linear Interpolation Texture // filter=2 --> Mipmapped Texture #include #include #include #include #include #include
<windows.h> <stdio.h> <stdlib.h> <string.h> <stdarg.h>
#include "bmp.h" float z_pos=-5.0f; float rot=0.0f; GLfloat LightAmbient[]= GLfloat LightDiffuse[]= GLfloat LightPosition[]=
{ 0.5f, 0.5f, 0.5f, 1.0f }; { 1.0f, 1.0f, 1.0f, 1.0f }; { 0.0f, 0.0f, 2.0f, 1.0f };
/* array to hold texture handles */ GLuint filter; // Which Filter To Use GLuint texture[3]; // Storage For 3 Textures int LoadGLTextures() Load Bitmaps And Convert To Textures { int Status=FALSE; Status Indicator AUX_RGBImageRec *TextureImage[1]; Storage Space For The Texture memset(TextureImage,0,sizeof(void *)*1); Pointer To NULL
//
//
// Create
// Set The
// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit if (TextureImage[0]=LoadBMP("Crate.bmp")) { Status=TRUE; // Set The Status To TRUE glGenTextures(3, &texture[0]); Create Three Textures // Create Nearest Filtered Texture glBindTexture(GL_TEXTURE_2D, texture[0]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]>data); // Create Linear Filtered Texture
//
27/117 glBindTexture(GL_TEXTURE_2D, texture[1]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]>data); // Create MipMapped Texture glBindTexture(GL_TEXTURE_2D, texture[2]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_ NEAREST); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); } if (TextureImage[0]) If Texture Exists { if (TextureImage[0]->data) If Texture Image Exists { free(TextureImage[0]->data); The Texture Image Memory }
//
//
// Free
free(TextureImage[0]); Free The Image Structure } return Status; // Return The Status } void resize(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (float)width/(float)height, 1.0, 300.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void myTimeOut(int id) { // called if timer event // ...advance the state of animation incrementally... rot+=10; glutPostRedisplay(); // request redisplay glutTimerFunc(100, myTimeOut, 0); // request next timer event } void myKeyboard(unsigned char key,int x, int y)
//
28/117 { if((key=='<')||(key==',')) z_pos-=0.1f; else if((key=='>')||(key=='.')) z_pos+=0.1f; else if((key=='F')||(key='f')) { filter+=1; if (filter>2) { filter=0; } printf("filter: %i",filter); } } void mydisplay(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glLoadIdentity(); glTranslatef(0.0,0.0f,z_pos); glRotatef(rot, 0, 1, 0); glBindTexture(GL_TEXTURE_2D, texture[filter]); glBegin(GL_QUADS); // Front Face glColor3f(1.0,0.0,0.0); glNormal3f( 0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, // Back Face glNormal3f( 0.0f, 0.0f,-1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, // Top Face glNormal3f( 0.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, // Bottom Face glNormal3f( 0.0f,-1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, // Right face glNormal3f( 1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 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, -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);
29/117 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Left Face glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glEnd(); glFlush(); glutSwapBuffers(); } void init() { if (!LoadGLTextures()) Jump To Texture Loading Routine { return; If Texture Didn't Load Return FALSE }
glEnable(GL_TEXTURE_2D); Enable Texture Mapping glShadeModel(GL_SMOOTH); Enable Smooth Shading glClearColor(0.0f, 0.0f, 0.0f, 0.5f); Background glClearDepth(1.0f); // Depth Buffer Setup glEnable(GL_DEPTH_TEST); Enables Depth Testing glDepthFunc(GL_LEQUAL); The Type Of Depth Testing To Do glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); Perspective Calculations glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); Ambient Light glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); Diffuse Light glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); Light glEnable(GL_LIGHT1); return; } int main(int argc, char** argv) { glutInit(&argc,argv); glutInitDisplayMode( GLUT_DOUBLE | GLUT_DEPTH ); glutInitWindowSize(500,500); glutInitWindowPosition(0,0);
//
//
// // // Black
// // // Really Nice
// Setup The // Setup The // Position The
30/117 glutCreateWindow("simple"); // callbacks glutDisplayFunc(mydisplay); glutKeyboardFunc(myKeyboard); glutTimerFunc(100, myTimeOut, 0); glutReshapeFunc(resize); init(); glutMainLoop(); return 0; }
Seringkali, efek yang diinginkan dapat diperoleh dengan mencampur lebih dari satu texture. Proses pencampuran lebih dari satu texture disebut dengan istilah blending. Salah satu efek blending yang paling sederhana adalah dengan memblending texture dengan warna. Fragment program 10 di bawah memperlihatkan perubahan yang terjadi pada void mydisplay() jika kita ingin melakukan pencampuran antara texture dan warna. Perhatikan fungsi glEnable(GL_BLEND). Di sini texture di blend dengan warna merah. TUGAS: coba rubah codenya agar warna blending berbeda-beda pada setiap surface dari box-nya. Program 10. void mydisplay(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glLoadIdentity(); glTranslatef(0.0,0.0f,z_pos); glRotatef(rot, 0, 1, 0); glEnable(GL_BLEND); glColor3f(1.0,0.0,0.0);
// Turn Blending On // Blending Color
glBindTexture(GL_TEXTURE_2D, texture[filter]); glBegin(GL_QUADS); // Front Face glNormal3f( 0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, // Back Face
1.0f); 1.0f); 1.0f); 1.0f);
31/117 glNormal3f( 0.0f, 0.0f,-1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, // Top Face glNormal3f( 0.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, // Bottom Face glNormal3f( 0.0f,-1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, // Right face glNormal3f( 1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, // Left Face glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, glEnd(); glFlush(); glutSwapBuffers(); }
-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);
-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);
32/117
Tutorial 07. Transparency Salah satu efek yang dapat ditampilkan dari blending adalah transparency, yaitu bagaimana membuat suatu permukaan tampak transparant. Hal ini dicapai dengan menggunakan warna blending putih (full-brightness) dengan transparansi satu dengan fungsi: glColor4f(1.0f, 1.0f, 1.0f, 0.5);// Full Brightness. glBlendFunc(GL_SRC_ALPHA,GL_ONE);
50% Alpha
Program 11 menampilkan perubahan yang terjadi pada void init() dan void mydisplay() pada Program 10 untuk menghasilkan suatu box tranparan. Coba untuk merubah texturenya dengan citra yang lebih sesuai agar didapat nuansa boks dengan material kaca dari pada boks “hantu”, seperti pada Gambar berikut:
Program 11 void mydisplay(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glLoadIdentity(); glTranslatef(0.0,0.0f,z_pos); glRotatef(rot, 0, 1, 0); glBindTexture(GL_TEXTURE_2D, texture[filter]);
glEnable(GL_BLEND); // Turn Blending On glDisable(GL_DEPTH_TEST); // Turn Depth Testing Off glBegin(GL_QUADS); // Front Face glNormal3f( 0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f,
1.0f); 1.0f); 1.0f);
33/117 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, // Back Face glNormal3f( 0.0f, 0.0f,-1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, // Top Face glNormal3f( 0.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, // Bottom Face glNormal3f( 0.0f,-1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, // Right face glNormal3f( 1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, // Left Face glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, glEnd();
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); -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);
glFlush(); glutSwapBuffers(); } void init() { if (!LoadGLTextures()) // Jump To Texture Loading Routine { return; // If Texture Didn't Load Return FALSE }
glEnable(GL_TEXTURE_2D); Enable Texture Mapping glShadeModel(GL_SMOOTH); Enable Smooth Shading glClearColor(0.0f, 0.0f, 0.0f, 0.5f); Background glClearDepth(1.0f); // Depth Buffer Setup glEnable(GL_DEPTH_TEST); Enables Depth Testing glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); Perspective Calculations
// // // Black
//
// Really Nice
34/117 glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); Ambient Light glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); Diffuse Light glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); Light glEnable(GL_LIGHT1); glColor4f(1.0f, 1.0f, 1.0f, 0.5); Brightness. 50% Alpha glBlendFunc(GL_SRC_ALPHA,GL_ONE);
// Setup The // Setup The // Position The
// Full
return; }
TUGAS: Pada Program 10 seluruh bagian dari boks bersifat transparan. Coba rubah agar hanya satu bagian saja yang menjadi transparan dengan texture yang berbeda dari permukaan boks lainnya.
35/117
Tutorial 08. Fog Kabut/fog adalah salah satu fitur OpenGL lain yang sering digunakan pada banyak kesempatan. Kabut digunakan dalam banyak kesempatan, antara lain: - mensimulasikan efek kabut - membatasi ruang pandang pengguna agar komputasi grafis yang diperlukan dapat dibatasi. Hal ini terutama dilakukan pada era-era ketika algoritma grafis masih dilakukan di CPU. Program 12 memperlihatkan bagaimana menggunakan fungsi-fungsi OpenGL yang terkait dengan fog. Program 12. // OpenGL // - Fog Filter // fogfilter=0 --> Nearest Filtered Texture // fogfilter=1 --> Linear Interpolation Texture // fogfilter=2 --> Mipmapped Texture #include #include #include #include #include #include
<windows.h> <stdio.h> <stdlib.h> <string.h> <stdarg.h>
#include "bmp.h" float z_pos=-5.0f; float rot=0.0f; GLfloat LightAmbient[]= GLfloat LightDiffuse[]= GLfloat LightPosition[]=
{ 0.5f, 0.5f, 0.5f, 1.0f }; { 1.0f, 1.0f, 1.0f, 1.0f }; { 0.0f, 0.0f, 2.0f, 1.0f };
/* array to hold texture handles */ GLuint filter; // Which Filter To Use GLuint texture[3]; // Storage For 3 Textures // Fog bool gp; GLuint fogfilter; GLuint fogMode[]={GL_EXP, GL_EXP2, GL_LINEAR}; GLfloat fogColor[4]={0.5f, 0.5f, 0.5f, 1.0f}; int LoadGLTextures() Load Bitmaps And Convert To Textures { int Status=FALSE; Status Indicator
//
//
36/117 AUX_RGBImageRec *TextureImage[1]; Storage Space For The Texture memset(TextureImage,0,sizeof(void *)*1); Pointer To NULL
// Create
// Set The
// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit if (TextureImage[0]=LoadBMP("glass.bmp")) { Status=TRUE; // Set The Status To TRUE glGenTextures(3, &texture[0]); Create Three Textures
//
// Create Nearest Filtered Texture glBindTexture(GL_TEXTURE_2D, texture[0]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]>data); // Create Linear Filtered Texture glBindTexture(GL_TEXTURE_2D, texture[1]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]>data); // Create MipMapped Texture glBindTexture(GL_TEXTURE_2D, texture[2]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_ NEAREST); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); } if (TextureImage[0]) If Texture Exists { if (TextureImage[0]->data) If Texture Image Exists { free(TextureImage[0]->data); The Texture Image Memory } free(TextureImage[0]); Free The Image Structure }
//
//
// Free
//
37/117 return Status; // Return The Status } void resize(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (float)width/(float)height, 1.0, 300.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void myTimeOut(int id) { // called if timer event // ...advance the state of animation incrementally... rot+=1; glutPostRedisplay(); // request redisplay glutTimerFunc(100, myTimeOut, 0); // request next timer event } void myKeyboard(unsigned char key,int x, int y) { if((key=='<')||(key==',')) z_pos-=0.1f; else if((key=='>')||(key=='.')) z_pos+=0.1f; else if((key=='F')||(key='f')) { filter+=1; if (filter>2) { filter=0; } printf("filter: %i \n",filter); } else if( (key=='G') || (key=='g')) { if(gp==FALSE) { gp=TRUE; fogfilter+=1; if (fogfilter>2) // Is fogfilter Greater Than 2? { fogfilter=0; // If So, Set fogfilter To Zero } glFogi (GL_FOG_MODE, fogMode[fogfilter]); // Fog Mode } else { gp=FALSE; } printf("filter: %i \n",fogfilter); } }
38/117
void mydisplay(void) { glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glLoadIdentity(); glTranslatef(0.0,0.0f,z_pos); glRotatef(rot, 0, 1, 0); glBindTexture(GL_TEXTURE_2D, texture[filter]); glBegin(GL_QUADS); // Front Face glNormal3f( 0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, // Back Face glNormal3f( 0.0f, 0.0f,-1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, // Top Face glNormal3f( 0.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, // Bottom Face glNormal3f( 0.0f,-1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, // Right face glNormal3f( 1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, // Left Face glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, glEnd(); glFlush(); glutSwapBuffers(); }
-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);
-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);
39/117
void init() { if (!LoadGLTextures()) Jump To Texture Loading Routine { return; If Texture Didn't Load Return FALSE }
//
//
glEnable(GL_TEXTURE_2D); Enable Texture Mapping glShadeModel(GL_SMOOTH); Enable Smooth Shading
// //
// FOG glClearColor(0.5f,0.5f,0.5f,1.0f); // We'll Clear To The Color Of The Fog glFogi(GL_FOG_MODE, fogMode[fogfilter]); // Fog Mode glFogfv(GL_FOG_COLOR, fogColor); // Set Fog Color glFogf(GL_FOG_DENSITY, 0.35f); // How Dense Will The Fog Be glHint(GL_FOG_HINT, GL_DONT_CARE); // Fog Hint Value glFogf(GL_FOG_START, 100.0f); // Fog Start Depth glFogf(GL_FOG_END, 1000.0f); // Fog End Depth glEnable(GL_FOG); glClearDepth(1.0f); // Depth Buffer Setup glEnable(GL_DEPTH_TEST); Enables Depth Testing glDepthFunc(GL_LEQUAL); The Type Of Depth Testing To Do glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); Ambient Light glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); Diffuse Light glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); Light glEnable(GL_LIGHT1); return; } int main(int argc, char** argv) { glutInit(&argc,argv); glutInitDisplayMode( GLUT_DOUBLE | GLUT_DEPTH ); glutInitWindowSize(500,500); glutInitWindowPosition(0,0); glutCreateWindow("simple"); // callbacks glutDisplayFunc(mydisplay); glutKeyboardFunc(myKeyboard); glutTimerFunc(100, myTimeOut, 0);
// //
// Setup The // Setup The // Position The
40/117 glutReshapeFunc(resize); init(); glutMainLoop(); return 0; }
Variabel global dibuat agar kita bisa mencoba-coba tiga macam mode fog. Pada void init() terdapat beberapa baris kode baru, yaitu: - glEnable(GL_FOG) yang melakukan inisialisasi fog - glFogi(GL_FOG_MODE, fogMode[fogfilter]); menentukan mode fog dengan variabel array fogMode. Variabel fogMode diinisialisasi dengan tiga nilai, yaitu GL_EXP, GL_EXP2, dan GL_LINEAR.: o GL_EXP – fog standar yang dirender pada keseluruhan screen. Efek fognya tidak terlalu terlihat namun jenis yang paling cepat dirender. o GL_EXP2 – fog jenis ini juga mirip dengan GL_EXP, hanya akan memberikan kesan dalam pada scene. o GL_LINEAR – pada fog jenis ini, efek fog diinterpolasi secara linier bergantung jarak dengan kamera sehingga didapat efek yang lebih baik. - glFogfv(GL_FOG_COLOR, fogcolor); menentukan warna dari fog. - glFogf(GL_FOG_DENSITY, 0.35f); menentukan seberapa padat kabutnya. Semakin tinggi nilainy, semakin pekat kabut yang dihasilkan. - The line glHint (GL_FOG_HINT, GL_DONT_CARE); memberikan informasi kepada OpenGL tentang bagaimana proses rendering ingin dilakukan. o gl_dont_care – terserah openGL o gl_nicest – perhitungan fog dilakukan per-pixel. o gl_fastest – perhitungan fog dilakukan per-vertex yang berarti lebih cepat tapi lebih tidak akurat. - glFogf(GL_FOG_START, 1.0f); menentukan jarak dari kamera ketika fog mulai. - glFogf(GL_FOG_END, 5.0f); menentukan sejauh mana kabut masih berefek. TUGAS: Rubah berbagai variabel di atas untuk memlihat apa pengaruhnya.
41/117
Tutorial 09. Obyek Kompleks Hingga tahap ini obyek yang digunakan masih berangkat dari pemodelan manual sehingga model yang dihasilkan masih sederhana. Kebutuhan untuk dapat menggunakan model yang lebih kompleks sangat besar. Ada banyak aplikasi perangkat lunak yang dapat digunakan untuk membuat model dengan lebih mudah. Contoh aplikasi untuk pembuatan model 3D antara lain: 3DS Max, Blender, Maya, Milkshape dengan dibantu aplikasi pemrosesan citra 2D seperti Photoshop, Corel dll. Format-format file standar model 3D yang biasa digunakan antara lain .3ds, .obj, dll. Persoalannya adalah bagaimana kita dapat menggunakan hasil dari aplikasi perangkat lunak tersebut dalam program kita. Untuk itu kita membutuhkan kode untuk membaca file tersebut dalam dan merepresentasikannya ke dalam program kita. Tutorial 09 memberikan ilustrasi tentang bagaimana kita membaca file model 3D dan menggunakannya dalam program berbasis openGL dan GLUT. Pertama file dalam format .txt yang kita buat sendiri dan kemudian dengan format file .3ds. Model dengan format 3DS sendiri awalnya merupakan file format pada versi-versi awal aplikasi 3D Studio dari Autodesk Animation Ltd. sebelum berubah menjadi format file yang lebih kompleks dengan aplikasinya menjadi 3D Studio Max. Format ini merupakan salah satu format yang paling banyak dikenal. Suatu set model 3D yang direpresentasikan oleh file .3ds biasanya ditemani oleh beberapa file citra untuk texture dari model tersebut. Format file-nya disusun dengan organisasi sebagai berikut: Pada Program 13 kita membuat file model 3D dengan format kita sendiri. File format kita adalah file .txt yang formatnya akan berupa aturan sebagai berikut: - Kita dapat memasukkan baris kosong semau kita agar file lebih mudah dibaca - Kita dapat memasukkan baris komentar dengan menambahkan “//” pada awal baris - Hanya terkait dengan satu file texture - Primitif yang digunakan hanya GL_TRIANGLES - Informasi awalnya akan berupa frasa NUMPOLLIES xx, dengan xx adalah jumlah primitif - Setelah itu kita harus memberi spesifikasi triangle-nya dengan menuliskan daftar vertex perbaris dengan format baris sbb. X Y Z S T dengan X,Y, Z adalah posisi vertex sedangkan S, T adalah pixel texture yang bersesuaian. Contoh datanya juga diberikan pada bagian bawah dari Program 13. Program 13 #include #include #include #include
<windows.h> <math.h> <stdio.h> <stdlib.h>
// Math Library Header File
42/117 #include #include #include #include const float float float
<string.h> <stdarg.h>
float piover180 = 0.0174532925f; heading; xpos; zpos;
GLfloat yrot; GLfloat walkbias = 0; GLfloat walkbiasangle = 0; GLfloat lookupdown = 0.0f; GLfloat z=0.0f;
// Y Rotation
GLuint GLuint
// Which Filter To Use // Storage For 3 Textures
filter; texture[3];
// Depth Into The Screen
typedef struct tagVERTEX { float x, y, z; float u, v; } VERTEX; typedef struct tagTRIANGLE { VERTEX vertex[3]; } TRIANGLE; typedef struct tagSECTOR { int numtriangles; TRIANGLE* triangle; } SECTOR; SECTOR sector1;
// Our Model Goes Here:
void readstr(FILE *f,char *string) { do { fgets(string, 255, f); } while ((string[0] == '/') || (string[0] == '\n')); return; } void SetupWorld() { float x, y, z, u, v; int numtriangles; FILE *filein; char oneline[255]; filein = fopen("data/world.txt", "rt"); To Load World Data From readstr(filein,oneline);
// File
43/117 sscanf(oneline, "NUMPOLLIES %d\n", &numtriangles); sector1.triangle = new TRIANGLE[numtriangles]; sector1.numtriangles = numtriangles; for (int loop = 0; loop < numtriangles; loop++) { for (int vert = 0; vert < 3; vert++) { readstr(filein,oneline); sscanf(oneline, "%f %f %f %f %f", &x, sector1.triangle[loop].vertex[vert].x sector1.triangle[loop].vertex[vert].y sector1.triangle[loop].vertex[vert].z sector1.triangle[loop].vertex[vert].u sector1.triangle[loop].vertex[vert].v } } fclose(filein); return;
&y, &z, &u, &v); = x; = y; = z; = u; = v;
} AUX_RGBImageRec *LoadBMP(char *Filename) Image { FILE *File=NULL; if (!Filename) Filename Was Given { return NULL; }
// Loads A Bitmap
// File Handle // Make Sure A
// If Not Return NULL
File=fopen(Filename,"r"); The File Exists
// Check To See If
if (File) Exist? { fclose(File); return auxDIBImageLoad(Filename); Return A Pointer } return NULL; Return NULL }
// Does The File
int LoadGLTextures() Convert To Textures { int Status=FALSE; AUX_RGBImageRec *TextureImage[1]; Space For The Texture memset(TextureImage,0,sizeof(void *)*1); NULL
// Close The Handle // Load The Bitmap And
// If Load Failed
// Load Bitmaps And
// Status Indicator // Create Storage
// Set The Pointer To
44/117 // Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit if (TextureImage[0]=LoadBMP("Data/Mud.bmp")) { Status=TRUE; // Set The Status To TRUE glGenTextures(3, &texture[0]);
// Create Three
Textures // Create Nearest Filtered Texture glBindTexture(GL_TEXTURE_2D, texture[0]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]>data); // Create Linear Filtered Texture glBindTexture(GL_TEXTURE_2D, texture[1]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]>data); // Create MipMapped Texture glBindTexture(GL_TEXTURE_2D, texture[2]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_ NEAREST); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); } if (TextureImage[0]) // If Texture Exists { if (TextureImage[0]->data) // If Texture Image Exists { free(TextureImage[0]->data); // Free The Texture Image Memory } free(TextureImage[0]);
// Free The Image
Structure } return Status;
// Return The Status
} void resize(int width, int height) Window { if (height==0) // Prevent A Divide By Zero By
// Resize And Initialize The GL
45/117 { height=1; // Making Height Equal One } glViewport(0,0,width,height); The Current Viewport
// Reset
glMatrixMode(GL_PROJECTION); The Projection Matrix glLoadIdentity(); Reset The Projection Matrix
// Select //
// Calculate The Aspect Ratio Of The Window gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f); glMatrixMode(GL_MODELVIEW); Select The Modelview Matrix glLoadIdentity(); Reset The Modelview Matrix } void init() Setup For OpenGL Goes Here { if (!LoadGLTextures()) Jump To Texture Loading Routine { return; If Texture Didn't Load Return FALSE } glEnable(GL_TEXTURE_2D); Enable Texture Mapping glBlendFunc(GL_SRC_ALPHA,GL_ONE); The Blending Function For Translucency glClearColor(0.0f, 0.0f, 0.0f, 0.0f); Will Clear The Background Color To Black glClearDepth(1.0); // Enables Clearing Of The Depth Buffer glDepthFunc(GL_LESS); The Type Of Depth Test To Do glEnable(GL_DEPTH_TEST); Enables Depth Testing glShadeModel(GL_SMOOTH); Enables Smooth Color Shading glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); Perspective Calculations
// //
// All
//
//
// // Set // This
// // // // Really Nice
SetupWorld(); return; Initialization Went OK }
void myTimeOut(int id)
//
46/117 { // called if timer event // ...advance the state of animation incrementally... //rot+=1; glutPostRedisplay(); // request redisplay glutTimerFunc(100, myTimeOut, 0); // request next timer event } void myKeyboard(unsigned char key,int x, int y) { } void mySpecialKeyboard(int key,int x, int y) { if(key==GLUT_KEY_UP) { xpos -= (float)sin(heading*piover180) * 0.05f; zpos -= (float)cos(heading*piover180) * 0.05f; if (walkbiasangle >= 359.0f) { walkbiasangle = 0.0f; } else { walkbiasangle+= 10; } walkbias = (float)sin(walkbiasangle * piover180)/20.0f; } else if(key==GLUT_KEY_DOWN) { xpos += (float)sin(heading*piover180) * 0.05f; zpos += (float)cos(heading*piover180) * 0.05f; if (walkbiasangle <= 1.0f) { walkbiasangle = 359.0f; } else { walkbiasangle-= 10; } walkbias = (float)sin(walkbiasangle * piover180)/20.0f; } else if(key==GLUT_KEY_RIGHT) { heading -= 1.0f; yrot = heading; } else if(key==GLUT_KEY_LEFT) { heading += 1.0f; yrot = heading; } else if(key==GLUT_KEY_PAGE_UP) {
47/117 z-=0.02f; lookupdown-= 1.0f; } else if(key==GLUT_KEY_PAGE_DOWN) { z+=0.02f; lookupdown+= 1.0f; } } void mydisplay(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); Screen And The Depth Buffer glLoadIdentity(); Reset The View GLfloat GLfloat GLfloat GLfloat GLfloat
x_m, y_m, z_m, u_m, v_m; xtrans = -xpos; ztrans = -zpos; ytrans = -walkbias-0.25f; sceneroty = 360.0f - yrot;
int numtriangles; glRotatef(lookupdown,1.0f,0,0); glRotatef(sceneroty,0,1.0f,0); glTranslatef(xtrans, ytrans, ztrans); glBindTexture(GL_TEXTURE_2D, texture[filter]); numtriangles = sector1.numtriangles; // Process Each Triangle for (int loop_m = 0; loop_m < numtriangles; loop_m++) { glBegin(GL_TRIANGLES); glNormal3f( 0.0f, 0.0f, 1.0f); x_m = sector1.triangle[loop_m].vertex[0].x; y_m = sector1.triangle[loop_m].vertex[0].y; z_m = sector1.triangle[loop_m].vertex[0].z; u_m = sector1.triangle[loop_m].vertex[0].u; v_m = sector1.triangle[loop_m].vertex[0].v; glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m); x_m = sector1.triangle[loop_m].vertex[1].x; y_m = sector1.triangle[loop_m].vertex[1].y; z_m = sector1.triangle[loop_m].vertex[1].z; u_m = sector1.triangle[loop_m].vertex[1].u; v_m = sector1.triangle[loop_m].vertex[1].v; glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m); x_m y_m z_m u_m v_m
= = = = =
sector1.triangle[loop_m].vertex[2].x; sector1.triangle[loop_m].vertex[2].y; sector1.triangle[loop_m].vertex[2].z; sector1.triangle[loop_m].vertex[2].u; sector1.triangle[loop_m].vertex[2].v;
// Clear The //
48/117 glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m); glEnd(); } glFlush(); glutSwapBuffers(); } int main(int argc, char** argv) { glutInit(&argc,argv); glutInitDisplayMode( GLUT_DOUBLE | GLUT_DEPTH ); glutInitWindowSize(500,500); glutInitWindowPosition(0,0); glutCreateWindow("simple"); // callbacks glutDisplayFunc(mydisplay); glutKeyboardFunc(myKeyboard); glutSpecialFunc(mySpecialKeyboard); glutTimerFunc(100, myTimeOut, 0); glutReshapeFunc(resize); init(); glutMainLoop(); return 0; }
World.txt NUMPOLLIES 36 // Floor 1 -3.0 0.0 -3.0 0.0 6.0 -3.0 0.0 3.0 0.0 0.0 3.0 0.0 3.0 6.0 0.0 -3.0 3.0 3.0
0.0 -3.0 0.0 6.0 0.0 -3.0 6.0 6.0 0.0 3.0 6.0 0.0
// Ceiling 1 -3.0 1.0 -3.0 -3.0 1.0 3.0 3.0 1.0 3.0 -3.0 1.0 -3.0 3.0 1.0 -3.0 3.0 1.0 3.0 // A1
0.0 0.0 6.0 0.0 6.0 6.0
6.0 0.0 0.0 6.0 6.0 0.0
49/117 -2.0 -2.0 -0.5 -2.0 -0.5 -0.5
1.0 0.0 0.0 1.0 1.0 0.0
-2.0 -2.0 -2.0 -2.0 -2.0 -2.0
0.0 0.0 1.5 0.0 1.5 1.5
1.0 0.0 0.0 1.0 1.0 0.0
1.0 0.0 0.0 1.0 1.0 0.0
-2.0 -2.0 -2.0 -2.0 -2.0 -2.0
2.0 2.0 0.5 2.0 0.5 0.5
1.0 0.0 0.0 1.0 1.0 0.0
1.0 0.0 0.0 1.0 1.0 0.0
2.0 2.0 2.0 2.0 2.0 0.5 2.0 2.0 2.0 0.5 2.0 0.5
1.0 0.0 0.0 1.0 1.0 0.0
1.0 0.0 0.0 1.0 1.0 0.0
2.0 2.0 2.0 2.0 2.0 0.5 2.0 2.0 2.0 0.5 2.0 0.5
1.0 0.0 0.0 1.0 1.0 0.0
1.0 0.0 0.0 1.0 1.0 0.0
-2.0 0.0 -2.0 0.0 -0.5 1.5 -2.0 0.0 -0.5 1.5 -0.5 1.5
1.0 0.0 0.0 1.0 1.0 0.0
2.0 2.0 1.0 2.0 2.0 0.0 0.5 0.5 0.0 2.0 2.0 1.0 0.5 0.5 1.0 0.5 0.5 0.0
// A2 2.0 2.0 0.5 2.0 0.5 0.5 // B1 -2.0 -2.0 -0.5 -2.0 -0.5 -0.5 // B2 2.0 2.0 0.5 2.0 0.5 0.5 // C1 -2.0 -2.0 -2.0 -2.0 -2.0 -2.0
1.0 0.0 0.0 1.0 1.0 0.0
// C2 -2.0 -2.0 -2.0 -2.0 -2.0 -2.0 // D1 2.0 2.0 2.0
1.0 0.0 0.0
-2.0 0.0 1.0 -2.0 0.0 0.0 -0.5 1.5 0.0
50/117 2.0 2.0 2.0
1.0 1.0 0.0
-2.0 0.0 1.0 -0.5 1.5 1.0 -0.5 1.5 0.0
// D2 2.0 2.0 2.0 2.0 2.0 2.0
1.0 0.0 0.0 1.0 1.0 0.0
2.0 2.0 1.0 2.0 2.0 0.0 0.5 0.5 0.0 2.0 2.0 1.0 0.5 0.5 1.0 0.5 0.5 0.0
// Upper hallway - L -0.5 1.0 -3.0 0.0 1.0 -0.5 0.0 -3.0 0.0 0.0 -0.5 0.0 -2.0 1.0 0.0 -0.5 1.0 -3.0 0.0 1.0 -0.5 1.0 -2.0 1.0 1.0 -0.5 0.0 -2.0 1.0 0.0 // Upper hallway - R 0.5 1.0 -3.0 0.0 1.0 0.5 0.0 -3.0 0.0 0.0 0.5 0.0 -2.0 1.0 0.0 0.5 1.0 -3.0 0.0 1.0 0.5 1.0 -2.0 1.0 1.0 0.5 0.0 -2.0 1.0 0.0 // Lower hallway - L -0.5 1.0 3.0 0.0 1.0 -0.5 0.0 3.0 0.0 0.0 -0.5 0.0 2.0 1.0 0.0 -0.5 1.0 3.0 0.0 1.0 -0.5 1.0 2.0 1.0 1.0 -0.5 0.0 2.0 1.0 0.0 // Lower hallway - R 0.5 1.0 3.0 0.0 1.0 0.5 0.0 3.0 0.0 0.0 0.5 0.0 2.0 1.0 0.0 0.5 1.0 3.0 0.0 1.0 0.5 1.0 2.0 1.0 1.0 0.5 0.0 2.0 1.0 0.0
// Left hallway - Lw -3.0 -3.0 -2.0 -3.0 -2.0 -2.0
1.0 0.0 0.0 1.0 1.0 0.0
0.5 1.0 1.0 0.5 1.0 0.0 0.5 0.0 0.0 0.5 1.0 1.0 0.5 0.0 1.0 0.5 0.0 0.0
// Left hallway - Hi
51/117 -3.0 -3.0 -2.0 -3.0 -2.0 -2.0
1.0 0.0 0.0 1.0 1.0 0.0
-0.5 1.0 1.0 -0.5 1.0 0.0 -0.5 0.0 0.0 -0.5 1.0 1.0 -0.5 0.0 1.0 -0.5 0.0 0.0
// Right hallway - Lw 3.0 3.0 2.0 3.0 2.0 2.0
1.0 0.0 0.0 1.0 1.0 0.0
0.5 1.0 1.0 0.5 1.0 0.0 0.5 0.0 0.0 0.5 1.0 1.0 0.5 0.0 1.0 0.5 0.0 0.0
// Right hallway - Hi 3.0 3.0 2.0 3.0 2.0 2.0
1.0 -0.5 1.0 1.0 0.0 -0.5 1.0 0.0 0.0 -0.5 0.0 0.0 1.0 -0.5 1.0 1.0 1.0 -0.5 0.0 1.0 0.0 -0.5 0.0 0.0
Perhatikan loop rendering yang terjadi pada void mydisplay() yang memperlihatkan bagaimana suatu model di render secara otomatis. TUGAS: Coba untuk merancang model 3D dengan membuat file dengan format di atas. Program 14 memberi ilustrasi tentang bagaimana suatu file .3ds dibaca dan dirender. Carilah model 3D dalam format .3ds lengkap dengan texture yang berkaitan. Perhatikan pula loop renderingnya. TUGAS: Dari membaca file 3dsloader.h dan 3dsloader.cpp, terangkan format dari file .3ds yang dapat dibaca oleh Program 14. Program 14. Tutorial4.h /********************************************************** * * TYPES DECLARATION * *********************************************************/ #define MAX_VERTICES 8000 // Max number of vertices (for each object) #define MAX_POLYGONS 8000 // Max number of polygons (for each object) // Our vertex type typedef struct{
52/117 float x,y,z; }vertex_type; // The polygon (triangle), 3 numbers that aim 3 vertices typedef struct{ int a,b,c; }polygon_type; // The mapcoord type, 2 texture coordinates for each vertex typedef struct{ float u,v; }mapcoord_type; // The object type typedef struct { char name[20]; int vertices_qty; int polygons_qty; vertex_type vertex[MAX_VERTICES]; polygon_type polygon[MAX_POLYGONS]; mapcoord_type mapcoord[MAX_VERTICES]; int id_texture; } obj_type, *obj_type_ptr;
Texture.h extern int num_texture; extern int LoadBitmap(char *filename);
Texture.cpp #include #include #include #include
<stdio.h> <windows.h> "texture.h"
/********************************************************** * * VARIABLES DECLARATION * *********************************************************/ int num_texture=-1; //Counter to keep track of the last loaded texture /********************************************************** * * FUNCTION LoadBitmap(char *) * * This function loads a bitmap file and return the OpenGL reference ID to use that texture
53/117 * *********************************************************/ int LoadBitmap(char *filename) { int i, j=0; //Index variables FILE *l_file; //File pointer unsigned char *l_texture; //The pointer to the memory zone in which we will load the texture // windows.h gives us these types to work with the Bitmap files BITMAPFILEHEADER fileheader; BITMAPINFOHEADER infoheader; RGBTRIPLE rgb; num_texture++; // The counter of the current texture is increased if( (l_file = fopen(filename, "rb"))==NULL) return (-1); // Open the file for reading fread(&fileheader, sizeof(fileheader), 1, l_file); // Read the fileheader fseek(l_file, sizeof(fileheader), SEEK_SET); // Jump the fileheader fread(&infoheader, sizeof(infoheader), 1, l_file); // and read the infoheader // Now we need to allocate the memory for our image (width * height * color deep) l_texture = (byte *) malloc(infoheader.biWidth * infoheader.biHeight * 4); // And fill it with zeros memset(l_texture, 0, infoheader.biWidth * infoheader.biHeight * 4); // At this point we can read every pixel of the image for (i=0; i < infoheader.biWidth*infoheader.biHeight; i++) { // We load an RGB value from the file fread(&rgb, sizeof(rgb), 1, l_file); // And store it l_texture[j+0] = l_texture[j+1] = l_texture[j+2] = l_texture[j+3] = j += 4; // Go to
rgb.rgbtRed; // Red component rgb.rgbtGreen; // Green component rgb.rgbtBlue; // Blue component 255; // Alpha value the next position
} fclose(l_file); // Closes the file stream glBindTexture(GL_TEXTURE_2D, num_texture); // Bind the ID texture specified by the 2nd parameter // The next commands sets the texture parameters glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // If the u,v coordinates overflow the range 0,1 the image is repeated glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
54/117 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // The magnification function ("linear" produces better results) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); //The minifying function glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // We don't combine the color with the original surface color, use only the texture map. // Finally we define the 2d texture glTexImage2D(GL_TEXTURE_2D, 0, 4, infoheader.biWidth, infoheader.biHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, l_texture); // And create 2d mipmaps for the minifying function gluBuild2DMipmaps(GL_TEXTURE_2D, 4, infoheader.biWidth, infoheader.biHeight, GL_RGBA, GL_UNSIGNED_BYTE, l_texture); free(l_texture); // Free the memory we used to load the texture return (num_texture); // Returns the current texture OpenGL ID }
3dsloader.h /********************************************************** * * FUNCTION Load3DS (obj_type_ptr, char *) * * This function loads a mesh from a 3ds file. * Please note that we are loading only the vertices, polygons and mapping lists. * If you need to load meshes with advanced features as for example: * multi objects, materials, lights and so on, you must insert other chunk parsers. * *********************************************************/ extern char Load3DS (obj_type_ptr ogg, char *filename);
3dsloader.cpp #include #include #include #include #include #include
<stdio.h> <stdlib.h> "tutorial4.h" "3dsloader.h"
/********************************************************** * * FUNCTION Load3DS (obj_type_ptr, char *) * * This function loads a mesh from a 3ds file. * Please note that we are loading only the vertices, polygons and mapping lists.
55/117 * If you need to load meshes with advanced features as for example: * multi objects, materials, lights and so on, you must insert other chunk parsers. * *********************************************************/ char Load3DS (obj_type_ptr p_object, char *p_filename) { int i; //Index variable FILE *l_file; //File pointer unsigned short l_chunk_id; //Chunk identifier unsigned int l_chunk_lenght; //Chunk lenght unsigned char l_char; //Char variable unsigned short l_qty; //Number of elements in each chunk unsigned short l_face_flags; //Flag that stores some face information if ((l_file=fopen (p_filename, "rb"))== NULL) return 0; //Open the file while (ftell (l_file) < filelength (fileno (l_file))) //Loop to scan the whole file { //getche(); //Insert this command for debug (to wait for keypress for each chuck reading) fread (&l_chunk_id, 2, 1, l_file); //Read the chunk header printf("ChunkID: %x\n",l_chunk_id); fread (&l_chunk_lenght, 4, 1, l_file); //Read the lenght of the chunk printf("ChunkLenght: %x\n",l_chunk_lenght); switch (l_chunk_id) { //----------------- MAIN3DS ----------------// Description: Main chunk, contains all the other chunks // Chunk ID: 4d4d // Chunk Lenght: 0 + sub chunks //------------------------------------------case 0x4d4d: break; //----------------- EDIT3DS ----------------// Description: 3D Editor chunk, objects layout info // Chunk ID: 3d3d (hex) // Chunk Lenght: 0 + sub chunks //------------------------------------------case 0x3d3d: break; //--------------- EDIT_OBJECT --------------// Description: Object block, info for each object
56/117 // Chunk ID: 4000 (hex) // Chunk Lenght: len(object name) + sub chunks //------------------------------------------case 0x4000: i=0; do { fread (&l_char, 1, 1, l_file); p_object->name[i]=l_char; i++; }while(l_char != '\0' && i<20); break; //--------------- OBJ_TRIMESH --------------// Description: Triangular mesh, contains chunks for 3d mesh info // Chunk ID: 4100 (hex) // Chunk Lenght: 0 + sub chunks //------------------------------------------case 0x4100: break; //--------------- TRI_VERTEXL --------------// Description: Vertices list // Chunk ID: 4110 (hex) // Chunk Lenght: 1 x unsigned short (number of vertices) // + 3 x float (vertex coordinates) x (number of vertices) // + sub chunks //------------------------------------------case 0x4110: fread (&l_qty, sizeof (unsigned short), 1, l_file); p_object->vertices_qty = l_qty; printf("Number of vertices: %d\n",l_qty); for (i=0; ivertex[i].x, sizeof(float), 1, l_file); printf("Vertices list x: %f\n",p_object>vertex[i].x); fread (&p_object->vertex[i].y, sizeof(float), 1, l_file); printf("Vertices list y: %f\n",p_object>vertex[i].y); fread (&p_object->vertex[i].z, sizeof(float), 1, l_file); printf("Vertices list z: %f\n",p_object>vertex[i].z); } break; //--------------- TRI_FACEL1 ---------------// Description: Polygons (faces) list // Chunk ID: 4120 (hex) // Chunk Lenght: 1 x unsigned short (number of
57/117 polygons) // + 3 x unsigned short (polygon points) x (number of polygons) // + sub chunks //------------------------------------------case 0x4120: fread (&l_qty, sizeof (unsigned short), 1, l_file); p_object->polygons_qty = l_qty; printf("Number of polygons: %d\n",l_qty); for (i=0; ipolygon[i].a, sizeof (unsigned short), 1, l_file); printf("Polygon point a: %d\n",p_object>polygon[i].a); fread (&p_object->polygon[i].b, sizeof (unsigned short), 1, l_file); printf("Polygon point b: %d\n",p_object>polygon[i].b); fread (&p_object->polygon[i].c, sizeof (unsigned short), 1, l_file); printf("Polygon point c: %d\n",p_object>polygon[i].c); fread (&l_face_flags, sizeof (unsigned short), 1, l_file); printf("Face flags: %x\n",l_face_flags); } break; //------------- TRI_MAPPINGCOORS -----------// Description: Vertices list // Chunk ID: 4140 (hex) // Chunk Lenght: 1 x unsigned short (number of mapping points) // + 2 x float (mapping coordinates) x (number of mapping points) // + sub chunks //------------------------------------------case 0x4140: fread (&l_qty, sizeof (unsigned short), 1, l_file); for (i=0; imapcoord[i].u, sizeof (float), 1, l_file); printf("Mapping list u: %f\n",p_object>mapcoord[i].u); fread (&p_object->mapcoord[i].v, sizeof (float), 1, l_file); printf("Mapping list v: %f\n",p_object>mapcoord[i].v); } break; //----------- Skip unknow chunks -----------//We need to skip all the chunks that currently we
58/117 don't use //We use the chunk lenght information to set the file pointer //to the same level next chunk //------------------------------------------default: fseek(l_file, l_chunk_lenght-6, SEEK_CUR); } } fclose (l_file); // Closes the file stream return (1); // Returns ok }
Main.h #define MAX_VERTICES 8000 // Max number of vertices (for each object) #define MAX_POLYGONS 8000 // Max number of polygons (for each object) // Our vertex type typedef struct{ float x,y,z; }vertex_type; // The polygon (triangle), 3 numbers that aim 3 vertices typedef struct{ int a,b,c; }polygon_type; // The mapcoord type, 2 texture coordinates for each vertex typedef struct{ float u,v; }mapcoord_type; // The object type typedef struct { char name[20]; int vertices_qty; int polygons_qty; vertex_type vertex[MAX_VERTICES]; polygon_type polygon[MAX_POLYGONS]; mapcoord_type mapcoord[MAX_VERTICES]; int id_texture; } obj_type, *obj_type_ptr;
Main.cpp #include #include #include #include #include
<windows.h> "tutorial4.h" "texture.h" "3dsloader.h"
59/117 // The width and height of your window, change them as you like int screen_width=640; int screen_height=480; // Absolute rotation each frame double rotation_x=0, double rotation_y=0, double rotation_z=0,
values (0-359 degrees) and rotation increments for rotation_x_increment=0.1; rotation_y_increment=0.05; rotation_z_increment=0.03;
// Flag for rendering as lines or filled polygons int filling=1; //0=OFF 1=ON //Now the object is generic, the cube has annoyed us a little bit, or not? obj_type object; void init(void) { glClearColor(0.0, 0.0, 0.0, 0.0); // This clear the background color to black glShadeModel(GL_SMOOTH); // Type of shading for the polygons // Viewport transformation glViewport(0,0,screen_width,screen_height); // Projection transformation glMatrixMode(GL_PROJECTION); // Specifies which matrix stack is the target for matrix operations glLoadIdentity(); // We initialize the projection matrix as identity gluPerspective(45.0f,(GLfloat)screen_width/(GLfloat)screen_height,10.0f, 10000.0f); // We define the "viewing volume" glEnable(GL_DEPTH_TEST); // We enable the depth test (also called z buffer) glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); // Polygon rasterization mode (polygon filled) glEnable(GL_TEXTURE_2D); // This Enable the Texture mapping Load3DS (&object,"spaceship.3ds"); object.id_texture=LoadBitmap("spaceshiptexture.bmp"); // The Function LoadBitmap() return the current texture ID // If the last function returns -1 it means the file was not found so we exit from the program if (object.id_texture==-1) { MessageBox(NULL,"Image file: spaceshiptexture.bmp not found", "Zetadeck",MB_OK | MB_ICONERROR); exit (0); } } void resize (int width, int height)
60/117 { screen_width=width; // We obtain the new screen width values and store it screen_height=height; // Height value glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // We clear both the color and the depth buffer so to draw the next frame glViewport(0,0,screen_width,screen_height); // Viewport transformation glMatrixMode(GL_PROJECTION); // Projection transformation glLoadIdentity(); // We initialize the projection matrix as identity gluPerspective(45.0f,(GLfloat)screen_width/(GLfloat)screen_height,10.0f, 10000.0f); glutPostRedisplay (); // This command redraw the scene (it calls the same routine of glutDisplayFunc) } void keyboard (unsigned char key, int x, int y) { switch (key) { case ' ': rotation_x_increment=0; rotation_y_increment=0; rotation_z_increment=0; break; case 'r': case 'R': if (filling==0) { glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); // Polygon rasterization mode (polygon filled) filling=1; } else { glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); // Polygon rasterization mode (polygon outlined) filling=0; } break; } } void keyboard_s (int key, int x, int y) { switch (key) { case GLUT_KEY_UP: rotation_x_increment = rotation_x_increment +0.005; break; case GLUT_KEY_DOWN:
61/117 rotation_x_increment = rotation_x_increment -0.005; break; case GLUT_KEY_LEFT: rotation_y_increment = rotation_y_increment +0.005; break; case GLUT_KEY_RIGHT: rotation_y_increment = rotation_y_increment -0.005; break; } } void display(void) { int l_index; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); // Modeling transformation glLoadIdentity(); // Initialize the model matrix as identity glTranslatef(0.0,0.0,-300); // We move the object forward (the model matrix is multiplied by the translation matrix) rotation_x = rotation_x + rotation_x_increment; rotation_y = rotation_y + rotation_y_increment; rotation_z = rotation_z + rotation_z_increment; if (rotation_x > 359) rotation_x = 0; if (rotation_y > 359) rotation_y = 0; if (rotation_z > 359) rotation_z = 0; glRotatef(rotation_x,1.0,0.0,0.0); // Rotations of the object (the model matrix is multiplied by the rotation matrices) glRotatef(rotation_y,0.0,1.0,0.0); glRotatef(rotation_z,0.0,0.0,1.0); glBindTexture(GL_TEXTURE_2D, object.id_texture); // We set the active texture glBegin(GL_TRIANGLES); // glBegin and glEnd delimit the vertices that define a primitive (in our case triangles) for (l_index=0;l_index
62/117 glVertex3f( object.vertex[ object.polygon[l_index].b ].x, object.vertex[ object.polygon[l_index].b ].y, object.vertex[ object.polygon[l_index].b ].z); //----------------- THIRD VERTEX ----------------// Texture coordinates of the third vertex glTexCoord2f( object.mapcoord[ object.polygon[l_index].c ].u, object.mapcoord[ object.polygon[l_index].c ].v); // Coordinates of the Third vertex glVertex3f( object.vertex[ object.polygon[l_index].c ].x, object.vertex[ object.polygon[l_index].c ].y, object.vertex[ object.polygon[l_index].c ].z); } glEnd(); glFlush(); // This force the execution of OpenGL commands glutSwapBuffers(); // In double buffered mode we invert the positions of the visible buffer and the writing buffer } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(screen_width,screen_height); glutInitWindowPosition(0,0); glutCreateWindow("simple"); glutDisplayFunc(display); glutIdleFunc(display); glutReshapeFunc (resize); glutKeyboardFunc (keyboard); glutSpecialFunc (keyboard_s); init(); glutMainLoop(); return(0); }
Tutorial 10. Particle System Salah satu teknik yang banyak digunakan dalam grafika komputer adalah sistem partikel. Banyak fenomena dapat dimodelkan dengan sistem partikel antara lain air, api, pasir dan lain-lain. Lebih jauh lagi, sistem partikel merupakan dasar dari teknik-teknik pemodelan yang disimulasikan dalam konsep simulasi mikro seperti SPH, DMS dan lain-lain. Sistem partikel, sesuai dengan namanya, adalah kumpulan obyek yang disebut partikel. Partikel di sini dapat divisualisasikan sebagai sekedar titik, bola, atau merupakan bagian dari obyek seperti triangle-soup dan lain-lain. Setiap partikel memiliki statusnya sendirisendiri, yaitu minimal memiliki nilai posisi. Sebagai kumpulan, dengan memodelkan interaksi antar partikel, kita dapat memodelkan berbagai macam fenomena seperti disebutkan di atas. Program 16 memberi ilustrasi tentang bagaimana sistem partikel bekerja. Pada Program 16, setiap partikel memiliki beberapa status yaitu posisi, kecepatan, warna, umur dan
63/117 kecepatan penurunan umur serta indikator aktif (Particle.h). Setiap partikel memiliki prosedur pembuatan, inisialisasi dan prosedur evolusi selama hidupnya. Perhatikan bagaimana kumpulan partikel ini dirender pada void mydisplay(). Program 16 Particle.h #ifndef PARTICLE_H_ typedef struct { float lifetime; float decay; float r,g,b; float xpos,ypos,zpos; float xspeed,yspeed,zspeed; boolean active; } PARTICLE;
// // // // // //
total lifetime of the particle decay speed of the particle color values of the particle position of the particle speed of the particle is particle active or not?
void CreateParticle(int i); void InitParticle(); void EvolveParticle(); #endif
Particle.cpp #include #include #include #include
<windows.h> <stdlib.h> <math.h> "particle.h"
const maxparticle=2000; PARTICLE particle[maxparticle];
// set maximum number of particles
void CreateParticle(int i) { particle[i].lifetime= (float)(500000*rand()/RAND_MAX)/500000.0; particle[i].decay=0.001; particle[i].r = 0.7; particle[i].g = 0.7; particle[i].b = 1.0; particle[i].xpos= 0.0; particle[i].ypos= 0.0; particle[i].zpos= 0.0; particle[i].xspeed = 10*(0.0005-(float)(100*rand()/RAND_MAX)/100000.0); particle[i].yspeed = 0.01-(float)(100*rand()/RAND_MAX)/100000.0; particle[i].zspeed = 0.0005-(float)(100*rand()/RAND_MAX)/100000.0; particle[i].active = true; } //--------------------------------------------------------------------------void InitParticle() {
64/117 for(int i=0;i<=maxparticle;i++){ // initialize the particle parameters CreateParticle(i); particle[i].active = false; // set all particles inactive } } //--------------------------------------------------------------------------void EvolveParticle() { for(int i=0;i<=maxparticle;i++){ // evolve the particle parameters particle[i].lifetime-=particle[i].decay; particle[i].xpos+=particle[i].xspeed; particle[i].ypos+=particle[i].yspeed; particle[i].zpos+=particle[i].zspeed; particle[i].yspeed-=0.00010; } }
Program.cpp // OpenGL // - Texture Mapping Magnification Filter // filter=0 --> Nearest Filtered Texture // filter=1 --> Linear Interpolation Texture // filter=2 --> Mipmapped Texture #include #include #include #include #include #include
<windows.h> <stdio.h> <stdlib.h> <string.h> <stdarg.h>
#include "bmp.h" #include "particle.h" float z_pos=-5.0f; float rot=0.0f; GLfloat LightAmbient[]= GLfloat LightDiffuse[]= GLfloat LightPosition[]=
{ 0.5f, 0.5f, 0.5f, 1.0f }; { 1.0f, 1.0f, 1.0f, 1.0f }; { 0.0f, 0.0f, 2.0f, 1.0f };
/* array to hold texture handles */ GLuint filter; // Which Filter To Use GLuint texture[1]; // Storage For 3 Textures /* Particle System */ extern const maxparticle=2000; particles extern PARTICLE particle[maxparticle]; int LoadGLTextures() Load Bitmaps And Convert To Textures { int Status=FALSE;
// set maximum number of
//
//
65/117 Status Indicator AUX_RGBImageRec *TextureImage[1]; Storage Space For The Texture memset(TextureImage,0,sizeof(void *)*1); Pointer To NULL
// Create
// Set The
// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit if (TextureImage[0]=LoadBMP("particle.bmp")) { Status=TRUE; // Set The Status To TRUE glGenTextures(1, &texture[0]); Create Three Textures
//
// Create Nearest Filtered Texture glBindTexture(GL_TEXTURE_2D, texture[0]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); } if (TextureImage[0]) Texture Exists { if (TextureImage[0]->data) Texture Image Exists { free(TextureImage[0]->data); Texture Image Memory }
// If
// If
// Free The
free(TextureImage[0]); Free The Image Structure }
//
glEnable(GL_TEXTURE_2D); Enable Texture Mapping
//
return Status; // Return The Status } void resize(int width, int height) { glViewport(0, 0, width, height); //glMatrixMode(GL_PROJECTION); //glLoadIdentity(); //gluPerspective(45.0, (float)width/(float)height, 0.0, 300.0); glMatrixMode(GL_PROJECTION); projection matrix glLoadIdentity();
// the following operations affect the // restore matrix to original state
66/117 glOrtho( -0.60,0.60,-0.20,0.60,-0.60,0.60); // defines the viewing volume
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void myTimeOut(int id) { // called if timer event // ...advance the state of animation incrementally... rot+=1; glutPostRedisplay(); // request redisplay glutTimerFunc(100, myTimeOut, 0); // request next timer event } void myKeyboard(unsigned char key,int x, int y) { if((key=='<')||(key==',')) z_pos-=0.1f; else if((key=='>')||(key=='.')) z_pos+=0.1f; else if((key=='F')||(key='f')) { filter+=1; if (filter>2) { filter=0; } printf("filter: %i",filter); } } void mydisplay(void) { glClear(GL_COLOR_BUFFER_BIT); //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glLoadIdentity(); glRotatef(50.0,1.0,0.0,0.0);
// show scene from top front
glBindTexture(GL_TEXTURE_2D,texture[0]); // choose particle texture for (int i=0;i<=maxparticle;i++){ if(particle[i].ypos<0.0) particle[i].lifetime=0.0; if((particle[i].active==true) && (particle[i].lifetime>0.0)){ glColor3f(particle[i].r,particle[i].g,particle[i].b); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(0.0,1.0); glVertex3f(particle[i].xpos+0.005, particle[i].ypos+0.005, particle[i].zpos+0.0); // top right glTexCoord2f(0.0,0.0); glVertex3f(particle[i].xpos-0.005, particle[i].ypos+0.005, particle[i].zpos+0.0); // top left glTexCoord2f(1.0,1.0); glVertex3f(particle[i].xpos+0.005, particle[i].ypos-0.005, particle[i].zpos+0.0); // bottom right glTexCoord2f(1.0,0.0); glVertex3f(particle[i].xpos-0.005, particle[i].ypos-0.005, particle[i].zpos+0.0); // bottom left glEnd();
67/117 } else CreateParticle(i); } EvolveParticle();
glFlush(); glutSwapBuffers(); } void init() { glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE);
// deactivate hidden surface removal // show backside of polygons
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); Background
// Black
glColor4f(1.0f, 1.0f, 1.0f, 0.5); Brightness. 50% Alpha
// Full
if (!LoadGLTextures()) Jump To Texture Loading Routine { return; Texture Didn't Load Return FALSE } InitParticle(); glMatrixMode(GL_MODELVIEW); return; } int main(int argc, char** argv) { glutInit(&argc,argv); glutInitDisplayMode( GLUT_DOUBLE); glutInitWindowSize(500,500); glutInitWindowPosition(0,0); glutCreateWindow("simple"); // callbacks glutDisplayFunc(mydisplay); glutKeyboardFunc(myKeyboard); glutTimerFunc(100, myTimeOut, 0); glutReshapeFunc(resize); init(); glutMainLoop(); return 0; }
//
// If
68/117
69/117
Tutorial 11. Lighting Visualisasi tentu saja tidak akan terjadi bila tidak ada cahaya. Pencahayaan merupakan esensi dari visualisasi dan merupakan topik yang sangat kompleks. Hingga tahap ini lingkungan diberi pencahayaan default/standar dengan cahaya lingkungan (ambient) yang sama pada setiap titik. Kondisi default/standar dapat dicapai kapan saja dengan mematikan status Lighting menjadi disabled dengan glDisable(GL_LIGHT0). Dalam pencahayaan, ada dua hal yang menentukan tampilan suatu obyek, yaitu: - Sumber cahaya dan pengaruh lingkungan terhadap cahaya o Lokasi sumber cahaya o Arah pencahayaan dari sumber cahaya (omni, spot) o Komponen pengaruh lingkungan terhadap cahaya (ambient, diffuse, specular) - Material dari obyek, yang memodelkan bagaimana material bereaksi terhadap sumber cahaya, yaitu: o Material reflektan terhadap komponen cahaya ambient o Material reflektan terhadap komponen cahaya diffuse o Material reflektan terhadap komponen cahaya specular o Material sebagai sumber cahaya (emitance) Komponen ambient adalah cahaya yang arahnya tidak dapat ditentukan karena datang secara merata dari segala arah. Biasanya merupakan cahaya yang dihasilkan dari pemantulan berkali-kali sumber cahaya yang berarah. Poligon dalam openGL selalu diiluminasi secara seragam oleh komponen ambient tanpa memperdulikan orientasi dan posisinya. Komponen diffuse adalah cahaya yang bersumber dari satu arah dan mempengaruhi poligon secara uniform bergantung pada sudut datang terhadap permukaan poligon. Komponen specular adalah cahaya yang memantul dari obyek yang bergantung pada derajat inklinasi dari poligon terhadap cahaya dan posisi observer. Program 17 memberi contoh bagaimana efek sumber cahaya dan material dari obyek. Program 18 memberi contoh tentang posisi sumber cahaya. Pada setiap windows gunakan tombol klik kanan untuk mengakses menu. Program 17 Material .h float Brass[] 0.329412, 0.780392, 0.992157, 27.897400 };
= { 0.223529, 0.027451, 1.000000, 0.568627, 0.113725, 1.000000, 0.941176, 0.807843, 1.000000,
float Bronze[] = { 0.212500, 0.127500, 0.054000, 1.000000,
70/117 0.714000, 0.428400, 0.181440, 1.000000, 0.393548, 0.271906, 0.166721, 1.000000, 25.600000 }; float Polished_Bronze[] 0.250000, 0.148000, 0.400000, 0.236800, 0.774597, 0.458561, 76.800003 };
= { 0.064750, 1.000000, 0.103600, 1.000000, 0.200621, 1.000000,
float Chrome[] = { 0.250000, 0.250000, 0.250000, 1.000000, 0.400000, 0.400000, 0.400000, 1.000000, 0.774597, 0.774597, 0.774597, 1.000000, 76.800003 }; float Copper[] = { 0.191250, 0.073500, 0.022500, 1.000000, 0.703800, 0.270480, 0.082800, 1.000000, 0.256777, 0.137622, 0.086014, 1.000000, 12.800000 }; float Polished_Copper[] 0.229500, 0.088250, 0.550800, 0.211800, 0.580594, 0.223257, 51.200001 };
= { 0.027500, 1.000000, 0.066000, 1.000000, 0.069570, 1.000000,
float Gold[] = { 0.247250, 0.199500, 0.074500, 1.000000, 0.751640, 0.606480, 0.226480, 1.000000, 0.628281, 0.555802, 0.366065, 1.000000, 51.200001 }; float Polished_Gold[] = 0.247250, 0.224500, 0.346150, 0.314300, 0.797357, 0.723991, 83.199997 };
{ 0.064500, 1.000000, 0.090300, 1.000000, 0.208006, 1.000000,
float Pewter[] = { 0.105882, 0.058824, 0.113725, 1.000000, 0.427451, 0.470588, 0.541176, 1.000000, 0.333333, 0.333333, 0.521569, 1.000000, 9.846150 }; float Silver[] = { 0.192250, 0.192250, 0.192250, 1.000000, 0.507540, 0.507540, 0.507540, 1.000000,
71/117 0.508273, 0.508273, 0.508273, 1.000000, 51.200001 }; float Polished_Silver[] 0.231250, 0.231250, 0.277500, 0.277500, 0.773911, 0.773911, 89.599998 };
= { 0.231250, 1.000000, 0.277500, 1.000000, 0.773911, 1.000000,
float Emerald[] = { 0.021500, 0.174500, 0.021500, 0.550000, 0.075680, 0.614240, 0.075680, 0.550000, 0.633000, 0.727811, 0.633000, 0.550000, 76.800003 }; float Jade[] = { 0.135000, 0.222500, 0.157500, 0.950000, 0.540000, 0.890000, 0.630000, 0.950000, 0.316228, 0.316228, 0.316228, 0.950000, 12.800000 }; float Obsidian[] = { 0.053750, 0.050000, 0.066250, 0.820000, 0.182750, 0.170000, 0.225250, 0.820000, 0.332741, 0.328634, 0.346435, 0.820000, 38.400002 }; float Pearl[] 0.250000, 1.000000, 0.296648, 11.264000 };
= { 0.207250, 0.207250, 0.922000, 0.829000, 0.829000, 0.922000, 0.296648, 0.296648, 0.922000,
float Ruby[] = { 0.174500, 0.011750, 0.011750, 0.550000, 0.614240, 0.041360, 0.041360, 0.550000, 0.727811, 0.626959, 0.626959, 0.550000, 76.800003 }; float Turquoise[] = { 0.100000, 0.187250, 0.174500, 0.800000, 0.396000, 0.741510, 0.691020, 0.800000, 0.297254, 0.308290, 0.306678, 0.800000, 12.800000 }; float Black_Plastic[] = 0.000000, 0.000000, 0.010000, 0.010000, 0.500000, 0.500000,
{ 0.000000, 1.000000, 0.010000, 1.000000, 0.500000, 1.000000,
72/117 32.000000 }; float Black_Rubber[] = { 0.020000, 0.020000, 0.020000, 1.000000, 0.010000, 0.010000, 0.010000, 1.000000, 0.400000, 0.400000, 0.400000, 1.000000, 10.000000 };
Lighting.cpp /* lightmaterial.c Nate Robins, 1997 Tool for teaching about OpenGL lighting & material properties. */
#include #include #include #include #include #include #include #include
<math.h> <stdio.h> <stdlib.h> <stdarg.h> <string.h> "glm.h" "materials.h"
#pragma comment( linker, "/entry:\"mainCRTStartup\"" ) entry point to be main()
// set the
typedef struct _cell { int id; int x, y; float min, max; float value; float step; char* info; char* format; } cell;
cell light_pos[4] = { { 1, 210, 30, -5.0, 5.0, -2.0, 0.01, "Specifies X coordinate of light vector.", "%.2f" }, { 2, 270, 30, -5.0, 5.0, 2.0, 0.01, "Specifies Y coordinate of light vector.", "%.2f" }, { 3, 330, 30, -5.0, 5.0, 2.0, 0.01, "Specifies Z coordinate of light vector.", "%.2f" }, { 4, 390, 30, 0.0, 1.0, 1.0, 1.0, "Specifies directional (0) or positional (1) light.", "%.2f" }, };
73/117
cell light_Ka[4] = { { 5, 200, 60, 0.0, 1.0, 0.0, 0.01, "Specifies ambient red intensity of the light.", "%.2f" }, { 6, 260, 60, 0.0, 1.0, 0.0, 0.01, "Specifies ambient green intensity of the light.", "%.2f" }, { 7, 320, 60, 0.0, 1.0, 0.0, 0.01, "Specifies ambient blue intensity of the light.", "%.2f" }, { 8, 380, 60, 0.0, 1.0, 1.0, 0.01, "Specifies ambient alpha intensity of the light.", "%.2f" }, }; cell light_Kd[4] = { { 9, 200, 90, 0.0, 1.0, 1.0, 0.01, "Specifies diffuse red intensity of the light.", "%.2f" }, { 10, 260, 90, 0.0, 1.0, 1.0, 0.01, "Specifies diffuse green intensity of the light.", "%.2f" }, { 11, 320, 90, 0.0, 1.0, 1.0, 0.01, "Specifies diffuse blue intensity of the light.", "%.2f" }, { 12, 380, 90, 0.0, 1.0, 1.0, 0.01, "Specifies diffuse alpha intensity of the light.", "%.2f" }, }; cell light_Ks[4] = { { 13, 200, 120, 0.0, 1.0, 1.0, 0.01, "Specifies specular red intensity of the light.", "%.2f" }, { 14, 260, 120, 0.0, 1.0, 1.0, 0.01, "Specifies specular green intensity of the light.", "%.2f" }, { 15, 320, 120, 0.0, 1.0, 1.0, 0.01, "Specifies specular blue intensity of the light.", "%.2f" }, { 16, 380, 120, 0.0, 1.0, 1.0, 0.01, "Specifies specular alpha intensity of the light.", "%.2f" }, }; cell spot_direction[3] = { { 17, 250, 260, -1.0, 1.0, 1.0, 0.01, "Specifies X coordinate of spotlight direction vector.", "%.2f" }, { 18, 310, 260, -1.0, 1.0, -1.0, 0.01, "Specifies Y coordinate of spotlight direction vector.", "%.2f" }, { 19, 370, 260, -1.0, 1.0, -1.0, 0.01, "Specifies Z coordinate of spotlight direction vector.", "%.2f" }, }; cell spot_exponent = { 20, 210, 290, 0.0, 128.0, 30.0, 1.0, "Specifies intensity distribution of spotlight.", "%.0f" }; cell spot_cutoff = { 21, 410, 290, 0.0, 91.0, 91.0, 1.0, "Specifies maximum spread angle of spotlight (180 = off).", "%.0f" }; cell Kc = { 22, 120, 410, 0.0, 5.0, 1.0, 0.01, "Specifies constant attenuation factor.", "%.2f" }; cell Kl = { 23, 215, 410, 0.0, 5.0, 0.0, 0.01, "Specifies linear attenuation factor.", "%.2f" }; cell Kq = { 24, 315, 410, 0.0, 5.0, 0.0, 0.01, "Specifies quadratic attenuation factor.", "%.2f" }; cell material_Ka[4] = {
74/117 { 25, 220, 260, 0.0, 1.0, 0.2, 0.01, "Specifies ambient red reflectance of the material.", "%.2f" }, { 26, 280, 260, 0.0, 1.0, 0.2, 0.01, "Specifies ambient green reflectance of the material.", "%.2f" }, { 27, 340, 260, 0.0, 1.0, 0.2, 0.01, "Specifies ambient blue reflectance of the material.", "%.2f" }, { 28, 400, 260, 0.0, 1.0, 1.0, 0.01, "Specifies ambient alpha reflectance of the material.", "%.2f" }, }; cell material_Kd[4] = { { 29, 220, 290, 0.0, 1.0, 0.8, 0.01, "Specifies diffuse red reflectance of the material.", "%.2f" }, { 30, 280, 290, 0.0, 1.0, 0.8, 0.01, "Specifies diffuse green reflectance of the material.", "%.2f" }, { 31, 340, 290, 0.0, 1.0, 0.8, 0.01, "Specifies diffuse blue reflectance of the material.", "%.2f" }, { 32, 400, 290, 0.0, 1.0, 1.0, 0.01, "Specifies diffuse alpha reflectance of the material.", "%.2f" }, }; cell material_Ks[4] = { { 33, 220, 320, 0.0, 1.0, 1.0, 0.01, "Specifies specular red reflectance of the material.", "%.2f" }, { 34, 280, 320, 0.0, 1.0, 1.0, 0.01, "Specifies specular green reflectance of the material.", "%.2f" }, { 35, 340, 320, 0.0, 1.0, 1.0, 0.01, "Specifies specular blue reflectance of the material.", "%.2f" }, { 36, 400, 320, 0.0, 1.0, 1.0, 0.01, "Specifies specular alpha reflectance of the material.", "%.2f" }, }; cell material_Ke[4] = { { 37, 220, 350, 0.0, 1.0, 0.0, 0.01, "Specifies red emitted light intensity of the material.", "%.2f" }, { 38, 280, 350, 0.0, 1.0, 0.0, 0.01, "Specifies green emitted light intensity of the material.", "%.2f" }, { 39, 340, 350, 0.0, 1.0, 0.0, 0.01, "Specifies blue emitted light intensity of the material.", "%.2f" }, { 40, 400, 350, 0.0, 1.0, 1.0, 0.01, "Specifies alpha emitted light intensity of the material.", "%.2f" }, }; cell material_Se = { 41, 200, 380, 0.0, 128.0, 50.0, 1.0, "Specifies the specular exponent of the material.", "%.0f" }; cell lmodel_Ka[4] = { { 42, 220, 260, 0.0, 1.0, 0.2, 0.01, "Specifies ambient red intensity of the entire scene.", "%.2f" }, { 43, 280, 260, 0.0, 1.0, 0.2, 0.01, "Specifies ambient green intensity of the entire scene.", "%.2f" },
75/117 { 44, 340, "Specifies { 45, 400, "Specifies
260, 0.0, 1.0, 0.2, 0.01, ambient blue intensity of the entire scene.", "%.2f" }, 260, 0.0, 1.0, 1.0, 0.01, ambient alpha intensity of the entire scene.", "%.2f" },
}; cell local_viewer = { 46, 460, 340, 0.0, 1.0, 0.0, 1.0, "Specifies infinite (0.0) or local (1.0) light model.", "%.1f" }; cell two_side = { 47, 415, 390, 0.0, 1.0, 0.0, 1.0, "Specifies one (0.0) or two (1.0) sided lighting.", "%.1f" };
GLfloat eye[3] = { 0.0, 0.0, 3.0 }; GLfloat at[3] = { 0.0, 0.0, 0.0 }; GLfloat up[3] = { 0.0, 1.0, 0.0 }; GLboolean world_draw = GL_TRUE; GLMmodel* pmodel = NULL; GLint selection = 0; GLfloat spin_x = 0.0; GLfloat spin_y = 0.0; void redisplay_all(void); GLdouble projection[16], modelview[16], inverse[16]; GLuint window, world, screen, command; GLuint sub_width = 256, sub_height = 256;
GLvoid *font_style = GLUT_BITMAP_TIMES_ROMAN_10; void setfont(char* name, int size) { font_style = GLUT_BITMAP_HELVETICA_10; if (strcmp(name, "helvetica") == 0) { if (size == 12) font_style = GLUT_BITMAP_HELVETICA_12; else if (size == 18) font_style = GLUT_BITMAP_HELVETICA_18; } else if (strcmp(name, "times roman") == 0) { font_style = GLUT_BITMAP_TIMES_ROMAN_10; if (size == 24) font_style = GLUT_BITMAP_TIMES_ROMAN_24; } else if (strcmp(name, "8x13") == 0) { font_style = GLUT_BITMAP_8_BY_13; } else if (strcmp(name, "9x15") == 0) { font_style = GLUT_BITMAP_9_BY_15; } } void drawstr(GLuint x, GLuint y, char* format, ...) { va_list args; char buffer[255], *s;
76/117
va_start(args, format); vsprintf(buffer, format, args); va_end(args); glRasterPos2i(x, y); for (s = buffer; *s; s++) glutBitmapCharacter(font_style, *s); } void cell_draw(cell* cell) { glColor3ub(0, 255, 128); if (selection == cell->id) { glColor3ub(255, 255, 0); drawstr(10, 525, cell->info); glColor3ub(255, 0, 0); } if (cell->id == 21 && cell->value > 90.0) /* treat cutoff specially */ drawstr(cell->x, cell->y, cell->format, 180.0); else drawstr(cell->x, cell->y, cell->format, cell->value); } int cell_hit(cell* cell, int x, int y) { if (x > cell->x && x < cell->x+60 && y > cell->y-20 && y < cell->y+10) return cell->id; return 0; } void cell_update(cell* cell, int update) { if (selection != cell->id) return; cell->value += update * cell->step; if (cell->value < cell->min) cell->value = cell->min; else if (cell->value > cell->max) cell->value = cell->max; } void cell_vector(float* dst, cell* cell, int num) { while (--num >= 0) dst[num] = cell[num].value; }
77/117
void drawmodel(void) { if (!pmodel) { pmodel = glmReadOBJ("data/soccerball.obj"); if (!pmodel) exit(0); glmUnitize(pmodel); glmFacetNormals(pmodel); glmVertexNormals(pmodel, 90.0); } glmDraw(pmodel, GLM_SMOOTH); } void drawaxes(void) { glColor3ub(255, 0, 0); glBegin(GL_LINE_STRIP); glVertex3f(0.0, 0.0, 0.0); glVertex3f(1.0, 0.0, 0.0); glVertex3f(0.75, 0.25, 0.0); glVertex3f(0.75, -0.25, 0.0); glVertex3f(1.0, 0.0, 0.0); glVertex3f(0.75, 0.0, 0.25); glVertex3f(0.75, 0.0, -0.25); glVertex3f(1.0, 0.0, 0.0); glEnd(); glBegin(GL_LINE_STRIP); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 1.0, 0.0); glVertex3f(0.0, 0.75, 0.25); glVertex3f(0.0, 0.75, -0.25); glVertex3f(0.0, 1.0, 0.0); glVertex3f(0.25, 0.75, 0.0); glVertex3f(-0.25, 0.75, 0.0); glVertex3f(0.0, 1.0, 0.0); glEnd(); glBegin(GL_LINE_STRIP); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 0.0, 1.0); glVertex3f(0.25, 0.0, 0.75); glVertex3f(-0.25, 0.0, 0.75); glVertex3f(0.0, 0.0, 1.0); glVertex3f(0.0, 0.25, 0.75); glVertex3f(0.0, -0.25, 0.75); glVertex3f(0.0, 0.0, 1.0); glEnd(); glColor3ub(255, 255, 0); glRasterPos3f(1.1, 0.0, 0.0); glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, 'x'); glRasterPos3f(0.0, 1.1, 0.0); glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, 'y'); glRasterPos3f(0.0, 0.0, 1.1); glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, 'z');
78/117 } void identity(GLdouble { m[0+4*0] = 1; m[1+4*0] = 0; m[2+4*0] = 0; m[3+4*0] = 0; }
m[16]) m[0+4*1] m[1+4*1] m[2+4*1] m[3+4*1]
= = = =
0; 1; 0; 0;
m[0+4*2] m[1+4*2] m[2+4*2] m[3+4*2]
= = = =
0; 0; 1; 0;
m[0+4*3] m[1+4*3] m[2+4*3] m[3+4*3]
= = = =
0; 0; 0; 1;
GLboolean invert(GLdouble src[16], GLdouble inverse[16]) { double t; int i, j, k, swap; GLdouble tmp[4][4]; identity(inverse); for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { tmp[i][j] = src[i*4+j]; } } for (i = 0; i < 4; i++) { /* look for largest element in column. */ swap = i; for (j = i + 1; j < 4; j++) { if (fabs(tmp[j][i]) > fabs(tmp[i][i])) { swap = j; } } if (swap != i) { /* swap rows. */ for (k = 0; k < 4; k++) { t = tmp[i][k]; tmp[i][k] = tmp[swap][k]; tmp[swap][k] = t; t = inverse[i*4+k]; inverse[i*4+k] = inverse[swap*4+k]; inverse[swap*4+k] = t; } } if (tmp[i][i] == 0) { /* no non-zero pivot. the matrix is singular, which shouldn't happen. This means the user gave us a bad matrix. */ return GL_FALSE; } t = tmp[i][i]; for (k = 0; k < 4; k++) {
79/117 tmp[i][k] /= t; inverse[i*4+k] /= t; } for (j = 0; j < 4; j++) { if (j != i) { t = tmp[j][i]; for (k = 0; k < 4; k++) { tmp[j][k] -= tmp[i][k]*t; inverse[j*4+k] -= inverse[i*4+k]*t; } } } } return GL_TRUE; } float normalize(float* v) { float length; length = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); v[0] /= length; v[1] /= length; v[2] /= length; return length; } void main_reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, width, height, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); #define GAP 25 /* gap between subwindows */ sub_width = (width-GAP*3)/3; sub_height = (height-GAP*3)/2; glutSetWindow(screen); glutPositionWindow(GAP, GAP); glutReshapeWindow(sub_width, sub_height); glutSetWindow(world); glutPositionWindow(GAP, GAP+sub_height+GAP); glutReshapeWindow(sub_width, sub_height); glutSetWindow(command); glutPositionWindow(GAP+sub_width+GAP, GAP); glutReshapeWindow(sub_width*2, sub_height*2+GAP); } void main_display(void) {
80/117 glClearColor(0.8, 0.8, 0.8, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3ub(0, 0, 0); setfont("helvetica", 12); drawstr(GAP, GAP-5, "Screen-space view"); drawstr(GAP+sub_width+GAP, GAP-5, "Command manipulation window"); drawstr(GAP, GAP+sub_height+GAP-5, "World-space view"); glutSwapBuffers(); } void world_reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)width/height, 0.01, 256.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -6.0); glRotatef(-45.0, 0.0, 1.0, 0.0); glClearColor(0.0, 0.0, 0.0, 0.0); glEnable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_LIGHT0); } void world_display(void) { double length; float l[3]; GLfloat pos[4], lKa[4], lKd[4], lKs[4]; GLfloat dir[3], mKa[4], mKd[4], mKs[4], mKe[4]; GLfloat lmKa[4]; cell_vector(pos, light_pos, 4); cell_vector(lKa, light_Ka, 4); cell_vector(lKd, light_Kd, 4); cell_vector(lKs, light_Ks, 4); cell_vector(dir, spot_direction, 3); cell_vector(mKa, material_Ka, 4); cell_vector(mKd, material_Kd, 4); cell_vector(mKs, material_Ks, 4); cell_vector(mKe, material_Ke, 4); cell_vector(lmKa, lmodel_Ka, 4); glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, local_viewer.value); glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, two_side.value); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmKa); glMaterialfv(GL_FRONT, GL_AMBIENT, mKa); glMaterialfv(GL_FRONT, GL_DIFFUSE, mKd); glMaterialfv(GL_FRONT, GL_SPECULAR, mKs); glMaterialfv(GL_FRONT, GL_EMISSION, mKe); glMaterialf(GL_FRONT, GL_SHININESS, material_Se.value);
81/117 glLightfv(GL_LIGHT0, GL_AMBIENT, lKa); glLightfv(GL_LIGHT0, GL_DIFFUSE, lKd); glLightfv(GL_LIGHT0, GL_SPECULAR, lKs); glLighti(GL_LIGHT0, GL_SPOT_EXPONENT, (int)spot_exponent.value); if (spot_cutoff.value > 90) glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, 180); else glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, (int)spot_cutoff.value); glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, Kc.value); glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, Kl.value); glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, Kq.value); l[0] = at[0] - eye[0]; l[1] = at[1] - eye[1]; l[2] = at[2] - eye[2]; invert(modelview, inverse); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glMultMatrixd(inverse); glTranslatef(l[0], l[1], l[2]); glColor3fv(lKd); glBegin(GL_LINE_STRIP); if (spot_cutoff.value > 90) glVertex3f(0, 0, 0); else glVertex3f(pos[0]+spot_direction[0].value, pos[1]+spot_direction[1].value, pos[2]+spot_direction[2].value); if (pos[3] == 0) /* 10.0 = 'infinite' light */ glVertex3f(pos[0]*10.0,pos[1]*10.0,pos[2]*10.0); else glVertex3f(pos[0], pos[1], pos[2]); glEnd(); glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, dir); glLightfv(GL_LIGHT0, GL_POSITION, pos); glPopMatrix(); length = normalize(l); if (world_draw) { glEnable(GL_LIGHTING); if (pmodel) drawmodel(); else glutSolidTorus(0.25, 0.75, 28, 28); glDisable(GL_LIGHTING); } #if 0 #define TESS 20 glNormal3f(0.0, 1.0, 0.0); for (i = 0; i < TESS; i++) { glBegin(GL_TRIANGLE_STRIP);
82/117 for (j = 0; j <= TESS; j++) { glVertex3f(-1+(float)i/TESS*2, -1.0, -1+(float)j/TESS*2); glVertex3f(-1+(float)(i+1)/TESS*2, -1.0, 1+(float)j/TESS*2); } glEnd(); } #endif glPushMatrix(); glMultMatrixd(inverse); /* draw the axis and eye vector */ glPushMatrix(); glColor3ub(0, 0, 255); glBegin(GL_LINE_STRIP); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 0.0, -1.0*length); glVertex3f(0.1, 0.0, -0.9*length); glVertex3f(-0.1, 0.0, -0.9*length); glVertex3f(0.0, 0.0, -1.0*length); glVertex3f(0.0, 0.1, -0.9*length); glVertex3f(0.0, -0.1, -0.9*length); glVertex3f(0.0, 0.0, -1.0*length); glEnd(); glColor3ub(255, 255, 0); glRasterPos3f(0.0, 0.0, -1.1*length); glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, 'e'); glColor3ub(255, 0, 0); glScalef(0.4, 0.4, 0.4); drawaxes(); glPopMatrix(); invert(projection, inverse); glMultMatrixd(inverse); /* draw the viewing frustum */ glColor3f(0.2, 0.2, 0.2); glBegin(GL_QUADS); glVertex3i(1, 1, 1); glVertex3i(-1, 1, 1); glVertex3i(-1, -1, 1); glVertex3i(1, -1, 1); glEnd(); glColor3ub(128, 196, 128); glBegin(GL_LINES); glVertex3i(1, 1, -1); glVertex3i(1, 1, 1); glVertex3i(-1, 1, -1); glVertex3i(-1, 1, 1); glVertex3i(-1, -1, -1); glVertex3i(-1, -1, 1); glVertex3i(1, -1, -1); glVertex3i(1, -1, 1); glEnd();
83/117 glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(0.2, 0.2, 0.4, 0.5); glBegin(GL_QUADS); glVertex3i(1, 1, -1); glVertex3i(-1, 1, -1); glVertex3i(-1, -1, -1); glVertex3i(1, -1, -1); glEnd(); glDisable(GL_BLEND); glPopMatrix(); glutSwapBuffers(); } void new_material(float* material) { material_Ka[0].value = material[0]; material_Ka[1].value = material[1]; material_Ka[2].value = material[2]; material_Ka[3].value = material[3]; material_Kd[0].value = material[4]; material_Kd[1].value = material[5]; material_Kd[2].value = material[6]; material_Kd[3].value = material[7]; material_Ks[0].value = material[8]; material_Ks[1].value = material[9]; material_Ks[2].value = material[10]; material_Ks[3].value = material[11]; material_Ke[0].value = 0; material_Ke[1].value = 0; material_Ke[2].value = 0; material_Ke[3].value = 0; material_Se.value = material[12]; } void world_menu(int value) { switch (value) { case 1: new_material(Brass); break; case 2: new_material(Bronze); break; case 3: new_material(Polished_Bronze); break; case 4: new_material(Chrome); break; case 5: new_material(Copper); break; case 6:
84/117 new_material(Polished_Copper); break; case 7: new_material(Gold); break; case 8: new_material(Polished_Gold); break; case 9: new_material(Pewter); break; case 10: new_material(Silver); break; case 11: new_material(Polished_Silver); break; case 12: new_material(Emerald); break; case 13: new_material(Jade); break; case 14: new_material(Obsidian); break; case 15: new_material(Pearl); break; case 16: new_material(Ruby); break; case 17: new_material(Turquoise); break; case 18: new_material(Black_Plastic); break; case 19: new_material(Black_Rubber); break; } redisplay_all(); } void screen_reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (float)width/height, 0.5, 8.0); glGetDoublev(GL_PROJECTION_MATRIX, projection); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(eye[0], eye[1], eye[2], at[0], at[1], at[2], up[0],
85/117 up[1],up[2]); glClearColor(0.2, 0.2, 0.2, 1.0); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); } void screen_display(void) { GLfloat pos[4], lKa[4], lKd[4], lKs[4]; GLfloat dir[3], mKa[4], mKd[4], mKs[4], mKe[4]; GLfloat lmKa[4]; cell_vector(pos, light_pos, 4); cell_vector(lKa, light_Ka, 4); cell_vector(lKd, light_Kd, 4); cell_vector(lKs, light_Ks, 4); cell_vector(dir, spot_direction, 3); cell_vector(mKa, material_Ka, 4); cell_vector(mKd, material_Kd, 4); cell_vector(mKs, material_Ks, 4); cell_vector(mKe, material_Ke, 4); cell_vector(lmKa, lmodel_Ka, 4); glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, local_viewer.value); glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, two_side.value); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmKa); glLightfv(GL_LIGHT0, GL_POSITION, pos); glLightfv(GL_LIGHT0, GL_AMBIENT, lKa); glLightfv(GL_LIGHT0, GL_DIFFUSE, lKd); glLightfv(GL_LIGHT0, GL_SPECULAR, lKs); glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, dir); glLighti(GL_LIGHT0, GL_SPOT_EXPONENT, (int)spot_exponent.value); if (spot_cutoff.value > 90) glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, 180); else glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, (int)spot_cutoff.value); glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, Kc.value); glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, Kl.value); glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, Kq.value); glMaterialfv(GL_FRONT, GL_AMBIENT, mKa); glMaterialfv(GL_FRONT, GL_DIFFUSE, mKd); glMaterialfv(GL_FRONT, GL_SPECULAR, mKs); glMaterialfv(GL_FRONT, GL_EMISSION, mKe); glMaterialf(GL_FRONT, GL_SHININESS, material_Se.value); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(spin_y, 1.0, 0.0, 0.0); glRotatef(spin_x, 0.0, 1.0, 0.0); glGetDoublev(GL_MODELVIEW_MATRIX, modelview); if (pmodel) drawmodel(); else
86/117 glutSolidTorus(0.25, 0.75, 28, 28); glPopMatrix(); #if 0 #define TESS 20 glNormal3f(0.0, 1.0, 0.0); for (i = 0; i < TESS; i++) { glBegin(GL_TRIANGLE_STRIP); for (j = 0; j <= TESS; j++) { glVertex3f(-1+(float)i/TESS*2, -1.0, -1+(float)j/TESS*2); glVertex3f(-1+(float)(i+1)/TESS*2, -1.0, 1+(float)j/TESS*2); } glEnd(); } #endif glutSwapBuffers(); } void screen_menu(int value) { char* name = 0; switch (value) { case 'a': name = "data/al.obj"; break; case 's': name = "data/soccerball.obj"; break; case 'd': name = "data/dolphins.obj"; break; case 'f': name = "data/flowers.obj"; break; case 'j': name = "data/f-16.obj"; break; case 'p': name = "data/porsche.obj"; break; case 'r': name = "data/rose+vase.obj"; break; case 'n': if (pmodel) glmDelete(pmodel); pmodel = NULL; redisplay_all(); return; } if (name) { if (pmodel) glmDelete(pmodel); pmodel = glmReadOBJ(name);
87/117 if (!pmodel) exit(0); glmUnitize(pmodel); glmFacetNormals(pmodel); glmVertexNormals(pmodel, 90.0); } redisplay_all(); } int old_x, old_y; void screen_mouse(int button, int state, int x, int y) { old_x = x; old_y = y; redisplay_all(); } void screen_motion(int x, int y) { spin_x = x - old_x; spin_y = y - old_y; redisplay_all(); } void command_reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, width, height, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.0, 0.0, 0.0, 0.0); } void lighting_display(void) { setfont("helvetica", 18); drawstr(10, light_pos[0].y, "GLfloat light_pos[ ] drawstr(10, light_Ka[0].y, "GLfloat light_Ka[ ] = drawstr(10, light_Kd[0].y, "GLfloat light_Kd[ ] = drawstr(10, light_Ks[0].y, "GLfloat light_Ks[ ] = drawstr(light_pos[0].x+50, light_pos[0].y, ","); drawstr(light_pos[1].x+50, light_pos[1].y, ","); drawstr(light_pos[2].x+50, light_pos[2].y, ","); drawstr(light_pos[3].x+50, light_pos[3].y, "};"); drawstr(light_Ka[0].x+50, light_Ka[0].y, ","); drawstr(light_Ka[1].x+50, light_Ka[1].y, ","); drawstr(light_Ka[2].x+50, light_Ka[2].y, ","); drawstr(light_Ka[3].x+50, light_Ka[3].y, "};");
= {"); {"); {"); {");
88/117 drawstr(light_Kd[0].x+50, drawstr(light_Kd[1].x+50, drawstr(light_Kd[2].x+50, drawstr(light_Kd[3].x+50, drawstr(light_Ks[0].x+50, drawstr(light_Ks[1].x+50, drawstr(light_Ks[2].x+50, drawstr(light_Ks[3].x+50,
light_Kd[0].y, light_Kd[1].y, light_Kd[2].y, light_Kd[3].y, light_Ks[0].y, light_Ks[1].y, light_Ks[2].y, light_Ks[3].y,
","); ","); ","); "};"); ","); ","); ","); "};");
setfont("helvetica", 12); drawstr(10, light_Ks[0].y+30, "glLightfv(GL_LIGHT0, GL_POSITION, light_pos);"); drawstr(10, light_Ks[1].y+50, "glLightfv(GL_LIGHT0, GL_AMBIENT, light_Ka);"); drawstr(10, light_Ks[2].y+70, "glLightfv(GL_LIGHT0, GL_DIFFUSE, light_Kd);"); drawstr(10, light_Ks[3].y+90, "glLightfv(GL_LIGHT0, GL_SPECULAR, light_Ks);"); setfont("helvetica", 18); cell_draw(&light_pos[0]); cell_draw(&light_pos[1]); cell_draw(&light_pos[2]); cell_draw(&light_pos[3]); cell_draw(&light_Ka[0]); cell_draw(&light_Ka[1]); cell_draw(&light_Ka[2]); cell_draw(&light_Ka[3]); cell_draw(&light_Kd[0]); cell_draw(&light_Kd[1]); cell_draw(&light_Kd[2]); cell_draw(&light_Kd[3]); cell_draw(&light_Ks[0]); cell_draw(&light_Ks[1]); cell_draw(&light_Ks[2]); cell_draw(&light_Ks[3]); glColor3ub(255, 255, 255); } void spotlight_display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3ub(255, 255, 255); lighting_display(); setfont("helvetica", 18); drawstr(10, spot_direction[0].y, "GLfloat spot_direction[ ] = {"); drawstr(10, spot_exponent.y, "GLint spot_exponent = "
89/117 "spot_cutoff = "); drawstr(spot_direction[0].x+50, spot_direction[0].y, ","); drawstr(spot_direction[1].x+50, spot_direction[1].y, ","); drawstr(spot_direction[2].x+50, spot_direction[2].y, "};"); drawstr(spot_exponent.x+40, spot_cutoff.y, ","); drawstr(spot_cutoff.x+40, spot_cutoff.y, ";"); setfont("helvetica", 12); drawstr(10, spot_cutoff.y+30, "glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);"); drawstr(10, spot_cutoff.y+50, "glLighti(GL_LIGHT0, GL_SPOT_EXPONENT, spot_exponent);"); drawstr(10, spot_cutoff.y+70, "glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, spot_cutoff);"); setfont("helvetica", 18); drawstr(10, Kc.y, "GLfloat Kc = setfont("helvetica", drawstr(10, Kq.y+30, Kc);"); drawstr(10, Kq.y+50, Kl);"); drawstr(10, Kq.y+70, Kq);");
, Kl =
, Kq =
;");
12); "glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, "glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, "glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION,
setfont("helvetica", 18); cell_draw(&spot_direction[0]); cell_draw(&spot_direction[1]); cell_draw(&spot_direction[2]); cell_draw(&spot_exponent); cell_draw(&spot_cutoff); cell_draw(&Kc); cell_draw(&Kl); cell_draw(&Kq); if (!selection) { glColor3ub(255, 255, 0); drawstr(10, 525, "Click on the arguments and move the mouse to modify values."); } glutSwapBuffers(); } void material_display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3ub(255, 255, 255); lighting_display();
90/117
setfont("helvetica", 18); drawstr(10, material_Ka[0].y, "GLfloat material_Ka[ ] drawstr(10, material_Kd[0].y, "GLfloat material_Kd[ ] drawstr(10, material_Ks[0].y, "GLfloat material_Ks[ ] drawstr(10, material_Ke[0].y, "GLfloat material_Ke[ ] drawstr(10, material_Se.y, "GLfloat material_Se = drawstr(material_Ka[0].x+50, material_Ka[0].y, ","); drawstr(material_Ka[1].x+50, material_Ka[1].y, ","); drawstr(material_Ka[2].x+50, material_Ka[2].y, ","); drawstr(material_Ka[3].x+50, material_Ka[3].y, "};"); drawstr(material_Kd[0].x+50, material_Kd[0].y, ","); drawstr(material_Kd[1].x+50, material_Kd[1].y, ","); drawstr(material_Kd[2].x+50, material_Kd[2].y, ","); drawstr(material_Kd[3].x+50, material_Kd[3].y, "};"); drawstr(material_Ks[0].x+50, material_Ks[0].y, ","); drawstr(material_Ks[1].x+50, material_Ks[1].y, ","); drawstr(material_Ks[2].x+50, material_Ks[2].y, ","); drawstr(material_Ks[3].x+50, material_Ks[3].y, "};"); drawstr(material_Ke[0].x+50, material_Ke[0].y, ","); drawstr(material_Ke[1].x+50, material_Ke[1].y, ","); drawstr(material_Ke[2].x+50, material_Ke[2].y, ","); drawstr(material_Ke[3].x+50, material_Ke[3].y, "};");
= = = =
{"); {"); {"); {"); ;");
setfont("helvetica", 12); drawstr(10, material_Se.y+30, "glMaterialfv(GL_FRONT, GL_AMBIENT, material_Ka);"); drawstr(10, material_Se.y+50, "glMaterialfv(GL_FRONT, GL_DIFFUSE, material_Kd);"); drawstr(10, material_Se.y+70, "glMaterialfv(GL_FRONT, GL_SPECULAR, material_Ks);"); drawstr(10, material_Se.y+90, "glMaterialfv(GL_FRONT, GL_EMISSION, material_Ke);"); drawstr(10, material_Se.y+110, "glMaterialfv(GL_FRONT, GL_SHININESS, material_Se);"); setfont("helvetica", 18); cell_draw(&material_Ka[0]); cell_draw(&material_Ka[1]); cell_draw(&material_Ka[2]); cell_draw(&material_Ka[3]); cell_draw(&material_Kd[0]); cell_draw(&material_Kd[1]); cell_draw(&material_Kd[2]); cell_draw(&material_Kd[3]); cell_draw(&material_Ks[0]); cell_draw(&material_Ks[1]); cell_draw(&material_Ks[2]); cell_draw(&material_Ks[3]); cell_draw(&material_Ke[0]); cell_draw(&material_Ke[1]); cell_draw(&material_Ke[2]); cell_draw(&material_Ke[3]);
91/117
cell_draw(&material_Se); if (!selection) { glColor3ub(255, 255, 0); drawstr(10, 525, "Click on the arguments and move the mouse to modify values."); } glutSwapBuffers(); } void lmodel_display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3ub(255, 255, 255); lighting_display(); setfont("helvetica", 18); drawstr(10, lmodel_Ka[0].y, "GLfloat lmodel_Ka[ ] = {"); drawstr(lmodel_Ka[0].x+50, lmodel_Ka[0].y, ","); drawstr(lmodel_Ka[1].x+50, lmodel_Ka[1].y, ","); drawstr(lmodel_Ka[2].x+50, lmodel_Ka[2].y, ","); drawstr(lmodel_Ka[3].x+50, lmodel_Ka[3].y, "};"); setfont("helvetica", 12); drawstr(10, lmodel_Ka[3].y+30, "glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_Ka);"); setfont("helvetica", 18); drawstr(10, local_viewer.y, "glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, "); drawstr(local_viewer.x+35, local_viewer.y, ");"); drawstr(10, two_side.y, "glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, "); drawstr(two_side.x+35, two_side.y, ");"); cell_draw(&lmodel_Ka[0]); cell_draw(&lmodel_Ka[1]); cell_draw(&lmodel_Ka[2]); cell_draw(&lmodel_Ka[3]); cell_draw(&local_viewer); cell_draw(&two_side); if (!selection) { glColor3ub(255, 255, 0); drawstr(10, 525, "Click on the arguments and move the mouse to modify values."); } glutSwapBuffers();
92/117 } void lighting_mouse(int x, int y) { /* mouse should only hit _one_ of the cells, so adding up all the hits just propagates a single hit. */ selection += cell_hit(&light_pos[0], x, y); selection += cell_hit(&light_pos[1], x, y); selection += cell_hit(&light_pos[2], x, y); selection += cell_hit(&light_pos[3], x, y); selection += cell_hit(&light_Ka[0], x, y); selection += cell_hit(&light_Ka[1], x, y); selection += cell_hit(&light_Ka[2], x, y); selection += cell_hit(&light_Ka[3], x, y); selection += cell_hit(&light_Kd[0], x, y); selection += cell_hit(&light_Kd[1], x, y); selection += cell_hit(&light_Kd[2], x, y); selection += cell_hit(&light_Kd[3], x, y); selection += cell_hit(&light_Ks[0], x, y); selection += cell_hit(&light_Ks[1], x, y); selection += cell_hit(&light_Ks[2], x, y); selection += cell_hit(&light_Ks[3], x, y); } void material_mouse(int button, int state, int x, int y) { selection = 0; if (state == GLUT_DOWN) { lighting_mouse(x, y); /* mouse should only hit _one_ of the cells, so adding up all the hits just propagates a single hit. */ selection += cell_hit(&material_Ka[0], x, y); selection += cell_hit(&material_Ka[1], x, y); selection += cell_hit(&material_Ka[2], x, y); selection += cell_hit(&material_Ka[3], x, y); selection += cell_hit(&material_Kd[0], x, y); selection += cell_hit(&material_Kd[1], x, y); selection += cell_hit(&material_Kd[2], x, y); selection += cell_hit(&material_Kd[3], x, y); selection += cell_hit(&material_Ks[0], x, y); selection += cell_hit(&material_Ks[1], x, y); selection += cell_hit(&material_Ks[2], x, y); selection += cell_hit(&material_Ks[3], x, y); selection += cell_hit(&material_Ke[0], x, y); selection += cell_hit(&material_Ke[1], x, y); selection += cell_hit(&material_Ke[2], x, y); selection += cell_hit(&material_Ke[3], x, y); selection += cell_hit(&material_Se, x, y); } old_y = y; redisplay_all(); }
93/117
void spotlight_mouse(int button, int state, int x, int y) { selection = 0; if (state == GLUT_DOWN) { lighting_mouse(x, y); /* mouse should only hit _one_ of the cells, the hits just propagates a single hit. */ selection += cell_hit(&spot_direction[0], x, selection += cell_hit(&spot_direction[1], x, selection += cell_hit(&spot_direction[2], x, selection += cell_hit(&spot_exponent, x, y); selection += cell_hit(&spot_cutoff, x, y); selection += cell_hit(&Kc, x, y); selection += cell_hit(&Kl, x, y); selection += cell_hit(&Kq, x, y); }
so adding up all y); y); y);
old_y = y; redisplay_all(); } void lmodel_mouse(int button, int state, int x, int y) { selection = 0; if (state == GLUT_DOWN) { lighting_mouse(x, y); /* mouse should only hit _one_ of the cells, so adding up all the hits just propagates a single hit. */ selection += cell_hit(&lmodel_Ka[0], x, y); selection += cell_hit(&lmodel_Ka[1], x, y); selection += cell_hit(&lmodel_Ka[2], x, y); selection += cell_hit(&lmodel_Ka[3], x, y); selection += cell_hit(&local_viewer, x, y); selection += cell_hit(&two_side, x, y); } old_y = y; redisplay_all(); } void command_motion(int x, int y) { cell_update(&light_pos[0], old_y-y); cell_update(&light_pos[1], old_y-y); cell_update(&light_pos[2], old_y-y); cell_update(&light_pos[3], old_y-y); cell_update(&light_Ka[0], old_y-y); cell_update(&light_Ka[1], old_y-y); cell_update(&light_Ka[2], old_y-y);
94/117 cell_update(&light_Ka[3], old_y-y); cell_update(&light_Kd[0], old_y-y); cell_update(&light_Kd[1], old_y-y); cell_update(&light_Kd[2], old_y-y); cell_update(&light_Kd[3], old_y-y); cell_update(&light_Ks[0], old_y-y); cell_update(&light_Ks[1], old_y-y); cell_update(&light_Ks[2], old_y-y); cell_update(&light_Ks[3], old_y-y); cell_update(&spot_direction[0], old_y-y); cell_update(&spot_direction[1], old_y-y); cell_update(&spot_direction[2], old_y-y); cell_update(&spot_exponent, old_y-y); cell_update(&spot_cutoff, old_y-y); cell_update(&Kc, old_y-y); cell_update(&Kl, old_y-y); cell_update(&Kq, old_y-y); cell_update(&material_Ka[0], old_y-y); cell_update(&material_Ka[1], old_y-y); cell_update(&material_Ka[2], old_y-y); cell_update(&material_Ka[3], old_y-y); cell_update(&material_Kd[0], old_y-y); cell_update(&material_Kd[1], old_y-y); cell_update(&material_Kd[2], old_y-y); cell_update(&material_Kd[3], old_y-y); cell_update(&material_Ks[0], old_y-y); cell_update(&material_Ks[1], old_y-y); cell_update(&material_Ks[2], old_y-y); cell_update(&material_Ks[3], old_y-y); cell_update(&material_Ke[0], old_y-y); cell_update(&material_Ke[1], old_y-y); cell_update(&material_Ke[2], old_y-y); cell_update(&material_Ke[3], old_y-y); cell_update(&material_Se, old_y-y); cell_update(&lmodel_Ka[0], old_y-y); cell_update(&lmodel_Ka[1], old_y-y); cell_update(&lmodel_Ka[2], old_y-y); cell_update(&lmodel_Ka[3], old_y-y); cell_update(&local_viewer, old_y-y); cell_update(&two_side, old_y-y); old_y = y; redisplay_all(); } void redisplay_all(void) { glutSetWindow(command); glutPostRedisplay(); glutSetWindow(world); world_reshape(sub_width, sub_height); glutPostRedisplay(); glutSetWindow(screen); screen_reshape(sub_width, sub_height); glutPostRedisplay();
95/117 } void main_keyboard(unsigned char key, int x, int y) { switch (key) { case 'r': light_pos[0].value = -2.0; light_pos[1].value = 2.0; light_pos[2].value = 2.0; light_pos[3].value = 1.0; light_Ka[0].value = 0; light_Ka[1].value = 0; light_Ka[2].value = 0; light_Ka[3].value = 1; light_Kd[0].value = 1; light_Kd[1].value = 1; light_Kd[2].value = 1; light_Kd[3].value = 1; light_Ks[0].value = 1; light_Ks[1].value = 1; light_Ks[2].value = 1; light_Ks[3].value = 1; spot_direction[0].value = 1.0; spot_direction[1].value = -1.0; spot_direction[2].value = -1.0; spot_exponent.value = 30.0; spot_cutoff.value = 91.0; Kc.value = 1.0; Kl.value = 0.0; Kq.value = 0.0; new_material(Pewter); lmodel_Ka[0].value = 0.2; lmodel_Ka[1].value = 0.2; lmodel_Ka[2].value = 0.2; lmodel_Ka[3].value = 1.0; local_viewer.value = 0; two_side.value = 0; break; case 'm': glutSetWindow(command); glutMouseFunc(material_mouse); glutDisplayFunc(material_display); break; case 's': glutSetWindow(command); glutMouseFunc(spotlight_mouse); glutDisplayFunc(spotlight_display); break; case 'l': glutSetWindow(command); glutMouseFunc(lmodel_mouse); glutDisplayFunc(lmodel_display); break; case 27: exit(0); }
96/117
redisplay_all(); } void command_menu(int value) { main_keyboard((unsigned char)value, 0, 0); } int main(int argc, char** argv) { glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); glutInitWindowSize((512+GAP*3)*3/2, 512+GAP*3); glutInitWindowPosition(50, 50); glutInit(&argc, argv); window = glutCreateWindow("Light & Material"); glutReshapeFunc(main_reshape); glutDisplayFunc(main_display); glutKeyboardFunc(main_keyboard); world = glutCreateSubWindow(window, GAP, GAP, 256, 256); glutReshapeFunc(world_reshape); glutDisplayFunc(world_display); glutKeyboardFunc(main_keyboard); glutCreateMenu(world_menu); glutAddMenuEntry("Materials", 0); glutAddMenuEntry("", 0); glutAddMenuEntry("Brass", 1); glutAddMenuEntry("Bronze", 2); glutAddMenuEntry("Polished_Bronze", 3); glutAddMenuEntry("Chrome", 4); glutAddMenuEntry("Copper", 5); glutAddMenuEntry("Polished_Copper", 6); glutAddMenuEntry("Gold", 7); glutAddMenuEntry("Polished_Gold", 8); glutAddMenuEntry("Pewter", 9); glutAddMenuEntry("Silver", 10); glutAddMenuEntry("Polished_Silver", 11); glutAddMenuEntry("Emerald", 12); glutAddMenuEntry("Jade", 13); glutAddMenuEntry("Obsidian", 14); glutAddMenuEntry("Pearl", 15); glutAddMenuEntry("Ruby", 16); glutAddMenuEntry("Turquoise", 17); glutAddMenuEntry("Black_Plastic", 18); glutAddMenuEntry("Black_Rubber", 19); glutAttachMenu(GLUT_RIGHT_BUTTON); screen = glutCreateSubWindow(window, GAP+256+GAP, GAP, 256, 256); glutReshapeFunc(screen_reshape); glutDisplayFunc(screen_display); glutKeyboardFunc(main_keyboard); glutMotionFunc(screen_motion); glutMouseFunc(screen_mouse);
97/117 glutCreateMenu(screen_menu); glutAddMenuEntry("Models", 0); glutAddMenuEntry("", 0); glutAddMenuEntry("Torus", 'n'); glutAddMenuEntry("Flat plane", 'l'); glutAddMenuEntry("Soccerball", 's'); glutAddMenuEntry("Al Capone", 'a'); glutAddMenuEntry("F-16 Jet", 'j'); glutAddMenuEntry("Dolphins", 'd'); glutAddMenuEntry("Flowers", 'f'); glutAddMenuEntry("Porsche", 'p'); glutAddMenuEntry("Rose", 'r'); glutAttachMenu(GLUT_RIGHT_BUTTON); command = glutCreateSubWindow(window, GAP+256+GAP, GAP+256+GAP, 256, 256); glutReshapeFunc(command_reshape); glutDisplayFunc(material_display); glutMotionFunc(command_motion); glutMouseFunc(material_mouse); glutKeyboardFunc(main_keyboard); glutCreateMenu(command_menu); glutAddMenuEntry("Light & Material", 0); glutAddMenuEntry("", 0); glutAddMenuEntry("Material parameters", 'm'); glutAddMenuEntry("Light model parameters", 'l'); glutAddMenuEntry("Spotlight & attenuation", 's'); glutAddMenuEntry("Reset parameters (r)", 'r'); glutAddMenuEntry("", 0); glutAddMenuEntry("Quit", 27); glutAttachMenu(GLUT_RIGHT_BUTTON); new_material(Pewter); redisplay_all(); glutMainLoop(); return 0; }
Program 18 /* lightposition.c Nate Robins, 1997 Tool for teaching about OpenGL light positioning. */
#include <math.h> #include <stdio.h> #include <stdlib.h>
98/117 #include #include #include #include
<stdarg.h> <string.h> "glm.h"
#pragma comment( linker, "/entry:\"mainCRTStartup\"" ) entry point to be main()
// set the
typedef struct _cell { int id; int x, y; float min, max; float value; float step; char* info; char* format; } cell;
cell lookat[9] = { { 1, 180, 120, -5.0, 5.0, 0.0, 0.1, "Specifies the X position of the eye point.", "%.2f" }, { 2, 240, 120, -5.0, 5.0, 0.0, 0.1, "Specifies the Y position of the eye point.", "%.2f" }, { 3, 300, 120, -5.0, 5.0, 2.0, 0.1, "Specifies the Z position of the eye point.", "%.2f" }, { 4, 180, 160, -5.0, 5.0, 0.0, 0.1, "Specifies the X position of the reference point.", "%.2f" }, { 5, 240, 160, -5.0, 5.0, 0.0, 0.1, "Specifies the Y position of the reference point.", "%.2f" }, { 6, 300, 160, -5.0, 5.0, 0.0, 0.1, "Specifies the Z position of the reference point.", "%.2f" }, { 7, 180, 200, -2.0, 2.0, 0.0, 0.1, "Specifies the X direction of the up vector.", "%.2f" }, { 8, 240, 200, -2.0, 2.0, 1.0, 0.1, "Specifies the Y direction of the up vector.", "%.2f" }, { 9, 300, 200, -2.0, 2.0, 0.0, 0.1, "Specifies the Z direction of the up vector.", "%.2f" }, }; cell light[4] = { { 10, 180, 40, -5.0, 5.0, 1.5, 0.1, "Specifies X coordinate of light vector.", "%.2f" }, { 11, 240, 40, -5.0, 5.0, 1.0, 0.1, "Specifies Y coordinate of light vector.", "%.2f" }, { 12, 300, 40, -5.0, 5.0, 1.0, 0.1, "Specifies Z coordinate of light vector.", "%.2f" }, { 13, 360, 40, 0.0, 1.0, 0.0, 1.0, "Specifies directional (0) or positional (1) light.", "%.2f" } }; GLboolean swapped = GL_FALSE; GLboolean world_draw = GL_TRUE; GLMmodel* pmodel = NULL; GLint selection = 0;
99/117
void redisplay_all(void); GLdouble projection[16], modelview[16], inverse[16]; GLuint window, world, screen, command; GLuint sub_width = 256, sub_height = 256;
GLvoid *font_style = GLUT_BITMAP_TIMES_ROMAN_10; void setfont(char* name, int size) { font_style = GLUT_BITMAP_HELVETICA_10; if (strcmp(name, "helvetica") == 0) { if (size == 12) font_style = GLUT_BITMAP_HELVETICA_12; else if (size == 18) font_style = GLUT_BITMAP_HELVETICA_18; } else if (strcmp(name, "times roman") == 0) { font_style = GLUT_BITMAP_TIMES_ROMAN_10; if (size == 24) font_style = GLUT_BITMAP_TIMES_ROMAN_24; } else if (strcmp(name, "8x13") == 0) { font_style = GLUT_BITMAP_8_BY_13; } else if (strcmp(name, "9x15") == 0) { font_style = GLUT_BITMAP_9_BY_15; } } void drawstr(GLuint x, GLuint y, char* format, ...) { va_list args; char buffer[255], *s; va_start(args, format); vsprintf(buffer, format, args); va_end(args); glRasterPos2i(x, y); for (s = buffer; *s; s++) glutBitmapCharacter(font_style, *s); } void cell_draw(cell* cell) { glColor3ub(0, 255, 128); if (selection == cell->id) { glColor3ub(255, 255, 0); drawstr(10, 240, cell->info); glColor3ub(255, 0, 0); } drawstr(cell->x, cell->y, cell->format, cell->value); }
100/117 int cell_hit(cell* cell, int x, int y) { if (x > cell->x && x < cell->x + 60 && y > cell->y-30 && y < cell->y+10) return cell->id; return 0; } void cell_update(cell* cell, int update) { if (selection != cell->id) return; cell->value += update * cell->step; if (cell->value < cell->min) cell->value = cell->min; else if (cell->value > cell->max) cell->value = cell->max; } void cell_vector(float* dst, cell* cell, int num) { while (--num >= 0) dst[num] = cell[num].value; } void drawmodel(void) { if (!pmodel) { pmodel = glmReadOBJ("data/soccerball.obj"); if (!pmodel) exit(0); glmUnitize(pmodel); glmFacetNormals(pmodel); glmVertexNormals(pmodel, 90.0); } glmDraw(pmodel, GLM_SMOOTH | GLM_MATERIAL); } void drawaxes(void) { glColor3ub(255, 0, 0); glBegin(GL_LINE_STRIP); glVertex3f(0.0, 0.0, 0.0); glVertex3f(1.0, 0.0, 0.0); glVertex3f(0.75, 0.25, 0.0); glVertex3f(0.75, -0.25, 0.0); glVertex3f(1.0, 0.0, 0.0); glVertex3f(0.75, 0.0, 0.25); glVertex3f(0.75, 0.0, -0.25);
101/117 glVertex3f(1.0, 0.0, 0.0); glEnd(); glBegin(GL_LINE_STRIP); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 1.0, 0.0); glVertex3f(0.0, 0.75, 0.25); glVertex3f(0.0, 0.75, -0.25); glVertex3f(0.0, 1.0, 0.0); glVertex3f(0.25, 0.75, 0.0); glVertex3f(-0.25, 0.75, 0.0); glVertex3f(0.0, 1.0, 0.0); glEnd(); glBegin(GL_LINE_STRIP); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 0.0, 1.0); glVertex3f(0.25, 0.0, 0.75); glVertex3f(-0.25, 0.0, 0.75); glVertex3f(0.0, 0.0, 1.0); glVertex3f(0.0, 0.25, 0.75); glVertex3f(0.0, -0.25, 0.75); glVertex3f(0.0, 0.0, 1.0); glEnd(); glColor3ub(255, 255, 0); glRasterPos3f(1.1, 0.0, 0.0); glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, 'x'); glRasterPos3f(0.0, 1.1, 0.0); glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, 'y'); glRasterPos3f(0.0, 0.0, 1.1); glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, 'z'); } void identity(GLdouble { m[0+4*0] = 1; m[1+4*0] = 0; m[2+4*0] = 0; m[3+4*0] = 0; }
m[16]) m[0+4*1] m[1+4*1] m[2+4*1] m[3+4*1]
= = = =
0; 1; 0; 0;
m[0+4*2] m[1+4*2] m[2+4*2] m[3+4*2]
= = = =
0; 0; 1; 0;
m[0+4*3] m[1+4*3] m[2+4*3] m[3+4*3]
GLboolean invert(GLdouble src[16], GLdouble inverse[16]) { double t; int i, j, k, swap; GLdouble tmp[4][4]; identity(inverse); for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { tmp[i][j] = src[i*4+j]; } } for (i = 0; i < 4; i++) {
= = = =
0; 0; 0; 1;
102/117 /* look for largest element in column. */ swap = i; for (j = i + 1; j < 4; j++) { if (fabs(tmp[j][i]) > fabs(tmp[i][i])) { swap = j; } } if (swap != i) { /* swap rows. */ for (k = 0; k < 4; k++) { t = tmp[i][k]; tmp[i][k] = tmp[swap][k]; tmp[swap][k] = t; t = inverse[i*4+k]; inverse[i*4+k] = inverse[swap*4+k]; inverse[swap*4+k] = t; } } if (tmp[i][i] == 0) { /* no non-zero pivot. the matrix is singular, which shouldn't happen. This means the user gave us a bad matrix. */ return GL_FALSE; } t = tmp[i][i]; for (k = 0; k < 4; k++) { tmp[i][k] /= t; inverse[i*4+k] /= t; } for (j = 0; j < 4; j++) { if (j != i) { t = tmp[j][i]; for (k = 0; k < 4; k++) { tmp[j][k] -= tmp[i][k]*t; inverse[j*4+k] -= inverse[i*4+k]*t; } } } } return GL_TRUE; } float normalize(float* v) { float length; length = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); v[0] /= length; v[1] /= length; v[2] /= length; return length;
103/117 } void main_reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, width, height, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); #define GAP 25 /* gap between subwindows */ sub_width = (width-GAP*3)/2.0; sub_height = (height-GAP*3)/2.0; glutSetWindow(world); glutPositionWindow(GAP, GAP); glutReshapeWindow(sub_width, sub_height); glutSetWindow(screen); glutPositionWindow(GAP+sub_width+GAP, GAP); glutReshapeWindow(sub_width, sub_height); glutSetWindow(command); glutPositionWindow(GAP, GAP+sub_height+GAP); glutReshapeWindow(sub_width+GAP+sub_width, sub_height); } void main_display(void) { glClearColor(0.8, 0.8, 0.8, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3ub(0, 0, 0); setfont("helvetica", 12); drawstr(GAP, GAP-5, "World-space view"); drawstr(GAP+sub_width+GAP, GAP-5, "Screen-space view"); drawstr(GAP, GAP+sub_height+GAP-5, "Command manipulation window"); glutSwapBuffers(); } void main_keyboard(unsigned char key, int x, int y) { switch (key) { case 's': swapped = !swapped; break; case 'r': light[0].value = 1.5; light[1].value = 1.0; light[2].value = 1.0; light[3].value = 0.0; lookat[0].value = 0.0; lookat[1].value = 0.0; lookat[2].value = 2.0; lookat[3].value = 0.0; lookat[4].value = 0.0;
104/117 lookat[5].value lookat[6].value lookat[7].value lookat[8].value break; case 27: exit(0); }
= = = =
0.0; 0.0; 1.0; 0.0;
redisplay_all(); } void world_reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)width/height, 0.01, 256.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -5.0); glRotatef(-45.0, 0.0, 1.0, 0.0); glClearColor(0.0, 0.0, 0.0, 0.0); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHT0); } void world_display(void) { GLfloat pos[4]; double length; float l[3]; cell_vector(pos, light, 4); l[0] = lookat[3].value - lookat[0].value; l[1] = lookat[4].value - lookat[1].value; l[2] = lookat[5].value - lookat[2].value; invert(modelview, inverse); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (swapped) { glPushMatrix(); glTranslatef(l[0], l[1], l[2]); glMultMatrixd(inverse); glColor3ub(255, 255, 255); glBegin(GL_LINE_STRIP); glVertex3f(0.0, 0.0, 0.0); if (pos[3] == 0) /* 10.0 = 'infinite' light */ glVertex3f(pos[0]*10.0,pos[1]*10.0,pos[2]*10.0); else glVertex3f(pos[0], pos[1], pos[2]); glEnd(); glLightfv(GL_LIGHT0, GL_POSITION, pos);
105/117 glPopMatrix(); } else { glColor3ub(255, 255, 255); glBegin(GL_LINE_STRIP); glVertex3f(0.0, 0.0, 0.0); if (pos[3] == 0) /* 10.0 = 'infinite' light */ glVertex3f(pos[0]*10.0,pos[1]*10.0,pos[2]*10.0); else glVertex3f(pos[0], pos[1], pos[2]); glEnd(); glLightfv(GL_LIGHT0, GL_POSITION, pos); } length = normalize(l); if (world_draw) { glEnable(GL_LIGHTING); drawmodel(); glDisable(GL_LIGHTING); } glPushMatrix(); glMultMatrixd(inverse); /* draw the axes and eye vector */ glPushMatrix(); glColor3ub(0, 0, 255); glBegin(GL_LINE_STRIP); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 0.0, -1.0*length); glVertex3f(0.1, 0.0, -0.9*length); glVertex3f(-0.1, 0.0, -0.9*length); glVertex3f(0.0, 0.0, -1.0*length); glVertex3f(0.0, 0.1, -0.9*length); glVertex3f(0.0, -0.1, -0.9*length); glVertex3f(0.0, 0.0, -1.0*length); glEnd(); glColor3ub(255, 255, 0); glRasterPos3f(0.0, 0.0, -1.1*length); glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, 'e'); glColor3ub(255, 0, 0); glScalef(0.4, 0.4, 0.4); drawaxes(); glPopMatrix(); invert(projection, inverse); glMultMatrixd(inverse); /* draw the viewing frustum */ glColor3f(0.2, 0.2, 0.2); glBegin(GL_QUADS); glVertex3i(1, 1, 1); glVertex3i(-1, 1, 1); glVertex3i(-1, -1, 1); glVertex3i(1, -1, 1); glEnd();
106/117 glColor3ub(128, 196, 128); glBegin(GL_LINES); glVertex3i(1, 1, -1); glVertex3i(1, 1, 1); glVertex3i(-1, 1, -1); glVertex3i(-1, 1, 1); glVertex3i(-1, -1, -1); glVertex3i(-1, -1, 1); glVertex3i(1, -1, -1); glVertex3i(1, -1, 1); glEnd(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(0.2, 0.2, 0.4, 0.5); glBegin(GL_QUADS); glVertex3i(1, 1, -1); glVertex3i(-1, 1, -1); glVertex3i(-1, -1, -1); glVertex3i(1, -1, -1); glEnd(); glDisable(GL_BLEND); glPopMatrix(); glutSwapBuffers(); } void world_menu(int value) { switch (value) { case 'm': world_draw = !world_draw; break; } redisplay_all(); } void screen_reshape(int width, int height) { GLfloat pos[4]; cell_vector(pos, light, 4); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (float)width/height, 0.5, 8.0); glGetDoublev(GL_PROJECTION_MATRIX, projection); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (swapped) { glLightfv(GL_LIGHT0, GL_POSITION, pos); gluLookAt(lookat[0].value, lookat[1].value, lookat[2].value, lookat[3].value, lookat[4].value, lookat[5].value, lookat[6].value, lookat[7].value, lookat[8].value); } else {
107/117 gluLookAt(lookat[0].value, lookat[1].value, lookat[2].value, lookat[3].value, lookat[4].value, lookat[5].value, lookat[6].value, lookat[7].value, lookat[8].value); glLightfv(GL_LIGHT0, GL_POSITION, pos); } glGetDoublev(GL_MODELVIEW_MATRIX, modelview); glClearColor(0.2, 0.2, 0.2, 0.0); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); } void screen_display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawmodel(); glutSwapBuffers(); } void screen_menu(int value) { char* name = 0; switch (value) { case 'a': name = "data/al.obj"; break; case 's': name = "data/soccerball.obj"; break; case 'd': name = "data/dolphins.obj"; break; case 'f': name = "data/flowers.obj"; break; case 'j': name = "data/f-16.obj"; break; case 'p': name = "data/porsche.obj"; break; case 'r': name = "data/rose+vase.obj"; break; } if (name) { pmodel = glmReadOBJ(name); if (!pmodel) exit(0); glmUnitize(pmodel); glmFacetNormals(pmodel); glmVertexNormals(pmodel, 90.0); }
108/117 redisplay_all(); } void command_reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, width, height, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.0, 0.0, 0.0, 0.0); } void command_display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3ub(255, 255, 255); setfont("helvetica", 18); if (swapped) { lookat[0].y = 120; lookat[1].y = 120; lookat[2].y = 120; lookat[3].y = 120+40; lookat[4].y = 120+40; lookat[5].y = 120+40; lookat[6].y = 120+80; lookat[7].y = 120+80; lookat[8].y = 120+80; } else { lookat[0].y = 80; lookat[1].y = 80; lookat[2].y = 80; lookat[3].y = 80+40; lookat[4].y = 80+40; lookat[5].y = 80+40; lookat[6].y = 80+80; lookat[7].y = 80+80; lookat[8].y = 80+80; } drawstr(30, light[0].y, "GLfloat pos[4] = {"); drawstr(230, light[0].y, ","); drawstr(290, light[0].y, ","); drawstr(350, light[0].y, ","); drawstr(410, light[0].y, "};"); if (swapped) drawstr(30, 80, "glLightfv(GL_LIGHT0, GL_POSITION, pos);"); else drawstr(30, 200, "glLightfv(GL_LIGHT0, GL_POSITION, pos);"); drawstr(78, lookat[0].y, "gluLookAt("); drawstr(230, lookat[0].y, ","); drawstr(290, lookat[0].y, ",");
109/117 drawstr(350, drawstr(380, drawstr(230, drawstr(290, drawstr(350, drawstr(380, drawstr(230, drawstr(290, drawstr(350, drawstr(380,
lookat[0].y, lookat[0].y, lookat[3].y, lookat[3].y, lookat[3].y, lookat[3].y, lookat[6].y, lookat[6].y, lookat[6].y, lookat[6].y,
","); "<- eye"); ","); ","); ","); "<- center"); ","); ","); ");"); "<- up");
cell_draw(&light[0]); cell_draw(&light[1]); cell_draw(&light[2]); cell_draw(&light[3]); cell_draw(&lookat[0]); cell_draw(&lookat[1]); cell_draw(&lookat[2]); cell_draw(&lookat[3]); cell_draw(&lookat[4]); cell_draw(&lookat[5]); cell_draw(&lookat[6]); cell_draw(&lookat[7]); cell_draw(&lookat[8]); if (!selection) { glColor3ub(255, 255, 0); drawstr(10, 240, "Click on the arguments and move the mouse to modify values."); } glutSwapBuffers(); } int old_y; void command_mouse(int button, int state, int x, int y) { selection = 0; if (state == GLUT_DOWN) { /* mouse should only hit _one_ of the cells, so adding up all the hits just propagates a single hit. */ selection += cell_hit(&light[0], x, y); selection += cell_hit(&light[1], x, y); selection += cell_hit(&light[2], x, y); selection += cell_hit(&light[3], x, y); selection += cell_hit(&lookat[0], x, y); selection += cell_hit(&lookat[1], x, y); selection += cell_hit(&lookat[2], x, y); selection += cell_hit(&lookat[3], x, y); selection += cell_hit(&lookat[4], x, y); selection += cell_hit(&lookat[5], x, y); selection += cell_hit(&lookat[6], x, y);
110/117 selection += cell_hit(&lookat[7], x, y); selection += cell_hit(&lookat[8], x, y); } old_y = y; redisplay_all(); } void command_motion(int x, int y) { cell_update(&light[0], old_y-y); cell_update(&light[1], old_y-y); cell_update(&light[2], old_y-y); cell_update(&light[3], old_y-y); cell_update(&lookat[0], old_y-y); cell_update(&lookat[1], old_y-y); cell_update(&lookat[2], old_y-y); cell_update(&lookat[3], old_y-y); cell_update(&lookat[4], old_y-y); cell_update(&lookat[5], old_y-y); cell_update(&lookat[6], old_y-y); cell_update(&lookat[7], old_y-y); cell_update(&lookat[8], old_y-y); old_y = y; redisplay_all(); } void command_menu(int value) { main_keyboard((unsigned char)value, 0, 0); } void redisplay_all(void) { glutSetWindow(command); glutPostRedisplay(); glutSetWindow(world); world_reshape(sub_width, sub_height); glutPostRedisplay(); glutSetWindow(screen); screen_reshape(sub_width, sub_height); glutPostRedisplay(); } int main(int argc, char** argv) { glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); glutInitWindowSize(512+GAP*3, 512+GAP*3); glutInitWindowPosition(50, 50); glutInit(&argc, argv);
111/117
window = glutCreateWindow("Light Positioning"); glutReshapeFunc(main_reshape); glutDisplayFunc(main_display); glutKeyboardFunc(main_keyboard); world = glutCreateSubWindow(window, GAP, GAP, 256, 256); glutReshapeFunc(world_reshape); glutDisplayFunc(world_display); glutKeyboardFunc(main_keyboard); glutCreateMenu(world_menu); glutAddMenuEntry("Toggle model", 'm'); glutAttachMenu(GLUT_RIGHT_BUTTON); screen = glutCreateSubWindow(window, GAP+256+GAP, GAP, 256, 256); glutReshapeFunc(screen_reshape); glutDisplayFunc(screen_display); glutKeyboardFunc(main_keyboard); glutCreateMenu(screen_menu); glutAddMenuEntry("Models", 0); glutAddMenuEntry("", 0); glutAddMenuEntry("Soccerball", 's'); glutAddMenuEntry("Al Capone", 'a'); glutAddMenuEntry("F-16 Jet", 'j'); glutAddMenuEntry("Dolphins", 'd'); glutAddMenuEntry("Flowers", 'f'); glutAddMenuEntry("Porsche", 'p'); glutAddMenuEntry("Rose", 'r'); glutAttachMenu(GLUT_RIGHT_BUTTON); command = glutCreateSubWindow(window, GAP+256+GAP, GAP+256+GAP, 256, 256); glutReshapeFunc(command_reshape); glutDisplayFunc(command_display); glutMotionFunc(command_motion); glutMouseFunc(command_mouse); glutKeyboardFunc(main_keyboard); glutCreateMenu(command_menu); glutAddMenuEntry("Light Positioning", 0); glutAddMenuEntry("", 0); glutAddMenuEntry("[s] Swap lookat/position calls", 's'); glutAddMenuEntry("[r] Reset parameters", 'r'); glutAddMenuEntry("", 0); glutAddMenuEntry("Quit", 27); glutAttachMenu(GLUT_RIGHT_BUTTON); redisplay_all(); glutMainLoop(); return 0; }
112/117
Tutorial 12.Vertex Animation Banyak obyek sebetulnya bukan benda rigid. Sebagai contoh adalah bendera atau selembar kertas. Program 19 memberi ilustrasi tentang bagaimana membuat suatu bendera berkibar. Program 19 melakukan ilusi berkibar dengan merubah posisi relatif suatu vertex terhadap koordinat bendanya. Cara yang lebih canggih mencakup proses pemodelan dinamika benderanya. TUGAS: Terangkan bagaimana cara bekerjanya animasi bendera tersebut. Program 19 #include #include #include #include #include #include #include #include
<windows.h> <math.h> <stdio.h> <stdlib.h> <string.h> <stdarg.h>
float points[45][45][3]; "Wave" int wiggle_count = 0;
// Math Library Header File
// The Array For The Points On The Grid Of Our // Counter Used To Control How Fast Flag Waves
GLfloat xrot; GLfloat yrot; GLfloat zrot; GLfloat hold;
// X Rotation // Y Rotation // Z Rotation // Temporarily Holds
GLuint texture[1];
// Storage For One Texture ( NEW )
AUX_RGBImageRec *LoadBMP(char *Filename) Bitmap Image { FILE *File=NULL; // File Handle
( ( ( A
NEW ) NEW ) NEW ) Floating Point Value
// Loads A
if (!Filename) // Make Sure A Filename Was Given { return NULL; // If Not Return NULL } File=fopen(Filename,"r"); Check To See If The File Exists if (File) // Does The File Exist? { fclose(File); // Close The Handle return auxDIBImageLoad(Filename); Bitmap And Return A Pointer } return NULL;
//
// Load The
113/117 // If Load Failed Return NULL } int LoadGLTextures() // Load Bitmaps And Convert To Textures { int Status=FALSE; // Status Indicator AUX_RGBImageRec *TextureImage[1]; Storage Space For The Texture memset(TextureImage,0,sizeof(void *)*1); To NULL
// Create
// Set The Pointer
// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit if (TextureImage[0]=LoadBMP("Data/Tim.bmp")) { Status=TRUE; // Set The Status To TRUE glGenTextures(1, &texture[0]); Create The Texture
//
// Typical Texture Generation Using Data From The Bitmap glBindTexture(GL_TEXTURE_2D, texture[0]); glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); } if (TextureImage[0]) // If Texture Exists { if (TextureImage[0]->data) // If Texture Image Exists { free(TextureImage[0]->data); // Free The Texture Image Memory } free(TextureImage[0]); // Free The Image Structure } return Status; // Return The Status } void resize(GLsizei width, GLsizei height) The GL Window { if (height==0) // Prevent A Divide By Zero By { height=1; // Making Height Equal One } glViewport(0,0,width,height); Reset The Current Viewport
// Resize And Initialize
//
114/117 glMatrixMode(GL_PROJECTION); Select The Projection Matrix glLoadIdentity(); // Reset The Projection Matrix
//
// Calculate The Aspect Ratio Of The Window gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f); glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix glLoadIdentity(); // Reset The Modelview Matrix } int init(GLvoid) // All Setup For OpenGL Goes Here { if (!LoadGLTextures()) // Jump To Texture Loading Routine ( NEW ) { return FALSE; // If Texture Didn't Load Return FALSE } glEnable(GL_TEXTURE_2D); Enable Texture Mapping ( NEW ) glShadeModel(GL_SMOOTH); Enable Smooth Shading glClearColor(0.0f, 0.0f, 0.0f, 0.5f); Background glClearDepth(1.0f); // Depth Buffer Setup glEnable(GL_DEPTH_TEST); Enables Depth Testing glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); Perspective Calculations glPolygonMode( GL_BACK, GL_FILL ); Back Face Is Solid glPolygonMode( GL_FRONT, GL_LINE ); Front Face Is Made Of Lines
// // // Black
//
// Really Nice // //
for(int x=0; x<45; x++) { for(int y=0; y<45; y++) { points[x][y][0]=float((x/5.0f)-4.5f); points[x][y][1]=float((y/5.0f)-4.5f); points[x][y][2]=float(sin((((x/5.0f)*40.0f)/360.0f)*3.141592654*2.0f)); } } return TRUE; // Initialization Went OK } void mydisplay(GLvoid) // Here's Where We Do All The Drawing { int x, y; float float_x, float_y, float_xb, float_yb;
115/117
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); And The Depth Buffer glLoadIdentity(); // Reset The View
// Clear The Screen
glTranslatef(0.0f,0.0f,-12.0f); glRotatef(xrot,1.0f,0.0f,0.0f); glRotatef(yrot,0.0f,1.0f,0.0f); glRotatef(zrot,0.0f,0.0f,1.0f); glBindTexture(GL_TEXTURE_2D, texture[0]); glBegin(GL_QUADS); for( x = 0; x < 44; x++ ) { for( y = 0; y < 44; y++ ) { float_x = float(x)/44.0f; float_y = float(y)/44.0f; float_xb = float(x+1)/44.0f; float_yb = float(y+1)/44.0f; glTexCoord2f( float_x, float_y); glVertex3f( points[x][y][0], points[x][y][1], points[x][y][2] ); glTexCoord2f( float_x, float_yb ); glVertex3f( points[x][y+1][0], points[x][y+1][1], points[x][y+1][2] ); glTexCoord2f( float_xb, float_yb ); glVertex3f( points[x+1][y+1][0], points[x+1][y+1][1], points[x+1][y+1][2] ); glTexCoord2f( float_xb, float_y ); glVertex3f( points[x+1][y][0], points[x+1][y][1], points[x+1][y][2] ); } } glEnd(); if( wiggle_count == 2 ) { for( y = 0; y < 45; y++ ) { hold=points[0][y][2]; for( x = 0; x < 44; x++) { points[x][y][2] = points[x+1][y][2]; } points[44][y][2]=hold; } wiggle_count = 0; } wiggle_count++; xrot+=0.3f; yrot+=0.2f; zrot+=0.4f;
116/117
glFlush(); glutSwapBuffers(); } void myTimeOut(int id) { // called if timer event // ...advance the state of animation incrementally... //rot+=1; glutPostRedisplay(); // request redisplay glutTimerFunc(10, myTimeOut, 0); // request next timer event } void myKeyboard(unsigned char key,int x, int y) { } void mySpecialKeyboard(int key,int x, int y) { } int main(int argc, char** argv) { glutInit(&argc,argv); glutInitDisplayMode( GLUT_RGB |GLUT_DOUBLE| GLUT_DEPTH ); glutInitWindowSize(500,500); glutInitWindowPosition(0,0); glutCreateWindow("simple"); // callbacks glutDisplayFunc(mydisplay); glutKeyboardFunc(myKeyboard); glutSpecialFunc(mySpecialKeyboard); glutTimerFunc(100, myTimeOut, 0); glutReshapeFunc(resize); init(); glutMainLoop(); return 0; }