Android Studio之Error running ‘app’ No target device found

Android Studio之Error running ‘app’ No target device found

剛啟動Android Studio運行app進行調試『Run->Debug』時彈出:

『Error running ‘app’ No target device found』運行app『應用』錯誤穩唔到目標設備.即穩唔到手機.之前已安裝『ADB 驅動』.肯定是ADB服務未啟動所以先未穩到部手機.

啟用ADB服務可按

『Run->Attach debuger to Android process』

或在CMD輸入

adb start-server

 

Android遊戲之全屏

Android遊戲之全屏

自『街機遊戲』到『電腦遊戲』所有遊戲都以全屏形態出現.所以Android遊戲也應全屏.在super.onCreate()調用之前設定全屏

去除APP標題欄

this.requestWindowFeature(Window.FEATURE_NO_TITLE);

去除系通通知欄

Window window = this.getWindow(); window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

創建Activity.需要

super.onCreate(savedInstanceState);

把圖形界面『佈局』填充給Activity

setContentView(R.layout.main);

 

FPS第一人稱視角射擊遊戲

是時候將之前所學OpenGL/DirectX知識粘合成一完整遊戲.最簡單是『第一人稱視角射擊遊戲』FPS(First-person Shooter). 通過控制『準星』描准『怪物』按滑鼠左鍵發射『飛彈』.若擊中便會『爆炸』並擊殺『怪物』,其實它會在另地在生.若擊中地面則會『爆炸』.按鍵盤上下鍵在山地中前後移動.若近距離接近『怪物』它會另轉面自動走開(AI部分). 所有『怪物』與『飛彈』都是MD2模型.按ESC鍵退出遊戲.下載FPS:

下面所需要文章與代碼:

  1. AI之行走
  2. 準星
  3. 粒子爆炸特效
  4. 邊界球
  5. MD2文檔讀取與解析
  6. WAV音檔分釋與讀取
  7. DirectSound3D
  8. DirectInput之查詢滑鼠
  9. DirectInput之查詢鍵盤
  10. 粒子系統
  11. 廣告牌
  12. 紋理地形
  13. 天幕
  14. 紋理映射

OpenGL之準星

OpenGL之準星
準星紋理

射擊遊戲在屏幕中畫有『準星』用於射擊怪獸.因為『準星』固定在屏幕中心.當你移動滑鼠時.相機視角也隨之改變.而當你要移動射擊位置可通過按鍵盤上下鍵

移動滑鼠 響應 角度
左移 相機視角繞Y軸左轉 if (y >= 360.0f) y = 0.0f;
右移 相機視角繞Y軸右轉 if (y <= -360.0f) y = 0.0f;
上移 相機視角繞Z軸右轉 if (z > 60.0f) z = 60.0f;
下移 相機視角繞Z軸左轉 if (z < -60.0f) z = -60.0f;

 

鍵盤鍵 響應 位置與角度
上鍵 位置前移 x = x – (speed * cos(DEG_TO_RAD(y)));

z = z – (speed * sin(DEG_TO_RAD(y)));

下鍵 位置後移
左鍵 相機視角繞Y軸左轉 if (y >= 360.0f) y = 0.0f;
右鍵 相機視角繞Y軸右轉 if (y <= -360.0f) y = 0.0f;

更新相機位置與角度

1.旋轉角度 deltaTime為時間間隔

rot.x = rot.x + dx * 60 *deltaTime;

rot.y = rot.y + dy * 60 *deltaTime;

rot.z = rot.z + dz * 60 *deltaTime;

2.限制視口角度否則會上下搖恍

if ( rot.y >= 360.0f || rot.y <= -360.0f)

player->rot.y = 0.0f;

if (player->rot.z > 60.0f)

player->rot.z = 60.0f;

if (player->rot.z < -60.0f)

player->rot.z = -60.0f;

3.角度轉弧度

float radian = 3.141592654f * player->rot.y / 180.0f;

計算移動速度

4.float speed = dx * 3 *deltaTime;

5.計算位置

pos.x = (float)(pos.x – (speed * cos(radian)));

pos.z = (float)(pos.z – (speed * sin(radian)));

radian = 3.141592654f * rot.z / 180.0f; // 弧度

pos.y = (float)(pos.y – (speed * sin(radian)));

6.UVN相機模型

LookAt_Camera3D();

最後製作準星紋理

  1. 繪製白色準星
  2. 背景色填充為黑色
  3. 模色設為『索引色』256色
  4. 設定透面色為黑色glAlphaFunc(GL_GREATER, 0);

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;

 

 

MD2文檔讀取與解析

