OpenGL之粒子爆炸特效

OpenGL之粒子爆炸特效

遊戲中最常見『爆炸』特效.最常見做法是使用粒子系統模擬『爆炸』.你大約需要:

  1. 黑底白圓位圖『bmp』如上圖
  2. 爆炸特效音頻『wav』
  3. 粒子系統
  4. 廣告牌

其實所有『爆炸』特效都是圓心紋理位圖.只是它使用『廣告牌』技術讓其正對著你即『相機』. 渲染為紅色並讓其隨時間『下墜』『變暗』『縮小』直至消亡.之前模擬『飄雪』特效時使用三級系統.這次我將粒子結構大大簡化.其代碼我都在模擬『爆炸』特效中實現.W

粒子結構體

typedef struct PARTICLE_TYP {

VECTOR3D pos;// 位置

VECTOR3D pos_prev;// 之前位置

VECTOR3D velocity;// 速度與方向

VECTOR3D acceleration;// 加速度

float    energy;// 生命週期(秒)

float size;// 尺寸

float size_delta;//增量

float weight;// 重量

float wdight_delta;// 重量增量

float color[4];// 顏色

float color_delta[4];// 顏色增量

}PARTICLE,*PARTICLE_PTR;

爆炸特效結構體

typedef struct EXPLOSION_TPY {

int          state;//  狀態

PARTICLE_PTR array;// 數組

int          count;// 粒子數量/爆炸量

VECTOR3D     origin;// 原點

VECTOR3D     velocity;// 速度與方向

VECTOR3D     variation;// 速度變量

VECTOR3D     acceleration;// 加速度

float        energy;// 生命週期(秒)

float        size;// 尺寸 5.0f

float        size_variation ;// 尺寸變量 2.0f

float        spread;// 爆炸傳播範圍

float        color[4];// 顏色

VECTOR2D     texture_coord[4];// 紋理座標

}EXPLOSION,*EXPLOSION_PTR;

生成『爆炸』特效函式

count:爆炸量大約1-10即可.

pos:位置

spread: 爆炸傳播範圍 0.1即可

void Build_Explosion(EXPLOSION_PTR explosion,int count,VECTOR3D_PTR pos,float spread){

explosion->state = MODEL3D_STATE_ACTIVE;

explosion->count = count; // 粒子數量

explosion->array = (PARTICLE_PTR)malloc(sizeof(PARTICLE)*count);// 分配空間

explosion->spread = spread;// 傳播

explosion->size           = 5.0f;// 尺寸

explosion->size_variation = 2.0f;// 尺寸變量

explosion->energy = 1.5f + FRAND_RANGE1() / 2.0f;// 生命週期(秒)

Init_VECTOR3D(&explosion->origin, pos->x, pos->y, pos->z);// 源點

Init_VECTOR3D(&explosion->velocity, 0.0f, 2.0f, 0.0f);// 速度

Init_VECTOR3D(&explosion->variation, 4.0f, 4.0f, 4.0f);// 變量

Init_VECTOR3D(&explosion->acceleration, 0.0f, -5.0f, 0.0f);// 加速度

PARTICLE_PTR particle;// 粒子

for (int i = 0; i < count; ++i){

particle = &explosion->array[i];// 粒子

Buid_Explosion(explosion, particle);//生成粒子『爆炸』特效

}

Set_Pos_DirectSound(&explosion_sound3D, pos->x, pos->y, pos->z);// 設置音源位置

Play_DirectSound(&explosion_sound3D, false);//播放音頻數據

}

生成爆炸粒子函式

void Buid_Explosion(EXPLOSION_PTR explosion, PARTICLE_PTR  particle){

// 在發射區隨機位置生成粒子

particle->pos.x = explosion->origin.x + FRAND_RANGE1() * explosion->spread;// 傳播

particle->pos.y = explosion->origin.y + FRAND_RANGE1() * explosion->spread;

particle->pos.z = explosion->origin.z + FRAND_RANGE1() * explosion->spread;

//給粒子隨機速度

particle->velocity.x += FRAND_RANGE1() * explosion->velocity.x;

particle->velocity.y += FRAND_RANGE1() * explosion->velocity.y;

particle->velocity.z += FRAND_RANGE1() * explosion->velocity.z;

// 加速度

particle->acceleration = explosion->acceleration;

// 生命週期

particle->energy = 1.5f + FRAND_RANGE1() / 2.0f;

// 顏色

particle->color[0] = 1.0f;

particle->color[1] = 0.5f + FRAND_RANGE1() * 0.5f;

particle->color[2] = 0.0f;

particle->color[3] = 1.0f;

// 顏色增量

particle->color_delta[0] = 0.0f;

particle->color_delta[1] = -(particle->color[1] / 2.0f) / particle->energy;

particle->color_delta[2] = 0.0f;

particle->color_delta[3] = -1.0f / particle->energy;

// 設置粒子的大小

particle->size = explosion->size + FRAND_RANGE1() * explosion->size_variation;

particle->size_delta = -particle->size / particle->energy;

}

