Director 偶而上來逛逛的過客
註冊時間: 2013-11-04 文章: 13
381.66 果凍幣
|
發表於: 2013-11-21, PM 7:43 星期四 文章主題: GLSL&GLUT 從環境設定開始的基礎教學(03) - 導入材質 |
|
|
引言回覆: |
※2015/7/5補充:
其實已經過很久了,GLee似乎已經沒再繼續更新,我在寫了這篇教學不久後也改用了GLEW取代Glee,它們的功能及函式名稱其實是幾乎一樣的,安裝方式同其他函式庫
所以只需要將glee.h及glee.lib改為glew.h及glew.lib就好,這是下載的地方,抓Binaries那個http://glew.sourceforge.net/。
lib用 lib\Release\Win32 裡面的。
|
因為上週在期考,這篇拖了有點久才生出來
那麼這個章節我們就要開始利用Shader做些有趣的事情。
這張圖是由兩個材質疊合成的,看起來像是兩張半透明的材質,這是利用Shader的簡單實作。
這並沒有用到Alpha及GL_BLEND之類的東西,這不過是把兩張圖的顏色加起來除以二得到的結果。
我想表達的是,學會GLSL可以讓我們真正的控制畫面上每一個像素的顏色。
以我來說,我當初學習這個的目的就是想做出影子,然而,我最大的問題是在,我不知道怎麼讓該有影子的地方出現影子(顏色變暗),我不懂得如何讓面上的一小塊材質變色,於是乎,這就是GLSL能做到的事情。
那我們開始來看Code吧!
簡單來描述一下步驟:
Step1: 載入材質是務必要有的
Step2: 告訴Shader我們要用哪張材質來渲染
Step3: 渲染它!
引言回覆: | Step1
在這個範例我選擇用人家寫好的函式庫來導入材質,主要是方便,不必自己處理各種檔案的材質。
我用的是一款叫做Devil的免費函式庫,安裝方式如freeglut,.h放到include、.lib放到lib、dll放到WINDOW資料夾(system32/sysWOW64),最後記得在專案的Linker(連結器)->Additional Dependencies加入你所有額外引入的.lib名稱,那詳細就不再贅述,網址在這http://openil.sourceforge.net/。
這款函式庫的用法有個要注意的地方,在你使用它的功能之前要先預先設定一些東西,不多,四行而已
代碼: | ilInit();
iluInit();
ilutInit();
ilutRenderer( ILUT_OPENGL ); // 告訴ilut我們要用OpenGL來渲染 |
把這四行寫在你做前置設定函式中,或者最少也要放在你開始用它的功能之前。
設定好之後,我們就可以開始使用他的方便功能了。
載入材質的部分不像以往我們要設定諸多glTexParameter,只要用ilutGLLoadImage就可以載入可以給OpenGL使用的材質。
※當然,如果有需要調整諸如GL_TEXTURE_MIN_FILTER之類的屬性,也還是可以的。
代碼: | myTex = ilutGLLoadImage( (wchar_t*)"example.jpg" ); |
myTex就是材質的ID或是handle。
ilutGLLoadImage就是這款函式庫要載入檔案給OpenGL的時候用的函式,引數內的const char*要強轉為wchar_t*才能夠給程式使用。
※string的話就要用到結構中的c_str()轉成const char*之後再轉為wchar_t*。
如此,也不必特地去設定多材質,所有的素材都依這種方式引入即可。
|
再來的部分會比較複雜,我們要初步介紹Shader使用的資料格式。
還是一樣附上這篇,和我的解釋可以對照著看,http://nehe.gamedev.net/article/glsl_an_introduction/25007/。
首先每個全域變數都要有以下三個的其中一種性質。
uniform,當你要從程式傳一些"定量"到Shader中會用到它。
varying,當你要從vertex shader傳資料到fragment shader的時候會用到。
attribute,當你要傳一些會隨著渲染不同地方而改變的資料會用到它(我目前只有在vertex buffer object的實作有用到它)。
他們的用途會在實作中慢慢領略,現在不懂也沒關係,而在這篇我們只會用到uniform還有varying而已。
介紹完屬性之後,再來要介紹的有一些我們比較會常用到的資料型。
vec2、vec3、vec4,簡單來說就是二三四維的向量,他們之間可以互相填充,甚麼意思呢?
例如我可以這樣寫:
代碼: | vec2 a = vec2( 1.0, 0.5 );
vec2 b = vec2( 0.3, 1.0 );
vec4 c = vec4( a, b ); // 這樣c就會是vec4( 1.0, 0.5, 0.3, 1.0) |
我想應該不難懂
那再來是這一章節要貼材質會用上的sampler2D。
他是代表一個2D材質的資料,我們之後會利用它來進行貼圖的動作。
※其他還有sampler1D、3D、Cube、1DShadow、2DShadow,之後用上了再做介紹。
那講完這些我們就可以進行第二步驟了。
引言回覆: | Step2-1
只要第一章程式碼還在,裡面的loadFile、loadShader、initShader沒有更動,我們應該只要修改vertex.vs以及fragment.frag的內容就可以了!
代碼: | // vertex.vs
varying vec2 texcoord; // 用來存放材質的索引(glTexCoord那類的東西)
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
texcoord = gl_MultiTexCoord0.st; // 取得該vertex的texCoord
} |
代碼: | // fragment.frag
uniform sampler2D tex; // 材質1
uniform sampler2D tex2; // 材質2
varying vec2 texcoord; // 從vertex shader傳來的vec2
void main()
{
vec4 texcolor = texture2D(tex, texcoord); // 依照texcoord傳回材質在該索引的顏色
vec4 texcolor2 = texture2D(tex2, texcoord);
gl_FragColor = texcolor; // 貼上材質tex
//gl_FragColor = texcolor*0.5 + texcolor2*0.5; // tex和tex2各混一半
} |
texcoord的格式就是我們要傳資料到fragment shader要用的varying,意義上就是說在vertex shader先把它算一算再送交給fragment shader。
texture2D詳細的可以看這篇,http://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/texturing.php。
gl_MultiTexCoord0.st我們在2-2講。
那麼Shader就在這邊寫完了,再來就剩下如何告訴他我們要用哪張材質。
Step2-2
回到我們的C++程式,
代碼: | GLuint myTex, myTex2;
void Initialize()
{
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( WINDOW_VISION_ANGLE, (float)WINDOW_SIZE_W/(float)WINDOW_SIZE_H, WINDOW_VISION_NEAR, WINDOW_VISION_FAR );
glEnable( GL_DEPTH_TEST );
// ilut初始化
ilInit();
iluInit();
ilutInit();
ilutRenderer(ILUT_OPENGL);
myTex = ilutGLLoadImage( (wchar_t*)"example.jpg" ); // 匯入example.jpg
myTex2 = ilutGLLoadImage( (wchar_t*)"example.bmp" ); // 匯入example.bmp
initShader( "vertex.vs", "fragment.frag" ); // 建立Shader
glEnable( GL_TEXTURE_2D );
}
|
這是我用來做初始設定的函式,基本上跟第一篇的都一樣,只差在ilut的初始化以及引入了兩個圖檔。
那下一步是關鍵,也是開始運用Shader的基礎,我們要把資料傳進去Shader中,如何做? 利用glUniform以及program_id。
代碼: | void Display()
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glClearColor( 0.0, 0.0, 0.0, 1.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
// 這是我自己定義的camera class, 我用glTranslate和glRotate來代替gluLookAt
// 當然也可以自己寫gluLookAt, 這個的程式碼我會在文尾貼上
MainCamera.Control( stateKeyboard, CAM_NO_MOUSE_INPUT );
MainCamera.UpdateCamera();
// 這部分是要點
// Shader中用的不是我們讀取材質後的那個id
// 而是這些被定義好的GL_TEXTURE加上一個數字
// 這代表能夠"同時"被使用的材質數量, 共有0~31, 32張(能夠同時用32張材質渲染一個面)
// 當然如果一個面只要貼一張圖就用一個就好
glActiveTexture( GL_TEXTURE0 ); // 選擇一個材質unit
glBindTexture( GL_TEXTURE_2D, myTex ); // 把你要用的材質綁上去
glActiveTexture( GL_TEXTURE1 ); // 換下一個
glBindTexture( GL_TEXTURE_2D, myTex2 ); //綁材質
// 再來則是傳遞資料到Shader之中
// glUniform就如同我們使用的諸多gl函式一樣會有1f, 2i, 3d之類的字尾
// 1i指的就是傳一個整數進去
// 參數裡面放的第一個, 就是在Shader中對應的那個uniform格式的變數
// 回憶一下這個-> uniform sampler2D tex;
// 第二個之後就是我們要傳入的值, 因為是1i所以只有1個整數
// 傳的東西就是GL_TEXTURE0 <-這個0
glUniform1i( glGetUniformLocation( program, "tex" ), 0 ); // 剛剛glActiveTexture綁的是myTex
glUniform1i( glGetUniformLocation( program, "tex2" ), 1 ); // 這個綁的是myTex2
glBegin(GL_QUADS);
glTexCoord2f( 1, 1 ); //這個就是剛剛在vertex.vs中看到的gl_MultiTexCoord
glVertex3f(1, 1, -5);
glTexCoord2f( 0, 1 );
glVertex3f(-1, 1, -5);
glTexCoord2f( 0, 0 );
glVertex3f(-1, -1, -5);
glTexCoord2f( 1, 0 );
glVertex3f(1, -1, -5);
glEnd();
glutSwapBuffers();
} |
OK! 大多的東西已經在註解中介紹完了。
剩下要提的有兩個。
第一個是介紹vertex.vs中的gl_MultiTexCoord.st的那個.st是什麼東西。
事實上它就是大家很熟悉的材質的ST座標,至於為什麼會這樣寫是因為gl_MultiTexCoord是一個vec4,而我們要把他轉成跟texcoord一樣的vec2才有的動作。
這個vec4很有意思,你可以用xyzw、rgba或是stpq來自由取得其中特定幾項。
例如說:
代碼: | vec4 a = vec4( 1.0, 0.5, 0.3, 1.0 );
vec2 b = a.yz; // 這樣b就會是vec2( 0.5, 0.3 )
vec3 c = a.xzw; // 這樣c就會是vec3( 1.0, 0.3, 1.0 )
vec3 d = a.rba; // 這個結果會跟c一樣 |
當然,要只取其中一項也是可以的。
再來要提的是,可能有些眼尖的朋友會發現,這樣寫不就跟平常渲染材質沒什麼兩樣嗎?
同樣都是先glBindTexture之後設定glTexCoord2f,差別在哪裡呢?
差別就在,我們現在可以操作fragment.frag的內容來改變我們貼上的圖。
我們可以指定gl_FragColor為tex或是tex2,甚至加起來除以二或多少,都可以透過修改fragment.frag就能夠改變程式而不必修改程式碼。
我想表達的是,我們終於開始利用Shader來控制渲染的功能了! (灑花
|
那麼,大家可以開始嘗試用兩張三張或是更多的圖來混和,下一章節沒意外的話會介紹光線的計算,最後附上程式碼。
Example in VC++2012
代碼: | // allheader.h
#ifndef ALLHEADER_H
#define ALLHEADER_H
#include <GL\GLee.h>
#include <GL\freeglut.h>
#include <iostream>
#include <fstream>
#include <string>
#include <math.h>
#define ILUT_USE_OPENGL
#include <IL/il.h>
#include <IL/ilu.h>
#include <IL/ilut.h>
#define PI 3.14159265 // 圓周率
struct vector3
{
float x, y, z;
};
#endif |
代碼: | // camera.h
#ifndef CAMERA_H
#define CAMERA_H
#include "allheader.h"
enum inputType{
CAM_NO_MOUSE_INPUT = 0,
CAM_MOUSE_LEFT,
CAM_MOUSE_RIGHT,
CAM_WHEELMOUSE_UP,
CAM_WHEELMOUSE_DOWN,
CAM_MOUSE_MOTION_LEFT,
CAM_MOUSE_MOTION_RIGHT
};
class camera{
vector3 loc;
float camPitch, camYaw;
float moveRate;
float mouseRate;
void lockCamera();
void moveCameraHori( float dir );
void moveCameraVert( float dir );
public:
camera();
camera( vector3 loc);
camera( vector3 loc, float yaw, float pitch );
camera( vector3 loc, float yaw, float pitch, float moveR, float mouseR );
void Control( bool* stateKeyboard, int stateMouse, int windowSizeW = NULL, int windowSizeH = NULL, int bufferX = NULL, int bufferY = NULL );
void UpdateCamera( );
vector3 getVector();
vector3 getLocation();
float getPitch();
float getYaw();
float getMoveRate();
float getMouseRate();
void setLocation(vector3 vec);
void lookAt(float pitch,float yaw);
void setSpeed( float moveR, float mouseR );
void showLocation();
};
#endif
|
代碼: | // camera.cpp
#include "camera.h"
camera::camera()
{
loc.x = 0.0;
loc.y = 0.0;
loc.z = 0.0;
camPitch = 0;
camYaw = 0;
moveRate = 0.2;
mouseRate = 0.2;
}
camera::camera( vector3 l )
{
loc.x = l.x;
loc.y = l.y;
loc.z = l.z;
camPitch = 0;
camYaw = 0;
moveRate = 0.2;
mouseRate = 0.2;
}
camera::camera( vector3 l, float yaw, float pitch )
{
loc.x = l.x;
loc.y = l.y;
loc.z = l.z;
camPitch = pitch;
camYaw = yaw;
moveRate = 0.2;
mouseRate = 0.2;
}
camera::camera( vector3 l, float yaw, float pitch, float moveR, float mouseR )
{
loc.x = l.x;
loc.y = l.y;
loc.z = l.z;
camPitch = pitch;
camYaw = yaw;
moveRate = moveR;
mouseRate = mouseR;
}
void camera::lockCamera()
{
if(camPitch > 90)
camPitch = 90;
if(camPitch < -90)
camPitch = -90;
if(camYaw < 0.0)
camYaw += 360.0;
if(camYaw > 360.0)
camYaw -= 360;
}
void camera::moveCameraHori( float dir )
{
float rad = (camYaw+dir)*PI/180.0;
loc.x += sin(rad)*moveRate;
loc.z += cos(rad)*moveRate;
}
void camera::moveCameraVert( float dir )
{
float rad = (camPitch+dir)*PI/180.0;
loc.y -= sin(rad)*moveRate;
}
void camera::Control( bool* stateKeyboard, int stateMouse, int windowSizeW, int windowSizeH, int bufferX, int bufferY )
{
// mouse
if( stateMouse != CAM_NO_MOUSE_INPUT )
{
if( stateMouse == CAM_MOUSE_MOTION_LEFT ){
camYaw += mouseRate*(bufferX/2.0);
camPitch += mouseRate*(bufferY/2.0);
}
if( stateMouse == CAM_WHEELMOUSE_UP ){
if( camPitch != 90 && camPitch != -90 )
moveCameraHori(0.0);
moveCameraVert(0.0);
}
if( stateMouse == CAM_WHEELMOUSE_DOWN ){
if( camPitch != 90 && camPitch != -90 )
moveCameraHori(180.0);
moveCameraVert(180.0);
}
if( stateMouse == CAM_MOUSE_MOTION_RIGHT ){
if( bufferX > 0 )
moveCameraHori(90.0);
else
moveCameraHori(270.0);
}
lockCamera();
}
// keyboard
if( stateKeyboard[(unsigned int)'w'] )
{
if( camPitch != 90 && camPitch != -90 )
moveCameraHori(0.0);
moveCameraVert(0.0);
}
if( stateKeyboard[(unsigned int)'s'] )
{
if( camPitch != 90 && camPitch != -90 )
moveCameraHori(180.0);
moveCameraVert(180.0);
}
if( stateKeyboard[(unsigned int)'a'] )
{
moveCameraHori(90.0);
}
if( stateKeyboard[(unsigned int)'d'] )
{
moveCameraHori(270.0);
}
if( stateKeyboard[(unsigned int)'h'] )
{
showLocation();
}
// result
glRotatef( -camPitch, 1.0, 0.0, 0.0 );
glRotatef( -camYaw, 0.0, 1.0, 0.0 );
}
void camera::UpdateCamera()
{
glTranslatef( loc.x, loc.y, loc.z );
}
vector3 camera::getVector()
{
vector3 tmp;
tmp.x = -cos(camPitch*PI/180.0)*sin(camYaw*PI/180.0);
tmp.y = sin(camPitch*PI/180.0);
tmp.z = -cos(camPitch*PI/180.0)*cos(camYaw*PI/180.0);
return tmp;
}
vector3 camera::getLocation()
{
return loc;
}
float camera::getPitch()
{
return camPitch;
}
float camera::getYaw()
{
return camYaw;
}
float camera::getMoveRate()
{
return moveRate;
}
float camera::getMouseRate()
{
return mouseRate;
}
void camera::setLocation( vector3 vec )
{
loc.x = vec.x;
loc.y = vec.y;
loc.z = vec.z;
}
void camera::lookAt( float pitch, float yaw )
{
camPitch = pitch;
camYaw = yaw;
}
void camera::setSpeed( float moveR, float mouseR )
{
moveRate = moveR;
mouseRate = mouseR;
}
void camera::showLocation()
{
std::cout << "Location: " << loc.x << ", " << loc.y << ", " << loc.z << std::endl
<< "Direction: " << camPitch << ", " << camYaw << std::endl;
} |
代碼: | // main.cpp
//---------------------------------------------------------
// 作者: DR
// 2013/11/21
//
// GLSL&GLUT 從環境設定開始的基礎教學(03) - 導入材質
//---------------------------------------------------------
//
#include "allheader.h"
#include "camera.h"
#define WINDOW_SIZE_W 500 // 起始視窗寬度
#define WINDOW_SIZE_H 500 // 起始視窗高度
#define WINDOW_VISION_ANGLE 45 // 視角
#define WINDOW_VISION_NEAR 1 // 最近視野
#define WINDOW_VISION_FAR 10001 // 最遠視野
bool stateKeyboard[256] = { false };
GLuint windowHandle;
int win_size_w = WINDOW_SIZE_W,
win_size_h = WINDOW_SIZE_H;
camera MainCamera;
GLuint myTex, myTex2;
void loadFile( const char* filename, std::string &string )
{
std::ifstream fp(filename);
if( !fp.is_open() ){
std::cout << "Open <" << filename << "> error." << std::endl;
return;
}
char temp[300];
while( !fp.eof() ){
fp.getline( temp, 300 );
string += temp;
string += '\n';
}
fp.close();
}
GLuint loadShader(std::string &source, GLenum type)
{
GLuint ShaderID;
ShaderID = glCreateShader( type ); // 告訴OpenGL我們要創的是哪種shader
const char* csource = source.c_str(); // 把std::string結構轉換成const char*
glShaderSource( ShaderID, 1, &csource, NULL ); // 把程式碼放進去剛剛創建的shader object中
glCompileShader( ShaderID ); // 編譯shader
char error[1000] = "";
glGetShaderInfoLog( ShaderID, 1000, NULL, error ); // 這是編譯過程的訊息, 錯誤什麼的把他丟到error裡面
std::cout << "Complie status: \n" << error << std::endl; // 然後輸出出來
return ShaderID;
}
GLuint vs, fs, program; // 用來儲存shader還有program的id
void initShader(const char* vname, const char* fname)
{
std::string source;
loadFile( vname, source ); // 把程式碼讀進source
vs = loadShader( source, GL_VERTEX_SHADER ); // 編譯shader並且把id傳回vs
source = "";
loadFile( fname, source );
fs = loadShader( source, GL_FRAGMENT_SHADER );
program = glCreateProgram(); // 創建一個program
glAttachShader( program, vs ); // 把vertex shader跟program連結上
glAttachShader( program, fs ); // 把fragment shader跟program連結上
glLinkProgram( program ); // 根據被連結上的shader, link出各種processor
glUseProgram( program ); // 然後使用它
}
void clean()
{
glDetachShader( program, vs );
glDetachShader( program, fs );
glDeleteShader( vs );
glDeleteShader( fs );
glDeleteProgram( program );
}
void Initialize()
{
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( WINDOW_VISION_ANGLE, (float)WINDOW_SIZE_W/(float)WINDOW_SIZE_H, WINDOW_VISION_NEAR, WINDOW_VISION_FAR );
glEnable( GL_DEPTH_TEST );
ilInit();
iluInit();
ilutInit();
ilutRenderer(ILUT_OPENGL);
myTex = ilutGLLoadImage( (wchar_t*)"example.jpg" );
myTex2 = ilutGLLoadImage( (wchar_t*)"example.bmp" );
initShader( "vertex.vs", "fragment.frag" );
glEnable( GL_TEXTURE_2D );
}
void Display()
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glClearColor( 0.0, 0.0, 0.0, 1.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
MainCamera.Control( stateKeyboard, CAM_NO_MOUSE_INPUT );
MainCamera.UpdateCamera();
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, myTex );
glActiveTexture( GL_TEXTURE1 );
glBindTexture( GL_TEXTURE_2D, myTex2 );
glUniform1i( glGetUniformLocation( program, "tex" ), 0 );
glUniform1i( glGetUniformLocation( program, "tex2" ), 1 );
glBegin(GL_QUADS);
glTexCoord2f( 1, 1 );
glVertex3f(1, 1, -5);
glTexCoord2f( 0, 1 );
glVertex3f(-1, 1, -5);
glTexCoord2f( 0, 0 );
glVertex3f(-1, -1, -5);
glTexCoord2f( 1, 0 );
glVertex3f(1, -1, -5);
glEnd();
glutSwapBuffers();
}
void Reshape( int w, int h )
{
glViewport( 0, 0, w, h );
if( h == 0 ) h = 1;
win_size_w = w;
win_size_h = h;
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( WINDOW_VISION_ANGLE, (float)w/(float)h, WINDOW_VISION_NEAR, WINDOW_VISION_FAR );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
}
bool MOUSE_LEFT = false, MOUSE_RIGHT = false;
int mouse_x = 0, mouse_y = 0, old_mouse_x = 0, old_mouse_y = 0;
void Mouse( int button, int state, int x, int y )
{
switch( button ){
case GLUT_LEFT_BUTTON:
if( state ){
MOUSE_LEFT = false;
mouse_x = 0;
mouse_y = 0;
}
else{
MainCamera.Control( stateKeyboard, CAM_MOUSE_LEFT, win_size_w, win_size_h, x, y );
MOUSE_LEFT = true;
old_mouse_x = x;
old_mouse_y = y;
}
break;
case GLUT_RIGHT_BUTTON:
if( state ){
MOUSE_RIGHT = false;
mouse_x = 0;
mouse_y = 0;
}
else{
MainCamera.Control( stateKeyboard, CAM_MOUSE_RIGHT, win_size_w, win_size_h, x, y );
MOUSE_RIGHT = true;
old_mouse_x = x;
old_mouse_y = y;
}
break;
}
}
void MouseWheel( int wheel, int direction, int x, int y )
{
switch( direction ){
case 1:
MainCamera.Control( stateKeyboard, CAM_WHEELMOUSE_UP );
break;
case -1:
MainCamera.Control( stateKeyboard, CAM_WHEELMOUSE_DOWN );
break;
}
}
void MotionMouse( int x, int y )
{
if( MOUSE_LEFT ){
mouse_x = x - old_mouse_x;
mouse_y = y - old_mouse_y;
MainCamera.Control( stateKeyboard, CAM_MOUSE_MOTION_LEFT, win_size_w, win_size_h, mouse_x, mouse_y );
old_mouse_x = x;
old_mouse_y = y;
}
if( MOUSE_RIGHT ){
mouse_x = x - old_mouse_x;
mouse_y = y - old_mouse_y;
MainCamera.Control( stateKeyboard, CAM_MOUSE_MOTION_RIGHT, win_size_w, win_size_h, mouse_x, mouse_y );
old_mouse_x = x;
old_mouse_y = y;
}
}
void Keyboard( unsigned char key, int x, int y )
{
stateKeyboard[key] = true;
}
void KeyboardUp( unsigned char key, int x, int y )
{
stateKeyboard[key] = false;
}
void Timer( int t )
{
glutPostRedisplay();
glutTimerFunc( 1000/60, Timer, 1 );
}
int main( int argc, char** argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH );
glutInitWindowPosition( 0, 0 );
glutInitWindowSize( WINDOW_SIZE_W, WINDOW_SIZE_H );
windowHandle = glutCreateWindow( "GLUT&GLSL example" );
Initialize();
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
glutMouseFunc( Mouse );
glutMouseWheelFunc( MouseWheel );
glutMotionFunc( MotionMouse );
glutKeyboardFunc( Keyboard );
glutKeyboardUpFunc( KeyboardUp );
glutTimerFunc( 1000/60, Timer, 0 );
glutMainLoop();
return 0;
} |
Happy coding! |
|