MD2文檔讀取與解析
MD2文檔讀取與解析
MD2文檔讀取與解析

.MD2文檔由美國id Software為其QuakeII開發3D模型.雖然它無骨架但它讀取簡單,而最重要是可以通過Internet穩到大量MD2文檔.根據模型規範.MD2格式最多含198動畫幀編號為0~197. 動畫幀將用於組成『步行』『功擊』『站立』『死亡』等動畫.MD2文檔 通常有下面幾個文檔組成:

文檔 簡述
TRIS.MD2 角色3D模型
WEAPON.MD2 武器3D模型
TRIS.PCX 角色皮膚也就是紋理圖檔,通常為8Bit『256色』長寬為256*256保存為.PCX格式
WEAPON.PCX 武器皮膚

 

MD2格式 簡述
Head 文檔頭部格式3D模型描述
Data 3D數據

 

MD2文檔頭部格式 MD2_HEADER 簡述
int id; MD2文檔標記’IDP2′,用於判斷是否MD2文檔
int version; 版本號總為8
int skin_width 皮膚紋理圖寬度.此值相對於紋理座標textcoord,通常為256
int skin_height; 皮膚紋理圖高度.此值相對於紋理座標textcoord,通常為256
int frame_size; 每幀字節總量
int skin_num; 皮膚紋理總量,忽略.
int vertex_num; 單幀頂點量.每幀字節量均相同.有些教科書搞錯左此值.
int textcoord_num; 紋理座標總量
int polygon_num; 多邊形總量
int command_num; OpenGL命令總量,忽略
int frame_num; 幀總量id softwarek規定為198幀編號為0~197.有些模型可能多於此值
int skin_offset; 皮膚pcx偏移量每個64字節.因包含QuakeII專用路徑.忽略.
int textcoord_offset; 紋理座標偏移量
int polygon_offset; 多邊形偏移量
int frame_offset; 幀偏移量
int command_offset; 命令偏移量忽略
int end_offset; 結尾偏移量.相當於文檔長度

MD2紋理座標通過(data + textcoord_offset)得到,結構如下:

typedef struct MD2_TEXTCOORD_TYP {

WORD u, v;

}MD2_TEXTCOORD,*MD2_TEXTCOORD_PTR;

 

MD2多邊形通過(data + polygon_offset)得到,保存頂點與紋理索引.結構如下:

typedef struct MD2_POLYGON_TYP {

WORD vertex_index[3];// 頂點索引

WORD textcoord_index[3];// 紋理座標索引

}MD2_POLYGON,*MD2_POLYGON_PTR;

 

MD2關鍵幀通過(data + frame_offset)得到,幀頭包含對頂點進行縮放與平移因子.頂點數組長度由vertex_num確定.結構如下:

typedef struct MD2_FRAME_TYP{

float scale[3];// 縮放

float translate[3];// 平移

char  name[16];// 動畫幀ASCII名

MD2_POINT list[1];// 頂點數組

}MD2_FRAME,*MD2_FRAME_PTR;

 

MD2頂點包含於關鍵幀裡,每個頂點由『xyz座標』與『法線索引』組成.『法線索引表』你需要自已構建.法線將在運行時計算所以忽略.結構如下:

typedef struct MD2_POINT_TYP {

BYTE v[3];// xyz頂點

BYTE noraml_index;// 頂點法線索引,此值忽略

}MD2_POINT,*MD2_POINT_PTR;

 

MD2動畫,用於控制模型動作速度,以秒為單位.結構如下:

typedef struct MD2_ANIMATION_TYP{

int   start;  // 起始幀索引

int   end;    // 結束幀索引

float irate;   // 插幀頻率(0.0f~1.0f),1.0f表示不插幀

float speed;   // 用於控制動畫播放時間,一般設為0~10秒

} MD2_ANIMATION, *MD2_ANIMATION_PTR;

動畫索引 動作名 幀索引 插幀頻率(幀) 播放時間(秒)
0 STANDING_IDLE站立 0-39 0.5f 4
1 RUN奔跑 40-45 0.5f 4
2 ATTACK開火/攻擊 46-53 0.5f 2
3 PAIN1 54-57 0.5f 4
4 PAIN2 58-61 0.5f 4
5 PAIN3 62-65 0.5f 4
6 JUMP跳躍 66-71 0.5f 5
7 FLIP手勢 72-83 0.5f 3
8 SALUTE  敬禮 84-94 0.5f 5
9 TAUNT 嘲笑 95-111 0.5f 5
10 WAVE 揮手致意 112-122 0.5f 5
11 POINT 指向它人 123-134 0.5f 5
12 CROUCH STAND   蹲伏-不動 135-153 0.5f 5
13 CROUCH WALK    蹲伏-行走 154-159 0.5f 5
14 CROUCH ATTACK  蹲伏-開火 160-168 0.5f 5
15 CROUCH_PAIN    蹲伏-被擊打 169-172 0.5f 5
16 CROUCH_DEATH  蹲伏-死亡 173-177 0.25f 5
17 DEATH_BACK    死亡-後趴 178-183 0.25f 5
18 DEATH_FORWARD 死亡-前趴 184-189 0.25f 5
19 DEATH_SLOW   緩慢死亡 190-197 0.25f 5

