電腦遊戲製作開發設計論壇 首頁 電腦遊戲製作開發設計論壇
任何可以在PC上跑的遊戲都可以討論,主要以遊戲之製作開發為主軸,希望讓台灣的遊戲人有個討論、交流、教學、經驗傳承的園地
 
 常見問題常見問題   搜尋搜尋   會員列表會員列表   會員群組會員群組   會員註冊會員註冊 
 個人資料個人資料   登入檢查您的私人訊息登入檢查您的私人訊息   登入登入 

Google
GLSL&GLUT 從環境設定開始的基礎教學(04) - 光線模擬

 
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 遊戲程式高級班:DirectX、OpenGL及各種圖型函式庫
上一篇主題 :: 下一篇主題  
發表人 內容
Director
偶而上來逛逛的過客


註冊時間: 2013-11-04
文章: 13

381.66 果凍幣

發表發表於: 2013-11-29, PM 4:42 星期五    文章主題: GLSL&GLUT 從環境設定開始的基礎教學(04) - 光線模擬 引言回覆

引言回覆:

 ※2015/7/5補充:
  其實已經過很久了,GLee似乎已經沒再繼續更新,我在寫了這篇教學不久後也改用了GLEW取代Glee,它們的功能及函式名稱其實是幾乎一樣的,安裝方式同其他函式庫
  所以只需要將glee.h及glee.lib改為glew.h及glew.lib就好,這是下載的地方,抓Binaries那個http://glew.sourceforge.net/。
  lib用 lib\Release\Win32 裡面的。



 那麼我們今天來談談怎麼做出光線吧 Very Happy

 看看這精美的反射光點。


 要如何讓3D的場景看起來很有立體感呢?

 答案就是加上陰影、光線,來進一步模擬真實世界,如果有唸過還是零分大大的OpenGL入門系列教學,應該就已經會用GL_LIGHT0來設置內建的光線了!

 那今天,我們就是要利用Shader以及數學來把光線客製化,是不是很令人興奮呢 Smile

引言回覆:

 在開始之前要先提到一個概念,即便是用GL_LIGHT0設置的光線,應該也會有人注意到,實作上一般的設置方法,光線的位置會隨著移動視角而變動。

 這是因為用來設置光座標的這行。
代碼:
   glLightfv( GL_LIGHT0, GL_POSITION, light_position );


 這樣設置光源,在一般情況下,光座標會是獨立於modelView之外的。

 什麼意思呢?

 就是說你的光不會跟著你創造的3D場景旋轉移動,而是與你的視角保持一定相對位置,如果你看過我寫的上上篇(02 - 關於矩陣),那我的意思就是說,這樣子設置的光線不是世界座標。

 結果就是,你的光像是你用2D畫在螢幕上的一個圖形,無論你怎麼改變視角,他都是定在那裡 Crying or Very sad

 那要怎麼讓光定在一點照物體呢? 你必須一直維持光與物體的相對位置。

 作法很簡單,把你的光座標用modelViewMatrix處理過之後,在你每次要畫物體之前重新設置就好。

 那我們來回顧一下第二章,希望你已經安裝好glm函式庫了 Very Happy
代碼:
   // gluLookAt版本
   // 假設這是你的gluLookAt
   gluLookAt( 0, 0, 2, 0, 0, 0, 0, 1, 0);

   // 用第二章提到的方法
   // 利用glm來製作modelViewMatrix
   glm::vec3 lsite( 0, 0, 2 );
   glm::vec3 lpoint( 0, 0, 0 );
   glm::vec3 lnvector( 0, 1, 0 );
   glm::mat4 modelViewMatrix = glm::lookAt( lsite, lpoint, lnvector );

   // light_position是你預先設定的光座標
   // 最尾的1只是用來補齊glm::vec4, 沒特別意思, 放什麼都可以
   // 為了讓座標能跟四維矩陣相乘, 我們才需要把光座標補齊成四維向量
   // light_p就是補齊後的光座標
   // light_wp就是利用modelViewMatrix校正後的光座標
   glm::vec4 light_p( light_position.x, light_position.y, light_position.z, 1 );
   glm::vec4 light_wp = modelViewMatrix * light_p;

   // 為了能夠放進去glLightfv, 轉成float的陣列
   GLfloat worldlight_position[3];
   worldlight_position[0] = light_wp.x;
   worldlight_position[1] = light_wp.y;
   worldlight_position[2] = light_wp.z;

   // 每次畫圖前都重新設置座標
   glLightfv( GL_LIGHT0, GL_POSITION, worldlight_position );


 突然想到有一點我之前忘了提,學過線性代數的朋友應該會覺得modelViewMatrix * light_p這行很奇怪。
 照我第二章給的矩陣樣子,應該是要寫light_p * modelViewMatrix,反過來乘才對,這是因為:
 OpenGL裡面的矩陣型態,是我圖中那種矩陣的轉置矩陣><
 所以說,因為是轉置矩陣,所以要反過來乘 Smile
 我想表達的是,順序很重要QQ
 一定要先矩陣後座標!

 最後,假如你用來調整視野的是我上一章貼的camera class,你可以打開cpp看看裡面是怎麼調整modelViewMatrix的。