釋放『爆炸』特效函式

void Free_Explosion(EXPLOSION_PTR explosion){

if (explosion->array != NULL)

free(explosion->array);

explosion->state = MODEL3D_STATE_NULL;

memset(explosion, 0, sizeof(EXPLOSION));// 清空

}

更新『爆炸』特效函式

deltaTime:時間間隔

void Update_Explosion(EXPLOSION_PTR explosion, float deltaTime){

PARTICLE_PTR particle;// 粒子

for (int i = 0; i < explosion->count; ){

particle = &explosion->array[i];// 粒子

// 基於時間和速度更新粒子的位置

particle->pos = particle->pos + particle->velocity * deltaTime;

particle->velocity = particle->velocity + particle->acceleration * deltaTime;

particle->energy = particle->energy – deltaTime;

particle->size += particle->size_delta * deltaTime;

particle->color[3] += particle->color_delta[3] * deltaTime;

particle->color[1] += particle->color_delta[1] * deltaTime;

// 將最後一個粒子移動到當前位置,並減少計數.

if (particle->energy <= 0.0)

explosion->array[i] = explosion->array[–explosion->count];

else

++i;

}

if (explosion->count == 0)

Free_Explosion(explosion); // 釋放

}

渲染『爆炸』特效紋理函式

void Render_Explosion2(EXPLOSION_PTR explosion, BILLBOARD_PTR billboard){

float viewMatrix[16];

VECTOR3D right, up, pos;

GLfloat size;

right = billboard->right;

up = billboard->up;

// 壓入當前屬性

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_DEPTH_TEST);

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);

glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, explosion_texture.ID);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glBegin(GL_QUADS);

for (int i = 0; i < explosion->count; ++i){

PARTICLE_PTR particle = &explosion->array[i];// 粒子

size = particle->size / 2;

pos = particle->pos;

glColor4fv(particle->color);

glTexCoord2f(0.0, 0.0); glVertex3fv((pos + (right + up) * -size).M);

glTexCoord2f(1.0, 0.0); glVertex3fv((pos + (right – up) * size).M);

glTexCoord2f(1.0, 1.0); glVertex3fv((pos + (right + up) * size).M);

glTexCoord2f(0.0, 1.0); glVertex3fv((pos + (up – right) * size).M);

}

glEnd();

glDisable(GL_TEXTURE_2D);

glDisable(GL_BLEND);

glEnable(GL_DEPTH_TEST);

glPopAttrib();// 彈出當前屬性

}

OpenGL之曲棍球

OpenGL之曲棍球

『曲棍球』是碰撞算法最好演示,在遊戲中『球臺』寬300長500,並且有四條圍邊,確保『曲棍球』在『球臺』圍邊範圍內移動. 『球臺』會給『曲棍球』帶來『磨擦』後慢慢慢落來並最後停低.而『玩家球』則通過鼠標移動控制.通過與『曲棍球』相碰給『曲棍球』帶來動力.演示程式:下載.我會給出遊戲設計重點,並給出關鍵代碼

1.『冰球』運動核心是碰撞算法.你試想下當『冰球』高速運動時有可能會穿越圍邊(圍邊使用平面定義).所以你需要準確計算碰撞時間.碰撞代碼睇『碰撞時間

2.控制『玩家角色』通過獲取滑鼠標偏移量與位置相加移動『玩家角色』.你需要通過DirectInput獲得滑鼠偏移量. 並且確保不要移出球臺圍邊.若與『冰球』則重新計算返射方向與速度

根據鼠標運動計算速度::Init_VECTOR3D(&player->velocity, diffX, 0, diffY);

計算位置player->position = player->position + player->velocity;

『玩家角色』與『冰球』碰撞使用『邊界球』進行檢測並重新計算返射方向與速度

puck->velocity = Reflection_VECTOR3D(&puck->velocity,&(puck->velocity ^ player->velocity)) + player->velocity*500;

3.設定『球臺』結構與『圍邊平面