現在需要MD2結構用於保存3D模型數據

MD2模型數據結構 簡述
MD2_ANIMATION animation_array[20] 動畫,MD2通常有20種不同動作
int animation_num; 動畫總量
int frame_num; 幀量id softwarek規定198幀
MD2_POLYGON_PTR polygon_array; 多邊形列表
int polygon_num; 多邊形總量
VECTOR3D_PTR  vertex_array; 頂點數組 長度=幀總量*單幀頂點量
int vertex_num; 單幀頂點量
VECTOR2D_PTR textcoord_array; 紋理座標數組,只有一組,由所有幀共享
int textcoord_num; 紋理座標總量
TEXTURE texture_array[8] 紋理skin
int texture_num; 皮膚紋理圖總量
float radius_avg; 平均半徑
float radius_max; 最大半徑

在遊戲中低級單位都一至如步兵.需要單獨保存位置、面方、動畫等狀態.需要一個新結構對MD2進行封裝.以共享數據節約空間.並且為讓動畫流暢需要對關鍵幀進行插值(插入其它幀).

3D模型結構定義(MODEL3D) 簡述
int state; 狀態, 死亡/存活
int attr; 屬性
int color; 沒有紋理時使用顏色 RGB(255,255,255)
VECTOR3D pos; 位置
VECTOR3D rot; 旋轉
int anim; 動畫索引
bool  loop; 循環播放
float speed; 動畫速度,(0.0f~1.0f)數值越小速度越慢,數值越大速度越快
float frame; 當前幀索引
float irate; 插幀頻率(0.0f~1.0f),1.0f表示不插幀
float count; 插幀計數器
bool complete; 動畫完成播放標記
VECTOR3D_PTR vertex_array; 單幀頂點量
VECTOR3D_PTR normal_array; 法線數組
TEXTURE_PTR texture; 指向紋理
MD2_PTR md2; 指向MD2模型

在載入數據之前還需要定義幾個頂點控制

#define MD2_INVERT_X       0x0001// 反轉X軸

#define MD2_INVERT_Y       0x0002// 反轉Y軸

#define MD2_INVERT_Z       0x0004// 反轉Z軸

#define MD2_SWAP_XY        0x0010// 交換XY軸

#define MD2_SWAP_YZ        0x0020// 交換YZ軸

#define MD2_SWAP_XZ        0x0040// 交換XZ軸

#define MD2_INVERT_WINDING_ORDER  0x0100 // 反轉環繞順序

基本結構已定義可以讀取數據並分釋:

bool Load_Data_MD2(MD2_PTR md2, PBYTE data, int size,float scale,DWORD flag ){

1.讀取MD2頭部,data為文檔數據

MD2_HEADER_PTR    header = (MD2_HEADER_PTR)data;

2.判斷是否MD2模型

if (header->id != ‘2PDI’) // MD2文檔標記

return false;

3.判斷版本號總為8

if (header->version != 8)

return false;

4.幀總量id softwarek規定198幀

md2->frame_num = header->frame_num;

5.皮膚紋理總量設為0

md2->texture_num   = 0;

4.多邊形總量

md2->polygon_num = header->polygon_num;

5.讀取幀頂點量

md2->vertex_num = header->vertex_num;

6.分配多邊形記憶體

md2->polygon_array = (MD2_POLYGON_PTR)malloc(md2->polygon_num*sizeof(MD2_POLYGON));

7.分配頂點記憶體

md2->vertex_array = (VECTOR3D_PTR)malloc(md2->frame_num * md2->vertex_num * sizeof(VECTOR3D));

8.分配紋理座標記憶體.以繪畫三角形進行排列

md2->textcoord_array = (VECTOR2D_PTR)malloc(md2->polygon_num * 3 * sizeof(VECTOR2D));

9.遍歷每一幀

for (int findex = 0; findex < md2->frame_num; ++findex){

MD2_FRAME_PTR frame;// 讀取幀

frame = (MD2_FRAME_PTR)(data+header->frame_offset + header->frame_size * findex);

10.遍歷每一頂點

for (int vindex = 0; vindex < md2->vertex_num; ++vindex){

VECTOR3D v ;

11.對頂點座標進行縮放和平移

v.x = frame->list[vindex].v[0] * frame->scale[0] + frame->translate[0];

v.y = frame->list[vindex].v[1] * frame->scale[1] + frame->translate[1];

v.z = frame->list[vindex].v[2] * frame->scale[2] + frame->translate[2];

12.跟據傳入參數進行縮放

if (scale != NULL)

v = v * scale;

13.反轉座標軸

if (flag & MD2_INVERT_X)

v.x = -v.x;

if (flag & MD2_INVERT_Y)

v.y = -v.y;

if (flag & MD2_INVERT_Z)

v.z = -v.z;

14.交換座標軸

float temp;

if (flag & MD2_SWAP_YZ)

SWAP(v.y, v.z, temp);

if (flag & MD2_SWAP_XZ)

SWAP(v.x, v.z, temp);

if (flag & MD2_SWAP_XY)

SWAP(v.x, v.y, temp);

15.將頂點插入列表中

md2->vertex_array[findex*md2->vertex_num + vindex] = v;

}

}

16.讀取紋理座標

MD2_TEXTCOORD_PTR textcoord = (MD2_TEXTCOORD_PTR)(data + header->textcoord_offset);

17.讀取多邊形

MD2_POLYGON_PTR polygon = (MD2_POLYGON_PTR)(data+header->polygon_offset);

18.遍歷多邊形

for (int pindex = 0; pindex<header->polygon_num; ++pindex){

if (flag & MD2_INVERT_WINDING_ORDER) {

19.反轉頂點環繞順序

md2->polygon_array[pindex].vertex_index[0] = polygon[pindex].vertex_index[2];

md2->polygon_array[pindex].vertex_index[1] = polygon[pindex].vertex_index[1];

md2->polygon_array[pindex].vertex_index[2] = polygon[pindex].vertex_index[0];

// 反轉紋理座標環繞順序

md2->polygon_array[pindex].textcoord_index[0] = polygon[pindex].textcoord_index[2];

md2->polygon_array[pindex].textcoord_index[1] = polygon[pindex].textcoord_index[1];

md2->polygon_array[pindex].textcoord_index[2] = polygon[pindex].textcoord_index[0];

}

else

{// 不改變頂點環繞順序

md2->polygon_array[pindex].vertex_index[0] = polygon[pindex].vertex_index[0];

md2->polygon_array[pindex].vertex_index[1] = polygon[pindex].vertex_index[1];

md2->polygon_array[pindex].vertex_index[2] = polygon[pindex].vertex_index[2];

// 不改變紋理座標環繞順序

md2->polygon_array[pindex].textcoord_index[0] = polygon[pindex].textcoord_index[0];

md2->polygon_array[pindex].textcoord_index[1] = polygon[pindex].textcoord_index[1];

md2->polygon_array[pindex].textcoord_index[2] = polygon[pindex].textcoord_index[2];

}

20.以繪畫順序讀取三角形頂點紋理座標,無需在提取幀是在生成.

int tindex0 = md2->polygon_array[pindex].textcoord_index[0];

int tindex1 = md2->polygon_array[pindex].textcoord_index[1];

int tindex2 = md2->polygon_array[pindex].textcoord_index[2];

21.讀取紋理座標轉換為0.0f~1.0f

md2->textcoord_array[pindex*3+0].u = (float)textcoord[tindex0].u / (float)header->skin_width;

md2->textcoord_array[pindex*3+0].v = (float)textcoord[tindex0].v / (float)header->skin_height;

md2->textcoord_array[pindex*3+1].u = (float)textcoord[tindex1].u / (float)header->skin_width;

md2->textcoord_array[pindex*3+1].v = (float)textcoord[tindex1].v / (float)header->skin_height;

md2->textcoord_array[pindex*3+2].u = (float)textcoord[tindex2].u / (float)header->skin_width;

md2->textcoord_array[pindex*3+2].v = (float)textcoord[tindex2].v / (float)header->skin_height;

}

22.遍歷每個頂點計算模型半徑

md2->radius_avg = 0;// 平均半徑

md2->radius_max = 0;// 最大半徑

for (int vindex = 0; vindex < header->vertex_num; ++vindex){

float dist;

dist = (float)sqrt(md2->vertex_array[vindex].x * md2->vertex_array[vindex].x +

md2->vertex_array[vindex].y * md2->vertex_array[vindex].y +

md2->vertex_array[vindex].z * md2->vertex_array[vindex].z);

md2->radius_avg = md2->radius_avg + dist;// 累加半徑

if (dist > md2->radius_max)

md2->radius_max = dist;// 最大半徑

}

23.計算平均半徑

md2->radius_avg = md2->radius_avg / header->vertex_num;//

23.載入默認動畫序列. md2_animations[]數組跟據上面動畫列表定義

for (int aindex = 0; aindex < (sizeof(md2_animations) / sizeof(MD2_ANIMATION)); ++aindex)

md2->animation_array[aindex] = md2_animations[aindex];

return true;

}

 