代碼:
   // camera class版本
   // 調整的作法都一樣, 只是取Matrix的方法變成這樣
   glm::mat4 modelViewMatrix =
      glm::rotate( -MainCamera.getPitch(), 1.0f, 0.0f, 0.0f ) *
      glm::rotate( -MainCamera.getYaw(), 0.0f, 1.0f, 0.0f ) *
      glm::translate( MainCamera.getLocation().x, MainCamera.getLocation().y, MainCamera.getLocation().z );



 那麼,我想前情提要這樣就夠了,來看Code吧!

 這篇我們沿用上一章的程式碼,要做出開頭圖片那樣子的反射光點(雖然不太自然 Embarassed ),要讓光點很自然就要等到介紹normal mapping的時候了。

 這次比較麻煩的是在Shader上面寫的數學。

 先簡單敘述一下步驟:
  Step1: 製作modelViewMatrix,然後即時調整光座標
  Step2: 把光座標、顏色傳入Shader
  Step3: 調整渲染的材質顏色,做出光線的效果

引言回覆:
Step1


 這部分在前情提要應該講得差不多了,就設定一個光座標下去調整吧!
代碼:
   vector3 lightPosition;
   lightPosition.x = 0;
   lightPosition.y = 0;
   lightPosition.z = 2;

   glm::mat4 modelViewMatrix =
      glm::rotate( -MainCamera.getPitch(), 1.0f, 0.0f, 0.0f ) *
      glm::rotate( -MainCamera.getYaw(), 0.0f, 1.0f, 0.0f ) *
      glm::translate( MainCamera.getLocation().x, MainCamera.getLocation().y, MainCamera.getLocation().z );
   glm::vec4 worldLightPosition = modelViewMatrix * glm::vec4( lightPosition.x, lightPosition.y, lightPosition.z, 1.0f );



引言回覆:
Step2


 這部分也很簡單,我們這次要傳入的Shader的參數就五個
代碼:
// 渲染平面的材質
   glUniform1i( glGetUniformLocation( program, "tex" ), 0 );
   // 校正後的光的世界座標
   glUniform3f( glGetUniformLocation( program, "lightPosition" ), worldLightPosition.x, worldLightPosition.y, worldLightPosition.z );
   // 光的顏色
   glUniform4f( glGetUniformLocation( program, "lambient" ), 0.4, 0.4, 0.4, 1.0 );
   glUniform4f( glGetUniformLocation( program, "ldiffuse" ), 0.7, 0.7, 0.7, 1.0 );
   glUniform4f( glGetUniformLocation( program, "lspecular" ), 1.0, 1.0, 1.0, 1.0 );



 那我們來看這次的Shader。
引言回覆:
Step3


 vertex shader和fragment shader的部分還是一如既往,用第一章的程式碼讀取,只修改這兩個檔案的內容。
代碼:
// vertex.vs

varying vec2 texcoord;
varying vec3 outNormal;
varying vec3 position;

void main()
{
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
   
   position = vec3( gl_ModelViewMatrix * gl_Vertex );
   outNormal = gl_NormalMatrix * gl_Normal;
   
   texcoord = gl_MultiTexCoord0.st;
}


代碼:
// fragment.frag

uniform sampler2D tex;
uniform vec3 lightPosition;

uniform vec4 lambient;
uniform vec4 ldiffuse;
uniform vec4 lspecular;

varying vec2 texcoord;
varying vec3 outNormal;
varying vec3 position;

void main()
{
   vec4 texcolor = texture2D( tex, texcoord );
   
   vec3 normal = normalize( outNormal );
   vec3 surf2light = normalize( lightPosition - position );
   vec3 surf2camera = normalize( -position );
   vec3 reflection = -reflect( surf2camera, normal );

   vec3 ambient = vec3( lambient * texcolor );

   float diffuseContribution = max( 0.0, dot( normal, surf2light ) );
   vec3 diffuse = vec3( diffuseContribution * ldiffuse * texcolor );
   
   float specularContribution = pow( max( 0.0, dot( reflection, surf2light ) ), 32.0);
   vec3 specular = vec3( specularContribution * lspecular );

   gl_FragColor = vec4( ambient + diffuse + specular, 1.0 );
   //gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
}


 各位應該可以明顯看出這次的fragment shader變長了不少,我們一行一行來看 Very Happy

 先從vertex.vs開始,與上次不同的多了兩行