typedef struct TABLE_TPY {

TEXTURE   texture;// 球臺紋理

VECTOR3D  position;// 球臺位置

VECTOR3D  corner[4];//球臺角落

PLANE3D    wall[4];// 球臺圍邊平面

float width;// 寬度

float depth;// 深度

float height;// 高度

}TABLE,*TABLE_PTR;

球臺寬300*長500載入紋理Load_File_Texture()綁定紋理Bind_Image_Texture().圍邊使用Init_PLANE3D()定義為平面

4.定義『冰球』結構

typedef struct PUCK_TPY {

VECTOR3D     position;// 位置

VECTOR3D  acceleration;// 加速度

VECTOR3D  velocity;// 速度

float     radius;// 半徑

}PUCK,*PUCK_PTR;

5.定義『玩家角色』結構

typedef struct PLAYER_TPY {

VECTOR3D  position;// 位置

VECTOR3D  velocity;// 速度

float     radius;// 半徑

float     mouseX, mouseY;

}PLAYER,*PLAYER_PTR;

 

 

OpenGL之陰影

OpenGL陰影

『陰影』有很多種方法,而『投射陰影』技術已被大量的應用於遊戲,從光源位置透視投影3D物體,從而將陰影投射到平面. 而且視覺效果佳.算法思路如下:

  1. 啟用『模板緩存』,將陰影繪畫限制在相應的區域中
  2. 構造投影矩陣,此矩陣根據『光源位置』和被『投影平面』位置來設定,並且與模型視圖矩陣相乘.
  3. 禁用光照和紋理,並設置顏色為黑色.
  4. 禁用深度測試.
  5. 啟用透明混合
  6. 繪畫陰影

落雪陰影演示下載:

構造投影矩陣

void Build_Matrix_Shadow(MATRIX4X4_PTR destMat, VECTOR4D_PTR lightPos, PLANE3D_PTR plane)

{

float dot;// 點積

dot = plane->M[0] * lightPos->M[0] +

plane->M[1] * lightPos->M[1] +

plane->M[2] * lightPos->M[2] +

plane->M[3] * lightPos->M[3];

// 第一列

destMat->_M[0] = dot – lightPos->M[0] * plane->M[0];

destMat->_M[4] = 0.0f – lightPos->M[0] * plane->M[1];

destMat->_M[8] = 0.0f – lightPos->M[0] * plane->M[2];

destMat->_M[12] = 0.0f – lightPos->M[0] * plane->M[3];

// 第二列

destMat->_M[1] = 0.0f – lightPos->M[1] * plane->M[0];

destMat->_M[5] = dot – lightPos->M[1] * plane->M[1];

destMat->_M[9] = 0.0f – lightPos->M[1] * plane->M[2];

destMat->_M[13] = 0.0f – lightPos->M[1] * plane->M[3];

// 第三列

destMat->_M[2] = 0.0f – lightPos->M[2] * plane->M[0];

destMat->_M[6] = 0.0f – lightPos->M[2] * plane->M[1];

destMat->_M[10] = dot – lightPos->M[2] * plane->M[2];

destMat->_M[14] = 0.0f – lightPos->M[2] * plane->M[3];

// 第四列

destMat->_M[3] = 0.0f – lightPos->M[3] * plane->M[0];

destMat->_M[7] = 0.0f – lightPos->M[3] * plane->M[1];

destMat->_M[11] = 0.0f – lightPos->M[3] * plane->M[2];

destMat->_M[15] = dot – lightPos->M[3] * plane->M[3];

}

MATRIX4X4 shadow_matrix; // 投影矩陣

GLfloat   light_pos[4] = { 150.0, 150.0, 150.0, 1.0 };// 『光照位置』

PLANE3D plane  = { 0.0, 1.0, 0.0, 0.0 };// 『投影平面』

構造投影矩陣

Build_Matrix_Shadow(&shadow_matrix,(VECTOR4D_PTR)&Light_Pos,(PLANE3D_PTR) &plane);

渲染陰影示例代碼

// 禁用對所有的然色分量的修改

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

// 禁用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為只讀

glDepthMask(GL_FALSE);

// 啟用模板測試

glEnable(GL_STENCIL_TEST);

// 設置模板比較程序

glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);

// 設置模板ref操作,

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);//

// 繪畫地板,將模板環存中相應的地板像素設為1

Draw_Floor();

// 啟用所有顏色分量的修改

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

//  啟用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為可讀寫

glDepthMask(GL_TRUE);