紋理需要另外載入,紋理高寬需為2x2x.如256256、128*128

bool Load_Texture_MD2(MD2_PTR md2, const char * path){

int index = md2->texture_num; // 索引

++md2->texture_num;// 紋理數量

Load_File_Texture(&md2->texture_array[index], path);// 載入

Bind_Image_Texture(&md2->texture_array[index]);// 綁定

return true;

}

 

3D模型動畫平滑運動需要幀插值.『當前幀』frame_curr帶有小數在0~127之間.按權重插值公式如下:

vi=v0(1-value) + v1value

逐幀提取函式:

void Extract_Frame_MD2(MD2_PTR md2,VECTOR3D_PTR  vertex_array,VECTOR3D_PTR normal_array,float frame_curr){

MD2_POLYGON_PTR polyon;

int vindex;

1.判斷幀是否插值得到

float ivalue = frame_curr – (int)frame_curr;

if (ivalue == 0.0f) {//判斷是否為整數

2.若為整數則直接讀取

int frame = (int)frame_curr;

if (frame >= md2->frame_num)

frame = md2->frame_num – 1;

3.計算當前幀索引頂點偏移

int base = md2->vertex_num * frame;

4.遍歷每個多邊形

for (int pindex = 0, index = 0; pindex < md2->polygon_num; ++pindex, index= index+3){

5.讀取多邊形每個頂點

polyon = &md2->polygon_array[pindex];

vindex = polyon->vertex_index[0];

vertex_array[index+0]    = md2->vertex_array[base + vindex];

vindex = polyon->vertex_index[1];

vertex_array[index + 1] = md2->vertex_array[base + vindex];

vindex = polyon->vertex_index[2];

vertex_array[index + 2] = md2->vertex_array[base + vindex];

6.計算三角形法線

Normal_VECTOR3D(&normal_array[pindex],&vertex_array[index + 0],&vertex_array[index + 1],&vertex_array[index + 2]);

}

}

else{

2.若有小數數則進行幀插值,讓動畫平滑

int frame0 = (int)frame_curr;

int frame1 = (int)frame_curr + 1;

if (frame0 >= md2->frame_num)

frame0 = md2->frame_num – 1;

if (frame1 >= md2->frame_num)

frame1 = md2->frame_num – 1;

int base0 = md2->vertex_num * frame0;

int base1 = md2->vertex_num * frame1;

3.偏曆三角形在兩幀之間插值計算頂點

for (int pindex = 0, index = 0; pindex < md2->polygon_num; ++pindex, index = index + 3){

4.讀取兩個多邊形頂點並行權重插值

polyon = &md2->polygon_array[pindex];

vindex = polyon->vertex_index[0];

vertex_array[index+0] = md2->vertex_array[base0 + vindex] * (1 – ivalue) +md2->vertex_array[base1 + vindex] * (ivalue);

vindex = polyon->vertex_index[1];

vertex_array[index + 1] = md2->vertex_array[base0 + vindex] * (1 – ivalue) +md2->vertex_array[base1 + vindex] * (ivalue);

vindex = polyon->vertex_index[2];

vertex_array[index + 2] = md2->vertex_array[base0 + vindex] * (1 – ivalue) + md2->vertex_array[base1 + vindex] * (ivalue);

5.計算三角形法線

Normal_VECTOR3D(&normal_array[pindex],&vertex_array[index + 0], &vertex_array[index + 1], &vertex_array[index + 2]);

}

}

}

 

載入MD2模型.讓MODEL3D

void Load_MD2_MODEL3D(MODEL3D_PTR model3D,MD2_PTR md2,int texture_index){

1.清零

memset(model3D, 0, sizeof(MODEL3D));

2.指向md2模型

model3D->md2 = md2;

3.分配每幀多邊形頂點記憶體

model3D->vertex_array = (VECTOR3D_PTR)malloc(md2->polygon_num * 3*sizeof(VECTOR3D));

4.分配每幀多邊形法線記憶體

model3D->normal_array = (VECTOR3D_PTR)malloc(md2->polygon_num * 3 * sizeof(VECTOR3D));

5.指向紋理

if (texture_index < md2->texture_num){

texture_index = md2->texture_num – 1;

model3D->texture = &md2->texture_array[texture_index];

}

}

 

