剛啟動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
BOOKCARD
Open Graphics library
剛啟動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遊戲也應全屏.在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);
是時候將之前所學OpenGL/DirectX知識粘合成一完整遊戲.最簡單是『第一人稱視角射擊遊戲』FPS(First-person Shooter). 通過控制『準星』描准『怪物』按滑鼠左鍵發射『飛彈』.若擊中便會『爆炸』並擊殺『怪物』,其實它會在另地在生.若擊中地面則會『爆炸』.按鍵盤上下鍵在山地中前後移動.若近距離接近『怪物』它會另轉面自動走開(AI部分). 所有『怪物』與『飛彈』都是MD2模型.按ESC鍵退出遊戲.下載FPS:
下面所需要文章與代碼:
射擊遊戲在屏幕中畫有『準星』用於射擊怪獸.因為『準星』固定在屏幕中心.當你移動滑鼠時.相機視角也隨之改變.而當你要移動射擊位置可通過按鍵盤上下鍵
移動滑鼠 | 響應 | 角度 |
左移 | 相機視角繞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相機模型
最後製作準星紋理
遊戲中最常見『爆炸』特效.最常見做法是使用『粒子系統』模擬『爆炸』.你大約需要:
其實所有『爆炸』特效都是圓心紋理位圖.只是它使用『廣告牌』技術讓其正對著你即『相機』. 渲染為紅色並讓其隨時間『下墜』『變暗』『縮小』直至消亡.之前模擬『飄雪』特效時使用三級系統.這次我將粒子結構大大簡化.其代碼我都在模擬『爆炸』特效中實現.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();// 彈出當前屬性
}
『曲棍球』是碰撞算法最好演示,在遊戲中『球臺』寬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文檔由美國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;// 以完成動作
}
}
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();
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載入器程式:下載
經多日努力終於可以在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感知效果要素如下:
『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(¶m, 0, sizeof(DS3DBUFFER));
param.dwSize = sizeof(DS3DBUFFER);
sound3D->buffer3D->GetAllParameters(¶m);
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(¶m, DS3D_IMMEDIATE);// 立即設定
15.正確設置音源因子與3D聽者位置,關鍵部分是不要設置DS3DMODE_HEADRELATIVE(頭部模式)
DS3DLISTENER param;
memset(¶m, 0, sizeof(DS3DLISTENER));
param.dwSize = sizeof(DS3DLISTENER);
DirectSound3D_Listener->GetAllParameters(¶m);
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(¶m, 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();// 釋放聲卡
『陰影』有很多種方法,而『投射陰影』技術已被大量的應用於遊戲,從光源位置透視投影3D物體,從而將陰影投射到平面. 而且視覺效果佳.算法思路如下:
落雪陰影演示下載:
構造投影矩陣
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();
『鏡像』並非是真實世界中由光粒子所產生.而式通過模擬『鏡像』技術.工作原理如下:
『鏡像』演示程式下載:
『鏡像』示例代碼:
// 禁用對所有的然色分量的修改
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();
你必須登入才能發表留言。