// 設置模板比較程序,只能在模板緩存中值為1的相應去區域繪製

glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);

// 設置模板操作

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);// 保持當前值

// 繪製”鏡像”

glPushMatrix();

// 反轉

glScalef(1.0f, -1.0f, 1.0f);

//結束鏡像繪畫

glPopMatrix();

// 繪畫地板

Draw_Floor();

// 以黑色繪製陰影,將其與相應表面進行混合,無光照不進行深度測試

glPushMatrix();

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_TEXTURE_2D);// 禁用2D紋理

glDisable(GL_LIGHTING);// 禁用光照

glDisable(GL_DEPTH_TEST);// 禁用深度測試

glEnable(GL_BLEND);// 啟用混合

// 確保不在任何一個光柵位置處進行重複繪製

glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);

// 黑色陰影

glColor4f(0.0f, 0.0f, 0.0f, blend);

// 以陰影進行投影

glMultMatrixf((float*)&shadow_matrix);// 投影矩陣

// 繪畫粒子暴風雪陰影

Draw_Snowstorm(&snowstorm, &Camera3D, &billboard);

glEnable(GL_TEXTURE_2D);// 禁用2D紋理

glEnable(GL_LIGHTING);// 禁用光照

glEnable(GL_DEPTH_TEST);// 禁用深度測試

glDisable(GL_BLEND);// 啟用混合

glPopAttrib();

glPopMatrix();

// 禁用模板測試

glDisable(GL_STENCIL_TEST);

// 繪畫粒子暴風雪

Draw_Snowstorm();

 

OpenGL之鏡像

OpenGL鏡像

『鏡像』並非是真實世界中由光粒子所產生.而式通過模擬『鏡像』技術.工作原理如下:

  1. 禁用顏色和深度緩存
  2. 啟用『模板緩存』後繪畫地板,對『模板緩存』進行寫操作
  3. 然後反轉『平面軸』渲染3D多邊形物體『鏡像』.
  4. 在3D空間中渲染平面
  5. 在平面的正面渲染3D多邊形物體

『鏡像』演示程式下載:

『鏡像』示例代碼:

// 禁用對所有的然色分量的修改

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

// 禁用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為只讀

glDepthMask(GL_FALSE);

// 啟用模板測試

glEnable(GL_STENCIL_TEST);

// 設置模板比較程序

glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);

// 設置模板ref操作,

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);//

// 繪畫地板,將模板環存中相應的地板像素設為1

Draw_Floor();

// 啟用所有顏色分量的修改

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

//  啟用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為可讀寫

glDepthMask(GL_TRUE);

// 設置模板比較程序,只能在模板緩存中值為1的相應去區域繪製

glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);

// 設置模板操作

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);// 保持當前值

// 繪製”鏡像”

glPushMatrix();

// 反轉

glScalef(1.0f, -1.0f, 1.0f);

//繪畫正方體

Draw_Cube();

//結束鏡像繪畫

glPopMatrix();

// 繪畫地板

Draw_Floor();

// 禁用模板測試

glDisable(GL_STENCIL_TEST);

//繪畫正方體

Draw_Cube();

OpenGL之迷霧

OpenGL之迷霧

『迷霧』通過模糊遠端之物,而近端清晰.給3D世界帶來霧感.而且可以減小進場境中多邊形數量,從而提高渲染速度.演示程式按『+/-』鍵加減『迷霧』密度.下載『迷霧

OpenGL直接提供『迷霧』支持:

1.首先需要啟用『迷霧』

glEnable(GL_FOG);

2.將像素與『迷霧』顏色進行混合而實現,其混合因子的設定跟據其視點距離, 『迷霧密度』和『迷霧模式』,設定『迷霧』可通過以下函式:

void glFogf(GLenum pname,GLfloat param);

void glFogfv(GLenum pname,const GLfloat*params);

void glFogi(GLenum pname,GLint param);

void glFogiv(GLenum pname,const GLint*params);

Pname(參數名) Params(參數值)
GL_FOG_MODE 『迷霧』混合方程式,默認為GL_EXP

GL_LINEAR:factor=(end-z)/(end-start)

GL_EXP:factor=e(-density*depth)

GL_EXP2: factor=e(-density*depth)* (-density*depth)

GL_FOG_DENSITY 迷霧密度默認為1.0f
GL_FOG_START 迷霧離視點近端默認為0
GL_FOG_END 迷霧離視點遠端默認為1
GL_FOG_INDEX 迷霧顏色索引值默認為0
GL_FOG_COLOR 迷霧顏色默認為黑色(0,0,0,0).