設置模型動畫MD2有20個不同動作

void Set_Animation_MODEL3D(MODEL3D_PTR model3D,int anim,bool loop){

1.讀取MD2模型

MD2_PTR  md2= model3D->md2;

2.設定當前動畫索引

model3D->anim = anim;

3.動畫是否循環播放

model3D->loop = loop;

4.動畫播放標記設為未完成

model3D->complete = false;

5.動畫

MD2_ANIMATION_PTR  animation = md2->animation_array;

6.插幀頻率(0.0f~1.0f),1.0f表示不插幀

model3D->irate = animation[anim].irate;

7.當前幀

model3D->frame = animation[anim].start;

8.速度

model3D->speed = animation[anim].speed;

9.插幀計數器

model3D->count = 0;

10.提取動畫幀

Extract_Frame_MD2(model3D->md2,model3D->vertex_array,  model3D->normal_array,model3D->frame);

}

 

在遊戲引擎中你需要更新動畫, time為時間間隔通過Get_Counter_FPS(&fps);獲得

void Update_MODEL3D(MODEL3D_PTR model3D,float time){

MD2_PTR md2 = model3D->md2;

MD2_ANIMATION_PTR animation = md2->animation_array;

1.計算動畫有幾多幀

int   frame_num = animation->end – animation->start + 1;

2.計算每幀速度

float frame_speed = (float)frame_num / model3D->speed ;

3.當前幀帶小數以進行插值

model3D->frame = model3D->frame + frame_speed * time;

4.幀計數器控制插值

model3D->count = model3D->count + frame_speed * time;

5.判斷動畫是否播放完畢

if (model3D->frame > animation[model3D->anim].end)   {

if (model3D->loop == MD2_LOOP)  {// 循環播放動畫

model3D->count = 0;

model3D->frame = animation[model3D->anim].start;// 啟動幀

}

else{// 單次播放動畫

model3D->frame = animation[model3D->anim].end;// 結束幀

model3D->complete = true;// 以完成動作

}

}

  1. irate為插幀頻率(0.0f~1.0f), 1.0f表示不插幀

if (model3D->count >= model3D->irate || model3D->count == 0){

model3D->count = 0;//清零

8.提取動畫幀

Extract_Frame_MD2(model3D->md2, model3D->vertex_array,model3D->normal_array,model3D->frame);

}

}

 

每幀都要對3D模型進行渲染.這裡使用頂點數組進行渲染.當前你可以逐個三角形進行渲染但是會較慢.

void Render_MODEL3D(MODEL3D_PTR model3D){

1.當前矩陣堆棧壓棧

glPushMatrix();

  1. 移動多邊形位置並旋旋

glTranslatef(model3D->pos.x, model3D->pos.y, model3D->pos.z);// 移動

glRotatef(model3D->rot.x, 1.0f, 0.0f, 0.0f); // 繞X軸旋轉

glRotatef(model3D->rot.y, 0.0f, 1.0f, 0.0f); // 繞Y軸旋轉

glRotatef(model3D->rot.z, 0.0f, 0.0f, 1.0f); // 繞Z軸旋轉

3.壓入當前屬性

glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT | GL_TEXTURE_BIT);

4.提取MD2模型

MD2_PTR md2 = model3D->md2;

5.綁定紋理

TEXTURE_PTR texture = model3D->texture;

glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, texture->ID);

5.啟用頂點數組

glEnableClientState(GL_VERTEX_ARRAY);

6.啟用紋理座標數組

glEnableClientState(GL_TEXTURE_COORD_ARRAY);

7.啟用法線數組

glEnableClientState(GL_NORMAL_ARRAY);

8.指定頂點數組

glVertexPointer(3, GL_FLOAT, 0, model3D->vertex_array);

9.紋理座標

glTexCoordPointer(2, GL_FLOAT, 0, md2->textcoord_array);

10.三角形法線

glNormalPointer(GL_FLOAT,0, model3D->normal_array);

11.繪畫所有當前以啟用的頂點數組

glDrawArrays(GL_TRIANGLES, 0, md2->polygon_num * 3 );

12.啟用頂點數組

glDisableClientState(GL_VERTEX_ARRAY);

14.啟用紋理座標數組

glDisableClientState(GL_TEXTURE_COORD_ARRAY);