代碼:
   position = vec3( gl_ModelViewMatrix * gl_Vertex );
   outNormal = gl_NormalMatrix * gl_Normal;

 position和outNormal都是varying屬性的變數,也就是我們之後要傳給fragment用的。

 position是把modelViewMatrix乘上vertex之後得到的,各點的世界座標。
 gl_Normal大家可能比較陌生,還記得我們之前在加上光線模擬之後,都要在面上設置法向量嗎? 這個gl_Normal就是那個法向量(glNormal3f(x,y,z)<-這個東西)
 gl_NormalMatrix,再者,又出現了一個新矩陣,它又是什麼? 它其實就是modelViewMatrix去掉最右排跟最下排的3x3矩陣,為什麼要乘上它呢?
引言回覆:
gl_NormalMatrix


 是這樣的:

 透過這張圖應該就很清楚了,一個平面若只是平移還無妨,若是旋轉還是變形,你原本設定的那個法向量也要跟著旋轉變形才行。

 那又為什麼normalMatrix是modelViewMatrix去掉最右排跟最下排的3x3矩陣?

 還記得第二章我說到modelViewMatrix是長這樣的嗎?


 去掉最右排跟最下排就是把Translate的部分拿掉,因為沒有用到,平面如何平移都不會影響法向量。

 我想應該足夠清楚了 Very Happy



 那終於可以開始講fragment shader了。

 texcolor我就不提了。
代碼:
   vec3 normal = normalize( outNormal );

 這行用到的normalize,是把向量的長度等比縮為1的函式,為了讓之後用到的其他函式可以正常操作的預先初始動作
 normal就是處理過的法向量。

代碼:
   vec3 surf2light = normalize( lightPosition - position );

 surf2light是處理過後的,vertex到光座標的向量

代碼:
   vec3 surf2camera = normalize( -position );

 surf2camera是處理過後的,vertex到攝影機的向量。
 ※這個要記住,攝影機的位置永遠都在(0, 0, 0),不可以被gluLookAt給迷惑,當你在改變視野,其實是全部場景的物體一起在旋轉而已! 你的位置是不動的,是世界在旋轉。

代碼:
   vec3 reflection = -reflect( surf2camera, normal )

 reflect是OpenGL在Shader內建的運算函數,會回傳反射向量,可參照http://www.opengl.org/sdk/docs/manglsl/xhtml/reflect.xml
 reflection就是我們的視角對於那個平面的反射向量(物理在教入射角等於反射角時,畫的那兩條向量)。

 再來,我們的光線是來自三個顏色相加,就是大家常聽到的環境光、散射光以及反射光,我也簡單介紹一下。
 環境光(ambient),在實作上的意義就是,讓你即便不開燈也能看到東西的那個光,換句話說,照不到光的東西你要讓它有多少能見度就是環境光的功能。
 散射光(diffuse),讓圖形有立體感就靠它了,它只會加在照的到光的地方,讓照到光的地方更亮,顯出跟沒照到光的地方的對比。
 反射光(specular),在一個平面上面看到的光點就是它。

代碼:
   vec3 ambient = vec3( lambient * texcolor );

 ambient就是把環境光的顏色和材質顏色混和後的顏色。

代碼:
float diffuseContribution = max( 0.0, dot( normal, surf2light ) );

 dot是點積,我們可以透過它的值來判斷兩向量的夾角,可參照http://www.csie.ntnu.edu.tw/~u91029/PointLinePlane.html
 ※用IE瀏覽這個網頁如果都是亂碼,把網頁編碼改成Unicode就可以了
 diffuseContribution,是我們要來判斷散射光要多亮的依據,dot( normal, surf2light )算出來的結果如果小於0,表示法向量和點到光向量夾角大於90度,那這個面肯定照不到光,就設成0.0黑色,像這樣。


代碼:
   vec3 diffuse = vec3( diffuseContribution * ldiffuse * texcolor );

 diffuse就是把光顏色、計算後的光參數還有原先材質顏色混和後的顏色。