啟用迷霧函是代碼

void Enable_Fog()

{

float fogColor[] = { 0.5, 0.5, 0.5, 1.0 };// 灰色

glEnable(GL_FOG);// 啟用霧

glFogfv(GL_FOG_COLOR, fogColor);// 霧色

glFogf(GL_FOG_MODE, GL_EXP2);// 顏色模式

glFogf(GL_FOG_START, 200);// 霧近端範圍

glFogf(GL_FOG_END, 1000);// 霧遠端範圍

glFogf(GL_FOG_DENSITY, 0.06f);// 密度

}

 

OpenGL之粒子系統

OpenGL之粒子系統

『粒子系統』由多個『粒子』,每個『粒子』都有其獨立『屬性』如『尺寸』『顏色』『速度』,每個『粒子』都可獨立運作.當大量『粒子』同時運作便可產生其特的3D效果.『飄雪』『流星』『爆炸』『飛機引擎火焰』.

『飄雪』演示程式在天空落雪,時用『廣告牌』技術另廣告牌總是對這鏡頭.下載『飄雪

粒子系統大約分為

1.初此化粒子系統

2.生成粒子

3.更新粒子系統

4.渲染粒子系統

基本『粒子』屬性 簡介
位置 3D空間中的座標
速度 基本上粒子都會移動,你需要方向向量再乘以時間而求得速度
尺寸 粒子的尺寸可能變化
重量 外力對粒子影響
顏色 為粒子設定渲染顏色
形態 常見『點』『直線』『紋理四邊形』
宿主 粒子擁有者
生命週期 粒子存活時間

定義粒子結構

typedef struct PARTICLE_TYP {

VECTOR3D pos;// 位置

VECTOR3D velocity;// 速度與方向

VECTOR3D acceleration;// 加速度

float energy;// 生命週期(秒)

float size;// 尺寸

float weight;// 重量

float color[4];// 顏色

}PARTICLE,*PARTICLE_PTR;

『粒子系統』屬性 簡介
粒子隊列 要管理的所有粒子
數量 粒子量
位置 活動範圍中心位置
範圍 粒子活動範圍
屬性 常見『點』『直線』『紋理四邊形』
紋理 把紋理存儲在『粒子系統』

Particle(粒子系統)

typedef struct PARTICLE_SYSTEM_TYP{

int          attr;// 屬性

PARTICLE_PTR array;// 數組

int          count;// 實制個數

VECTOR3D     origin;// 原點

VECTOR3D     velocity;// 速度與方向

VECTOR3D     velocity_variation;// 速度變量

float        energy;// 生命週期(秒)

//有效範圍

float        height;// 高

float        width; // 寬

float        depth; // 深

float        size;// 尺寸

TEXTURE_PTR  texture;// 紋理

}PARTICLE_SYSTEM, *PARTICLE_SYSTEM_PTR;

1.初此化粒子系統

bool Init_Particle(PARTICLE_SYSTEM_PTR system, int count,

VECTOR3D_PTR origin, VECTOR3D_PTR velocity, VECTOR3D_PTR variation,

float height, float width, float depth,float size,float energy,

TEXTURE_PTR texture)// 紋理

{

if (system == NULL)

return false;

memset(system,0,sizeof(PARTICLE_SYSTEM));// 清空

system->count = count; // 粒子數量

system->array = (PARTICLE_PTR)malloc(sizeof(PARTICLE)*count);// 分配空間

system->texture = texture;// 紋理

system->size    = size;// 尺寸

system->height  = height;// 高

system->width   = width;// 寬

system->depth   = depth;// 深

Copy_VECTOR3D(&system->origin, origin);// 原點

Copy_VECTOR3D(&system->velocity, velocity);//  速度與方向

Copy_VECTOR3D(&system->velocity_variation, variation);//  速度變量

PARTICLE_PTR particle;// 粒子

for (int i = 0; i < count; ++i)

{

particle = &system->array[i];// 粒子

Buid_Particle(system, particle);

}

return true;

}

2.生成粒子

void Buid_Particle(PARTICLE_SYSTEM_PTR  system, PARTICLE_PTR  particle)

{

particle->energy = system->energy;// 生命週期(秒)

particle->size   = system->size;// 尺寸

// 粒子位置

particle->pos.x = system->origin.x + FRAND_RANGE1() * (system->width /2.0f);// x

particle->pos.y = system->origin.y + FRAND_RANGE1() * (system->height/2.0f);// y

particle->pos.z = system->origin.z + FRAND_RANGE1() * (system->depth /2.0f);// z

// 粒子速度

particle->velocity.x = system->velocity.x  ;

particle->velocity.y = system->velocity.y  ;

particle->velocity.z = system->velocity.z  ;

}