15.啟用法線數組

glDisableClientState(GL_NORMAL_ARRAY);

16.彈出當前屬性

glPopAttrib();

17.當前矩陣堆棧出棧

glPopMatrix();

}

MD2載入器程式:下載

  1. 可分別載入『角色』與『武器』MD2模型
  2. 紋理載入支持 『.PCX』『.BMP』『.TGA』
  3. 按ALT鍵彈出『MENU』點『ANIMATION』選切換不同的動畫.

DirectSound之3D狼嚎

DirectSound之3D狼嚎

經多日努力終於可以在3D空間中添加中添加3D效果,所帶來極強真實感效果有時比3D渲染更好.而且x86和x64均正確運行.在演示程式中.『狼嚎』放置於山地中心.隨著按方向鍵移動相機聲音產生變化.單這點很多書本例程均不正確. 3D狼嚎程式:下載

而DirectSound如何模擬3D效果.3D即有xyz三軸笛卡爾座標,以此定義聲源和聽者位置、速度、方向.要注意Z軸是指向屏幕內部.所以3D效果需單聲道.

typdef struct{

IDirectSoundBuffer       * buffer;  // 輔助緩存

IDirectSound3DBuffer8    * buffer3D;// 3D緩存

float x,y,z;// 音源3D位置

PBYTE data;// 音頻數據

int size;// 所占空間

}SOUND3D;

SOUND3D sound3D;

而一般我地有左右兩個音箱,最多四角四個音箱.要模擬3D感知效果要素如下:

  1. 響度:當聲源逐漸遠離聽者,聲源會逐漸衰減
  2. 兩耳聲響差:若聲源位於聽者右側,則右耳聽到聲響比左耳響.
  3. 兩耳時間差:若聲源位於聽者右側,則右耳比左耳早聽到一毫秒.
  4. 兩耳朝向:若聲源在前邊則比後邊響
  5. 聲音最大距離:大於此距離聲音不再衰減.默認為DS3D_DEFAULTMAXDISTANCE(109 m)
  6. 聲音最小距離:大於此距離聲音不再遞增.默認為DS3D_DEFAULTMINDISTANCE(1m)
  7. 默認模式: DS3DMODE_NORMAL使用3D座標設定音源位置
  8. 頭部模式: DS3DMODE_HEADRELATIVE.可以設定音源位置,但監聽位置總為(0,0,0)
  9. 禁用3D模式:DS3DMODE_DISABLE 禁用3D聲音

WAV數據載入』後交與DirectSound代碼如下:

1.創建聲卡接口,NULL為與默認聲卡鏈接

IDirectSound8 * direct_sound3D = NULL;// 默認聲卡接口

DirectSoundCreate8(NULL,&direct_sound3D,NULL);

2.設置為優先等級,允許設定主緩存音頻數據格式並獲取IDirectSound3DListener8接口

direct_sound3D->SetCooperativeLevel(hWnd, DSSCL_PRIORITY);// 優先級別

3.初始化Directsound主緩存,主緩存代表聲卡,並設置格式

DSBUFFERDESC buffer;// 緩存格式描述

ZeroMemory(&buffer, sizeof(DSBUFFERDESC));// 清零

buffer.dwSize = sizeof(DSBUFFERDESC);// 所占空間

buffer.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER;//3D緩存並使用聲卡緩存

4.創建默認聲卡緩存接口

IDirectSoundBuffer * DirectSound3D_Buffer = NULL;

direct_sound3D->CreateSoundBuffer(&buffer, &DirectSound3D_Buffer, NULL);

5.獲取3D空間『聽者』

IDirectSound3DListener8  * DirectSound3D_Listener;// 3D聽者

DirectSound3D_Buffer->QueryInterface(IID_IDirectSound3DListener, (VOID**)&DirectSound3D_Listener);

6.設置緩存數據格式

WAVEFORMATEX format;//緩存格式描敘

memset(&format, 0, sizeof(WAVEFORMATEX));

format.cbSize = 0;//總是0.

format.wFormatTag = WAVE_FORMAT_PCM;  //脈衝編碼格式

format.nChannels = 1;   //(聲道數目)1為單聲道回放

format.nSamplesPerSec = 11025;//(每秒的樣本)總是這個速度

format.wBitsPerSample = 8;//聲音為8Bit

format.nBlockAlign = 1;//字節對齊,單聲道8bit占1byte.雙聲道16bit占4yte.

format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; //每秒播放字節數

DirectSound3D_Buffer->SetFormat(&format); //設置主緩存音頻格式

7.現在建立輔助聲音緩存