代碼:
float specularContribution = pow( max( 0.0, dot( reflection, surf2light ) ), 32.0);

 specularContribution則是把剛剛取得的反射向量和點到光向量做點積,同diffuse背光面不會有反射光,而面光那一面的話,兩個向量夾角愈小(點積值愈大),能看到的光反射面愈大,這個大家可以拿一面鏡子,對著電燈嘗試看看。

 至於為什麼要做pow也就是次方運算,這是為了凝聚出中間那顆最亮的光點,在0~1的小數,以0.5為界,它們的次方數愈高差值會愈大。
 例如說原本是0.5和0.6,相差0.1,但平方後變成0.25及0.36,差值變成0.11,可以增加亮點跟稍微亮的點之間的對比度。
 再者,數字愈接近1的值,次方後還是會趨近於1,例如0.999平方後是0.998001,於是結論就是:
 讓亮的還是那麼亮,稍微暗點的會變得更暗,增加對比度的意思。
 我想這樣說明應該差不多了 Very Happy

代碼:
   vec3 specular = vec3( specularContribution * lspecular );

 specular就是光參數乘上反射光顏色,應該不用多解釋了。

代碼:
   gl_FragColor = vec4( ambient + diffuse + specular, 1.0 );

 最後把所有顏色加起來傳給gl_FragColor就結束了 Smile



 照這樣做,就能夠做出光線的模擬,我想應該沒有什麼還需要介紹的,那麼最後附上範例程式碼。

Example in VC++2012

代碼:
// allheader.h
#ifndef ALLHEADER_H
#define ALLHEADER_H

#include <GL\glm\glm.hpp>
#include <GL\glm\gtc\matrix_transform.hpp>
#include <GL\glm\gtx\transform.hpp>
#include <GL\glm\gtc\type_ptr.hpp>

#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/29
//
// GLSL&GLUT 從環境設定開始的基礎教學(04) - 光線模擬
//---------------------------------------------------------
//
#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;

// 光座標
vector3 lightPosition;

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" );   

   initShader( "vertex.vs", "fragment.frag" );

   glEnable( GL_TEXTURE_2D );

   MainCamera.setSpeed( 0.1, 0.2 );

   lightPosition.x = 0;
   lightPosition.y = 0;
   lightPosition.z = 2;
}

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();
   // camera class版本
   // 調整的作法都一樣, 只是取Matrix的方法變成這樣
   glm::mat4 modelViewMatrix =
      glm::rotate( -MainCamera.getPitch(), 1.0f, 0.0f, 0.0f ) *
      glm::rotate( -MainCamera.getYaw(), 0.0f, 1.0f, 0.0f ) *
      glm::translate( MainCamera.getLocation().x, MainCamera.getLocation().y, MainCamera.getLocation().z );
   glm::vec4 worldLightPosition = modelViewMatrix * glm::vec4( lightPosition.x, lightPosition.y, lightPosition.z, 1.0f );

   glActiveTexture( GL_TEXTURE0 );
   glBindTexture( GL_TEXTURE_2D, myTex );
   // 渲染平面的材質
   glUniform1i( glGetUniformLocation( program, "tex" ), 0 );
   // 校正後的光的世界座標
   glUniform3f( glGetUniformLocation( program, "lightPosition" ), worldLightPosition.x, worldLightPosition.y, worldLightPosition.z );
   // 光的顏色
   glUniform4f( glGetUniformLocation( program, "lambient" ), 0.4, 0.4, 0.4, 1.0 );
   glUniform4f( glGetUniformLocation( program, "ldiffuse" ), 0.7, 0.7, 0.7, 1.0 );
   glUniform4f( glGetUniformLocation( program, "lspecular" ), 1.0, 1.0, 1.0, 1.0 );
   

   glBegin(GL_QUADS);
   glNormal3f( 0, 0, 1 );
   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;
}



ch4.rar
 描述:
本章範例程式碼

下載
 檔名:  ch4.rar
 附件大小:  103.34 KB
 下載次數:  共 258 次



Director 在 2015-7-5, AM 6:06 星期日 作了第 2 次修改
回頂端
檢視會員個人資料 發送私人訊息 發送電子郵件 參觀發表人的個人網站
Kellypo
稍嫌羞澀的路人


註冊時間: 2015-06-25
文章: 2

11.02 果凍幣

發表發表於: 2015-6-25, PM 12:18 星期四    文章主題: 引言回覆

I never met anyone like you before, it's really good.
回頂端
檢視會員個人資料 發送私人訊息 參觀發表人的個人網站
從之前的文章開始顯示:   
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 遊戲程式高級班:DirectX、OpenGL及各種圖型函式庫 所有的時間均為 台灣時間 (GMT + 8 小時)
1頁(共1頁)

 
前往:  
無法 在這個版面發表文章
無法 在這個版面回覆文章
無法 在這個版面編輯文章
無法 在這個版面刪除文章
無法 在這個版面進行投票
可以 在這個版面附加檔案
可以 在這個版面下載檔案


Powered by phpBB © 2001, 2005 phpBB Group
正體中文語系由 phpbb-tw 維護製作