3.更新粒子系統

void Update_Particle(PARTICLE_SYSTEM_PTR  system,float time)

{

PARTICLE_PTR         particle;// 粒子

for (int i = 0; i < system->count; ++i)

{

particle = &system->array[i];// 粒子

VECTOR3D vector;

Scale_VECTOR3D(&vector, &particle->velocity, time);

Add_VECTOR3D(&particle->pos, &particle->pos, &vector);// 移動粒子

// 判斷有無超出返圍

particle->energy = particle->energy – time;// 計算生命週期

if (particle->energy < 0 && particle->pos.y < 0)// 生命週期

Buid_Particle(system, particle);

}

}

4.繪畫貝粒子系統(紋理)

bool Draw_Texture_Particle(PARTICLE_SYSTEM_PTR system,

CAMERA3D_PTR camera, BILLBOARD_PTR billboard)

{

glPushMatrix();// 壓入矩陣

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_LIGHTING);// 禁用光照

glDisable(GL_LIGHT0);// 禁用0號燈光

glEnable(GL_DEPTH_TEST);// 啟用深度測試

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glEnable(GL_ALPHA_TEST);// 啟用透面測試

glAlphaFunc(GL_GREATER, 0);// 透面色

PARTICLE_PTR         particle;// 粒子

for (int i = 0; i < system->count; ++i)

{

particle = &system->array[i];// 粒子

//檢查3D球體是否為於視錐體內

if (Compute_Sphere_In_Frustum(&camera->frustum, &particle->pos, particle->size) == true)

{   // 繪畫Billboard廣告牌

Draw_Billboard(billboard, &particle->pos, particle->size);

}

}

glPopAttrib();// 彈出屬性

glPopMatrix();// 彈出矩陣

return true;

}

 

OpenGL之廣告牌

OpenGL之廣告牌

當我地在游中渲染大量3D模型時,如果3D模型離視口很遠時,在屏幕上繪畫可能只是幾個像素,其中解卻方法是3D模型轉為2D圖像,然後作為紋理映射到四邊形.當視口靠近時再切換為3D模型.廣告牌作用是確保2D圖像此終面向視口.演示程式中按前後鍵移動相機,按左右鍵旋轉相機,仙人掌此終面向視口:下載

1.獲取當前『模型視圖矩陣』

typedef struct MATRIX4X4_TYP{

float M[4][4]; //以數組形式儲存

} MATRIX4X4, *MATRIX4X4_PTR;

glGetFloatv(GL_MODELVIEW_MATRIX, (float*)matrix.M);

2.提取視口右則向量

VECTOR3D  right;

Init_VECTOR3D(&right, matrix.M[0][0], matrix.M[1][0], matrix.M[2][0]);

3.提取視口右則向量

VECTOR3D  up;

Init_VECTOR3D(&up,matrix.M[0][1],matrix.M[1][1], matrix.M[2][1]);

4.移動廣告牌(四邊形)中心位置

glPushMatrix();

glTranslatef(position->x, position->y, position->z);

5.準備繪畫廣告牌(四邊形)

glBegin(GL_QUADS);

6.法線朝向

glNormal3f(0.0f, 0.0f, 1.0f);

7.重新計算廣告牌(四邊形)頂點位置,以實現廣告牌法線此終朝向視口

VECTOR3D  Vertex;

glTexCoord2f(0.0f, 0.0f); glVertex3fv(((right + up) * -size).M); // 左上角

glTexCoord2f(1.0f, 0.0f); glVertex3fv(((right – up) * size).M); // 右下角

glTexCoord2f(1.0f, 1.0f); glVertex3fv(((right + up) * size).M); // 右上角

glTexCoord2f(0.0f, 1.0f); glVertex3fv(((up – right) * size).M); // 左上角

8.完成繪畫廣告牌(四邊形)

glEnd();

glPopMatrix();// 彈出矩陣

OpenGL之NURBS(非均勻有理B樣條)

OpenGL之NURBS(非均勻有理B樣條)

貝賽爾(Bezier)曲線』(下面簡稱為『曲線』)跟據其『控制點』數目分類.擁有三個控制點『曲線』稱為『二次曲線』. 擁有四個控制點『曲線』稱為『三次曲線』.