DSBUFFERDESC desc;// 緩存格式描敘

memset(&desc,0,sizeof(DSBUFFERDESC));

desc.dwSize = sizeof(DSBUFFERDESC);//dsbd的大小

desc.dwFlags = DSBCAPS_CTRL3D |// 請求3D BUFFER

DSBCAPS_CTRLVOLUME | // 緩存擁有音量控制功能

DSBCAPS_CTRLFREQUENCY | // 緩存擁有頻率控制功能

DSBCAPS_GLOBALFOCUS |

DSBCAPS_CTRLPOSITIONNOTIFY |  // 3D位置

DSBCAPS_GETCURRENTPOSITION2; // 播放位置

desc.dwBufferBytes = sound3D->size;//聲音數據的大小.

desc.guid3DAlgorithm = DS3DALG_DEFAULT;// 主緩沖區在 DSBCAPS_CTRL3D 模式下可以啟用這個選項

desc.lpwfxFormat = &format;// 緩存數據格式

8.創建輔助聲音緩衝器

direct_sound3D->CreateSoundBuffer(&desc, &sound3D->buffer, NULL);

9.獲取3D空間緩存

sound3D->buffer->QueryInterface(IID_IDirectSound3DBuffer, (VOID**)&sound3D->buffer3D);

10.把數據寫入聲音輔助緩存,輔助(二級)聲音緩存是自循環

UCHAR  audio_ptr_1 = NULL,audio_ptr_2 = NULL; //指向緩存第一與第二部分

DWORD   audio_len_1 = 0, audio_len_2 = 0;  //第一與第二緩存長度

11.鎖住空間

sound3D->buffer->Lock(0,//寫入指針指向位置

sound3D->size,//要鎖定空間大小.

(void **)&audio_ptr_1,//第一個部分開始地址.

&audio_len_1,//第一個部分長度

(void **)&audio_ptr_2,//第二個部分開始地址.

&audio_len_2,//第二個部分長度

DSBLOCK_FROMWRITECURS );

11.複製數據到聲音緩沖存儲器,拷貝到圓形緩沖存儲器的第一段中

memcpy(audio_ptr_1, sound3D->data, audio_len_1);

12.拷貝到圓形緩沖存儲器的第二段中

memcpy(audio_ptr_2, (sound3D->data + audio_len_1), audio_len_2);

13.解鎖

sound3D->buffer->Unlock(audio_ptr_1, audio_len_1,audio_ptr_2, audio_len_2);

14.設置音源3D位置與距離

DS3DBUFFER  param;

memset(&param, 0, sizeof(DS3DBUFFER));

param.dwSize = sizeof(DS3DBUFFER);

sound3D->buffer3D->GetAllParameters(&param);

param.flMaxDistance = sound3D->max;// 聲音最大距離 DS3D_DEFAULTMAXDISTANCE

param.flMinDistance = sound3D->min;// 聲音最小距離 DS3D_DEFAULTMINDISTANCE

param.vPosition.x = x; //音源3D位置

param.vPosition.y = y;

param.vPosition.z = -z;// Z軸反轉

param.dwMode = DS3DMODE_NORMAL;// 默認,使用3D座標設定音源位置和

sound3D->buffer3D->SetAllParameters(&param, DS3D_IMMEDIATE);// 立即設定

15.正確設置音源因子與3D聽者位置,關鍵部分是不要設置DS3DMODE_HEADRELATIVE(頭部模式)

DS3DLISTENER  param;

memset(&param, 0, sizeof(DS3DLISTENER));

param.dwSize = sizeof(DS3DLISTENER);

DirectSound3D_Listener->GetAllParameters(&param);

param.flDopplerFactor = 0; //多譜勒頻移,一般設為0

param.flRolloffFactor = 0.1f; // 衰減率,此直越大衰減越快.一般設為0.1f~0.5f

param.vPosition.x = x; //聽者3D位置

param.vPosition.y = y;

param.vPosition.z = -z;

DirectSound3D_Listener->SetAllParameters(&param, DS3D_IMMEDIATE);

16.循環播放音頻數據

sound3D->buffer->Play(0,0,DSBPLAY_LOOPING);

17.單次播放

sound3D->buffer->Play(0, 0, 0);

18.停止音頻播放

sound3D->buffer->Stop();

19.釋放DirectSound3D

sound3D->buffer3D->Release();// 釋放3D緩存

sound3D->buffer->Release();// 釋放輔助緩存

DirectSound3D_Buffer->Release(); // 釋放聲卡主緩存

direct_sound3D->Release();// 釋放聲卡

 

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