隨著『曲線』控制點數目增加.曲線的平滑度可能會被大量控制點拉扯而被破壞.

所以引入NURBS(非均勻有理B樣條).『B樣條』其實就是『曲線』,只不過『B樣條』分為多個『曲線段』. 每個『曲線段』擁有四個控制點.將整條『曲線』分為若干『曲線段』,等於將多個『三次曲線』合併為『B樣條』.

『B樣條』有四個『控制點』,而每個『控制點』擁有兩個『節點』.其值可以取u和v域範圍任意值.『節點』影響『控制點』對『曲線』影響程度.這也是Bezier與NURBS區別之處.NURBS演示程式下載:

1.生成NURBS對象

GLUnurbsObj * object = gluNewNurbsRenderer();

2.定義八個『節點』

float knots[8] = { 0,0, 0,0, 1,1, 1,1 };

3.生成NURBS四個『控制點』

float nurbs[4][4][3] ;

4.設置NURBS曲面控制點,形成坨峰形

for (int u = 0; u < 4; ++u){

for (int v = 0; v < 4; ++v){

nurbs[u][v][0] = 3.0f*((float)u – 1.5f);

nurbs[u][v][1] = 2.0f*((float)v – 1.5f);

if ((u == 1 || u == 2) && (v == 1 || v == 2))

nurbs[u][v][2] = 3.0f;

else

nurbs[u][v][2] = -1.0f;

}

}

5.設置多邊形最大長度

gluNurbsProperty(object,GLU_SAMPLING_TOLERANCE,30.0f);

6.以多邊形繪製曲面

gluNurbsProperty(object,GLU_DISPLAY_MODE,GLU_FILL);

gluNurbsProperty()用於設置採樣以及如何繪畫NURBS

void gluNurbsProperty(GLUnurbs *nobj,GLenum property,GLfloat value);

參數 數值 簡介
nobj GLUnurbsObj * object; NURBS對象
property GLU_SAMPLING_TOLERANCE: 設置多邊形最大長度
GLU_DISPLAY_MODE: 多邊形渲染模式GLU_FILL:多變形實體填充
value   依property而定

7.設置NURBS曲面

gluBeginSurface(object);

8.準備繪製NURBS曲面

gluNurbsSurface(object,8,knots,8, knots,43,3,(GLfloat)&nurbs[0][0][0],4,4, GL_MAP2_VERTEX_3);

函式定義

void gluNurbsSurface(GLUnurbsnobj,GLint sknot_count,floatsknot,GLint  tknot_count,GLfloattknot,GLint s_stride,GLint t_stride,GLfloatctlarray,GLint sorder,GLint torder,GLenum type);

參數 簡介
nobj NURBS對象
sknot_count u節點個數
sknot u節點
tknot_count v節點個數
tknot v節點
s_stride u控制點距離
t_stride v控制點距離
ctlarray 指向控制點
sorder u方向控制點個數
torder v方向控制點個數
type 曲面類型

9.完成渲染

gluEndSurface(object);

 

 

OpenGL之貝賽爾(Bezier)曲面

OpenGL貝賽爾(Bezier)曲面

貝賽爾(Bezier)『曲面』與『曲線』相比不同之處在於多咗v空間域.曲面函式s(u,v).曲面演示程式按空格鍵在線框、填充、紋理模式中切換:下載

1.對曲面進行柵格映射

void glMap2f(GLenum target,GLfloat u1,GLfloat u2,GLint ustride,GLint uorder,GLfloat v1,GLfloat v2,GLint vstride,GLint vorder,const GLfloat *points);

函式示例:

glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3,u,  0,1, 3v,3,  (GLfloat)bezier->control_array);

參數 簡介
target 控制點類型
u1,u2 u空間域參數的定義域(u1~u2)
ustride u空間域頂點跨度
uorder u空間域頂點個數
v1, v2 v空間域參數的定義域(u1~u2)
vstride v空間域頂點跨度
vorder v空間域頂點個數
points 定點數組

 

控制點類型(target) 簡介
GL_MAP1_VERTEX_3 頂點座標(x,y,z)
GL_MAP1_VERTEX_4 頂點座標(x,y,z,w)
GL_MAP1_INDEX 顏色索引
GL_MAP1_COLOR_4 顏色值(RGBA)
GL_MAP1_NORMAL 法線座標
GL_MAP1_TEXTURE_COORD_1 紋理座標(s)
GL_MAP1_TEXTURE_COORD_2 紋理座標(s,t)
GL_MAP1_TEXTURE_COORD_3 紋理座標(s,t,r)
GL_MAP1_TEXTURE_COORD_4 紋理座標(s,t,r,g)

2.啟用貝賽爾(Bezier)曲面

glEnable(GL_MAP2_VERTEX_3);

3.定義定義均勻的柵格,並設置間隔個數

void glMapGrid2f(GLint un,GLfloat u1,GLfloat u2,GLint vn,GLfloat v1,GLfloat v2);

函式示例:

glMapGrid2f(10,0, 10, 10, 0, 10);

參數 簡介
un u域[u1~u2]間隔個數,必須為正
u1 u1=0
u2 u2=un
vn v域[v1~v2]間隔個數,必須為正
v1 v1=0
v2 v2=vn
  1. 根據柵格設置繪製曲面

void glEvalMesh2(GLenum mode,GLint i1,GLint i2,GLint j1,GLint j2);

函式示例:

glEvalMesh2(GL_FILL, 0, 10, 0, 10);

參數 簡介
mode 繪畫模式

GL_POINT:點

GL_LINE:直線

GL_FILL:填充

i1,i2 u空間域的範圍
j1,j2 v空間域的範圍

5.為曲面生成法線用於光照

glEnable(GL_AUTO_NORMAL);

6.對曲面進行紋理映射

glMap2f(GL_MAP2_TEXTURE_COORD_2, 0,1,2, 2,0,1, 4,2,(GLfloat*)bezier->texture_coord);

對曲面進行紋理映射glMap2f() 簡介
target 紋理座標(s,t):GL_MAP2_TEXTURE_COORD_2(睇上表)
u1,u2 s空間域:[0~1]
ustride s紋理座標頂點跨度:2
uorder s空間域頂點個數:2
v1,v2 t空間域:[0~1]
vstride t紋理座標頂點跨度:4
vorder t空間域頂點個數:2
points 紋理座標:{0.0, 0.0}, {0.0, 1.0}, {1.0, 0.0}, {1.0, 1.0}

4.啟用曲面紋理座標

glEnable(GL_MAP2_TEXTURE_COORD_2);

OpenGL之貝賽爾(Bezier)曲線

OpenGL貝賽爾(Bezier)曲線

OpenGL支持貝賽爾(Bezier)曲線繪畫,貝賽爾曲線由起點,終點和控制點組成,並且具有平滑的運動軌跡.控制點用於定義曲線的形狀,像磁石將曲線向控制點的位置吸引.上圖的演示程式通過滑鼠點選屏幕放置『控制點』繪畫貝賽爾(Bezier)曲線.ESC鍵清空『控制點』:下載

1.設置貝賽爾(Bezier)曲線

void glMap1f(GLenum target,GLfloat u1,GLfloat u2,GLint stride,GLint order,const GLfloat *points);

參數 簡介
target 控制點類型
u1, u2 u參數的定義域(0~1)
stride 頂點跨度
order 曲線頂點個數(由起點,終點和控制點組成)
points 指向曲線頂點數組由(起點,終點和控制點組成)

 

控制點類型(target) 簡介
GL_MAP1_VERTEX_3 頂點座標(x,y,z)
GL_MAP1_VERTEX_4 頂點座標(x,y,z,w)
GL_MAP1_INDEX 顏色索引
GL_MAP1_COLOR_4 顏色值(RGBA)
GL_MAP1_NORMAL 法線座標
GL_MAP1_TEXTURE_COORD_1 紋理座標(s)
GL_MAP1_TEXTURE_COORD_2 紋理座標(s,t)
GL_MAP1_TEXTURE_COORD_3 紋理座標(s,t,r)
GL_MAP1_TEXTURE_COORD_4 紋理座標(s,t,r,g)

2.啟用控制點

glEnable(GL_MAP1_VERTEX_3);

3.現在可以繪畫曲線,100條直線繪畫貝賽爾(Bezier)曲線,以均等間隔繪畫.

glBegin(GL_LINE_STRIP);

for (i = 0; i <= 100; ++i)

glEvalCoord1f((float)i / 100.0f);

glEnd();

上面的代碼可以簡化為

glMapGrid1d(100, 0.0f, 1.0f);// 定義定義均勻的間格

glEvalMesh1(GL_LINE,0, 100);// 繪製曲線

4.完成繪畫後需要繪畫控制點

glPointSize(5);

glBegin(GL_POINTS);

for (i = 0; i < bezier->countol_count; ++i)

           glVertex3fv(bezier->control_array[i].M);

glEnd();