很多網友都發現『星海爭霸II』的對戰AI非常嚴重延遲,每格幾秒就出現停頓,遊戲運行『延遲』無非是三個原因:
- 硬件問題:
- 網絡延遲:
- 3D模型多邊形複雜度:
而星海爭霸 II 的AI對戰是需要聯網的『UPD連接』,如果網絡延遲較大則造成運行網絡,你需要重新選擇鏈接的『伺服器』
- 啟動net(無需切換帳號)
- 地區/帳號:分別有『美洲』,『歐洲』,『亞洲』.選擇速度快的『伺服器』
- 再啟動遊戲
BOOKCARD
很多網友都發現『星海爭霸II』的對戰AI非常嚴重延遲,每格幾秒就出現停頓,遊戲運行『延遲』無非是三個原因:
而星海爭霸 II 的AI對戰是需要聯網的『UPD連接』,如果網絡延遲較大則造成運行網絡,你需要重新選擇鏈接的『伺服器』
多紋理地形使用『草地紋理(2D紋理)』與『高度紋理(1D紋理)』相結合,根據海平面的高度為地形進行著色.『高度紋理(1D紋理)』即只有1行的紋理,載入後需按以下代碼載入紋理
Load_File_Texture(&terrain->height_texture, NULL, “height.tga”);// 載入”高度”紋理
Bind_Image_Texture(&terrain->height_texture); // 綁定”高度”紋理
glGenTextures(1, &texture->ID);// 生成紋理
glBindTexture(GL_TEXTURE_1D, texture->ID);// 綁定紋理
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);//夾持紋理
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, texture->width, 0,GL_RGB,GL_UNSIGNED_BYTE,texture->image); // 載入紋理
// 紋理單元1
glActiveTexture(GL_TEXTURE1);// 激活紋理單元1
glEnable(GL_TEXTURE_GEN_S);// S座標
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);//紋理坐標的生成模式
GLfloat waterPlane[] = { 0.0, 1.0, 0.0, -TERRAIN_WATER_HEIGHT };
glTexGenfv(GL_S, GL_OBJECT_PLANE, waterPlane);//紋理坐標的生成
// 紋理單元0
glActiveTexture(GL_TEXTURE0);// 激活紋理單元0
glEnable(GL_DEPTH_TEST);// 深度測試
glEnable(GL_TEXTURE_2D);// 啟用2D紋理映射
繪畫紋理地形的函是代碼
void Draw_Height_Terrain(TERRAIN_PTR terrain)
{
// 激活紋理單元0
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);// 2D紋理
glBindTexture(GL_TEXTURE_2D, terrain->grass.ID);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);// 平鋪效果
// 激活紋理單元1
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_1D);// 1D紋理(單行)
glBindTexture(GL_TEXTURE_1D, terrain->height_texture.ID);//高度紋理綁定到它上面
glMatrixMode(GL_TEXTURE);// 切換到紋理矩陣
glLoadIdentity();
glScalef(1.0f / TERRAIN_MAX_HEIGHT, 1.0, 1.0);// 設定s坐標比例尺.
glMatrixMode(GL_MODELVIEW);// 切換到視口矩陣
// 繪畫地形
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
for (int z = 0; z < terrain->width – 1; ++z)
{
glBegin(GL_TRIANGLE_STRIP);
for (int x = 0; x < terrain->width; ++x)
{
GLfloat scaledHeight = terrain->data[z * terrain->width + x] / terrain->scale;
GLfloat nextScaledHeight = terrain->data[(z + 1)*terrain->width + x] / terrain->scale;
// 指定紋理單元0的紋理座標
glMultiTexCoord2f(GL_TEXTURE0, (GLfloat)x / terrain->width * 8.0f , (GLfloat)z / terrain->width * 8.0f);
glVertex3f((GLfloat)x – terrain->width / 2.0f, scaledHeight, (GLfloat)z – terrain->width / 2.0f);
// 指定紋理單元0的紋理座標
glMultiTexCoord2f(GL_TEXTURE0, (GLfloat)x / terrain->width * 8.0f, (GLfloat)(z + 1) / terrain->width * 8.0f );
glVertex3f((GLfloat)x – terrain->width / 2.0f, nextScaledHeight, (GLfloat)(z + 1) – terrain->width / 2.0f);
}
glEnd();
}
glDisable(GL_TEXTURE_1D);// 禁用紋理單元1
glActiveTexture(GL_TEXTURE0);// 激活紋理單元0
//繪畫水面
glBindTexture(GL_TEXTURE_2D, terrain->water.ID);// 水面紋理
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0);
glVertex3f(-terrain->width / 2.1f, terrain->water_height, terrain->width / 2.1f);
glTexCoord2f(terrain->width / 4.0f, 0.0);
glVertex3f(terrain->width / 2.1f, terrain->water_height, terrain->width / 2.1f);
glTexCoord2f(terrain->width / 4.0f, terrain->width / 4.0f);
glVertex3f(terrain->width / 2.1f, terrain->water_height, -terrain->width / 2.1f);
glTexCoord2f(0.0, terrain->width / 4.0f);
glVertex3f(-terrain->width / 2.1f, terrain->water_height, -terrain->width / 2.1f);
glEnd();
}
演示程式下載:
OpenGL可同時對多邊形映射多個紋理,稱為『多紋理映射』. 它是OpenGL的可選擴展實現,並非所有的OpenGL都有實現.『OpenGL API的所有擴展必須得到ARB(OpenGL體系評審委員會)的認可』,在進行多紋理映射時,每個紋理單元都將映射結果傳遞給下個紋理單元,直至所有的紋理的紋理單元都進行映射(最終結果)演示程式:下载
多紋理映射分為四個步驟: |
1.判斷是否支持『多紋理映射』 |
2.讀取擴展函式的指針 |
3.建立紋理單元 |
4.設置紋理坐標 |
定義多紋理映射結構
typedef struct MULTITEXTURE_TYP {
TEXTURE texture0;
TEXTURE texture1;
}MULTITEXTURE,*MULTITEXTURE_PTR;
1.驗證當前版本的OpenGL是否支持『多紋理映射』
函式 | 簡介 |
glGetString(GL_EXTENSIONS) | 獲取OpenGL所支持的所有擴展函式
返回以空格隔開的字符串列表 |
GL_ARB_multitexture | 查找(GL_ARB_multitexture)判斷是否支持『多紋理映射』 |
判斷是否支持某一擴展函式
bool Extensions_OpenGL(const char * name)
{
char * extensions;
int index = 0;
int length,n;
extensions = (char*)glGetString(GL_EXTENSIONS);// 擴展函式
length = strlen(name);// 長度
while (extensions[index] != NULL)// 循環查找
{
n = strcspn(extensions + index, ” “);// 查找空格
if (n == length && strncmp(extensions + index, name, length) == 0)// 比較
return true;
index = index + n + 1; // 跳過空格
}
return false;
}
2.讀取擴展函式的指針
函式 | 簡介 |
PROC wglGetProcAddress(LPCSTR lpszProc); | 返回擴展函式的地址 |
擴展函式定義 | 簡介 |
typedef void (APIENTRY * PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); | 為多紋理映射設置紋理座標 |
typedef void (APIENTRY * PFNGLACTIVETEXTUREARBPROC) (GLenum texture); | 設置當前的紋理單元 |
typedef void (APIENTRY * PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); | 設置選擇當前的紋理單位,以便用頂點數組指定紋理坐標數據 |
初此化多紋理映射
PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB;
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB;
void Init_Multitexture()
{// 判斷是否支持某一擴展函式
if( Extensions_OpenGL(“GL_ARB_multitexture”) )
{
glMultiTexCoord2fARB=(PFNGLMULTITEXCOORD2FARBPROC)wglGetProcAddress(“glMultiTexCoord2fARB”);
glActiveTextureARB=(PFNGLACTIVETEXTUREARBPROC)wglGetProcAddress(“glActiveTextureARB”);
glClientActiveTextureARB=(PFNGLCLIENTACTIVETEXTUREARBPROC)wglGetProcAddress(“glClientActiveTextureARB”);
}
}
3.建立紋理單元
載入紋理並綁定 | 簡介 |
『.bmp』 『.tga』 『.pcx』 | 載入紋理文件 |
glGenTextures(1, &texture->ID); | 生成紋理單元綁定紋理 |
激活多紋理映射
void Active_Multitexture(MULTITEXTURE_PTR multitexture)
{
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, multitexture->texture0.ID);// 綁定紋理
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, multitexture->texture1.ID);// 綁定紋理
}
OpenGL最多支持32個紋理『GL_TEXTURE0_ARB』~『GL_TEXTURE31_ARB』
但最後通過查詢當前版本支持的最大單元量
int maxTexUnits;
glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&maxTexUnits);
4.設置紋理坐標
簡介 | |
void glMultiTexCoord2fARB(GLenum texUnit,float coords); | 設置紋理坐標 |
texUnit: GL_TEXTURE0_ARB~GL_TEXTURE31_ARB | 紋理單元索引 |
Coords: | 紋理坐標 |
Windows其中一個最好用的工具是『Microsoft屏幕放大鏡』,通過win鍵與+鍵啟動.如果你有『Microsoft鼠標』可以通過安裝『IntelliPoint8.2』激活母指鍵啟動『Microsoft屏幕放大鏡』.安裝後要重啟電腦. 通過『檔案總管\控制台\所有控制台項目\滑鼠』設定『母指鍵』.在『全屏幕』下按『母指鍵』+『鼠標滑輪』用於梯相最好用.
快捷鍵 | 簡介 |
Win鍵與+鍵 | 方大/啟動放大鏡 |
Win鍵與-鍵 | 縮小 |
Win鍵與ESC鍵 | 關閉放大鏡 |
鼠標前母指鍵 | 啟動放大鏡/關閉放大鏡 |
鼠標前母指鍵+鼠標滑輪 | 方大/縮小(這個最好用) |
CTRL+ALT+F | 全屏幕 |
CTRL+ALT+L | 透鏡 |
CTRL+ALT+D | 以連接擴充座(分屏) |
CTRL+ALT+SPACE | 預覽全屏幕 |
CTRL+ALT+I | 反色 |
鼠標輕觸開關是最容易損耗的,當鼠標出現連擊時,鼠標要準備退役,即使是最貴的鼠標使用壽命也相差唔多,就算鼠標有五年保養也不會幫你更換輕觸開關,唔信你可以試下拿它返廠.
如果你很喜歡你的滑鼠可以通過更換滑鼠的輕觸開關(如上圖),延長滑鼠使用壽命.因位兩層上錫要將它熔焊有D難度.因為『霸雷鯊7000』只有兩隻腳所以無分正負,貼緊上錫即可.
如果『霸雷鯊7000』出現斷幀可以償試拔掉其它USB設備.
PCX圖檔較常用於3D紋理,你的3D遊戲引擎無任何理由拒絕支持PCX格式的圖檔.幸好PCX格式非常簡單.渲染演示程式下載:
它主要由三部份組成:
PCX文檔頭部結構 | 簡介 |
typedef struct PCX_HEADER_TAG{ | |
UCHAR manufacturer; | PCX標記:總是 0x0A |
UCHAR version; | 版本號 |
UCHAR encoding; | 編碼:總為 1,使用RLE編碼 |
UCHAR bits_per_pixel; | 每像數所占的位數 1,2,4,8 |
USHORT xmin, ymin; | 圖像的左下角邊界 |
USHORT xmax, ymax; | 圖像的右上角邊界 |
USHORT hres; | 水平分辨率 |
USHORT yres; | 垂直分辨率 |
UCHAR EGAcolors[48]; | EGA(16色)調色板,這種圖檔以較小式用 |
UCHAR reserved; | 保留字節 |
UCHAR color_planes; | 色彩平面量 (24Bit圖檔為3) |
USHORT bytes_per_line; | 每行字節數(單個顏色分量) |
USHORT palette_type; | 1為灰度,2為彩色調色板 |
USHORT scrnw; | 屏幕水平像素 |
USHORT scrnh; | 屏幕垂直像素 |
UCHAR filler[54]; | 54BYTE全零 |
} PCX_HEADER, *PCX_HEADER_PTR; |
你需要定義新的PCX結構用於保存PCX信息.
PCX | 結構 |
typedef struct PCX_TAG{ | |
int width; | 圖寬=xmax – xmin + 1 |
int height; | 圖高=ymax – ymin + 1 |
int bitCount; | 位圖像素的bits 8位,16位,24位,32位
bitCount=color_planes*bytes_per_line |
PCX_PALETTEENTRY palette[256]; | 調色板,當圖檔為256色時出現 |
PBYTE buffer; | 圖像數據 |
} PCX, *PCX_PTR; |
我實現PCX解釋器支持『256色』『16BIT』兩種常見格式:
bool Load_PCX(PCX_PTR pcx,PBYTE data,int size)
{
int index; // 循环变量
PBYTE image;// 图像数据
int image_size;// 像数个数
PBYTE RLE;// RLE编码图像数据
PCX_HEADER_PTR header;// 文档的头部
PCX_PALETTEENTRY_PTR palette;// 读取PCX的调色版
header = (PCX_HEADER_PTR)data; // 文档的头部
pcx->width = (header->xmax – header->xmin) + 1;// 图像宽度(像数)
pcx->height = (header->ymax – header->ymin) + 1;// 图像高度(像数)
pcx->bitCount = header->bits_per_pixel * header->color_planes;// 计算每像数所占的位数
RLE = data + sizeof(PCX_HEADER);
if (pcx->bitCount == 8)
{ // 分配图像记忆体
pcx->buffer = (PBYTE)malloc(pcx->width*pcx->height * 3);
image = (PBYTE)malloc(pcx->width*pcx->height);
image_size = pcx->width * pcx->height;// 图像的像素
Load_RLE_PCX(image, RLE, pcx->width, pcx->height);// RLE解码
// 读取PCX的调色版
palette = (PCX_PALETTEENTRY_PTR)(data + size – 768);// 在文件的结束的位置前移768字节即移动到调色板的开始位置
for (int i = 0; i < image_size; ++i)
{//掉转红色和绿色
index = image[i];
pcx->buffer[i*3 + 0] = palette[index].red;// 红色部分
pcx->buffer[i*3 + 1] = palette[index].green;// 取的绿色部分
pcx->buffer[i*3 + 2] = palette[index].blue;// 取的蓝色部分
}
pcx->bitCount = 24;
free(image);// 释放记忆体
}
else
if (pcx->bitCount == 24)
{// 分配图像记忆体
pcx->buffer = (PBYTE)malloc(pcx->widthpcx->height3);
Load_RLE24_PCX(pcx->buffer, RLE, pcx->width, pcx->height);// RLE解码
}
return true;
}
『PCX』與『BMP』同樣支持『RLE編碼』,而且支持8Bit和24Bit的『RLE編碼』渲染演示程式下載:
先講解8Bit (256色)『RLE編碼』算法:
PCX的24BIT圖檔同樣使用『RLE編碼』,但網絡上PCX的24Bit『RLE解碼』算法大多都是不正確,
24Bit『RLE編碼』算法:
8Bit (256色)『RLE解碼』C代碼:
void Load_RLE_PCX(PBYTE image, PBYTE data, int width,int height)
{
BYTE value;
int length;// 像素個數
int data_index = 0;// RLE索引
int image_index = 0;// 圖像索引
int image_size = width * height;
while (image_index < image_size)// 遍歷壓縮後數據
{// 判斷是否RLE編碼
if (data[data_index] >= 192 && data[data_index] <= 255)
{// 重複的數據
length = data[data_index] – 192;// 長度
value = data[data_index + 1];// 索引值
while (length > 0)
{
image[image_index] = value;
++image_index;
–length;
}
data_index = data_index + 2;
}
else
{// 不重的數據
image[image_index] = data[data_index];
++image_index;
++data_index;
}
}
}
24Bit『RLE解碼』C代碼:
void Load_RLE24_PCX(PBYTE image, PBYTE data, int width, int height)
{
BYTE value;
int length = 0;// 像素個數
int data_index = 0; // RLE索引
int image_index = 0; // 圖像索引
int image_size = width * height;
for (image_index = 0; image_index < image_size; image_index = image_index + width)// 遍歷RLE編碼數據
{
for (int i = 0, x = 0; i < 3 && x < width; )
{// 判斷是否RLE編碼
if (data[data_index] >= 192 && data[data_index] <= 255)
{// 讀取重複的數據
length = data[data_index] – 192;// 數據的長度
//length = data[data_index] & 0x3F;
value = data[data_index + 1];// 數值
data_index = data_index + 2; // RLE編碼索引
while (length > 0)
{
if (x >= width)
{
++i;
x = 0;// 以達到行尾跳轉 i加1 x設0
}
image[(image_index + x) * 3 + i] = value;//寫入重複數據
++x;// x座標索引加一
–length;// 重複數據量減一
}
}
else
{// 無壓縮的數據
image[(image_index + x) * 3 + i] = data[data_index];// 寫入
++data_index; // RLE編碼索引
++x;
}
if (x >= width)
{
++i;
x = 0;
}
}
}
return;
}
地形文檔其實就是灰度圖,每一位灰度(0~255)對應其高度值,由高度值組成的二維點,二維點以沿X軸和Z軸分佈. 而Y軸則代表地形的高度,將這些數據作為網格渲染得到大概的地貌『地形圖』,將『灰度值』剩以『地圖比例』的高地形的高度值,較亮的灰度對應於較高的地形,較暗的灰度對應於較低的地形.
『高度值=灰度值*地圖尺寸比例因子』 |
『地圖頂點座標=二維點索引*地圖尺寸比例因子』 |
渲染地形時沿著Z軸方向每個二維點使用GL_TRIANGLE_STRIP繪畫相連的三角形,沿X軸正方向移動按Z字形路線模式來繪製,當達到行的令一端時就移到下一行用同樣的方法進行繪製,如此直至完成繪畫.對地形進行紋理映射,因為紋理是正方形,所以每四個點頂點指定一個紋理,由兩個三角形組成四邊形,每個四邊形對應一個紋理.
除地形圖外還有海平面掩蓋低窪地帶,海平面由四邊形和的高度值組成,再將『水紋理』應用於四邊形使得海水具有真實感.
繪畫紋理地形的C代碼:
void Draw_Terrain(TERRAIN_PTR terrain)
{
// 繪畫紋理地形
glBindTexture(GL_TEXTURE_2D, terrain->grass.ID);
// 紋理顏色與像素顏色相剩
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
for (int z = 0; z < terrain->width – 1; ++z)
{
glBegin(GL_TRIANGLE_STRIP);// 繪畫三角形
for (int x = 0; x < terrain->width; ++x)
{// 一次渲染兩個頂點
float currScaledHeight = terrain->data[z * terrain->width + x] / terrain->scale;
float nextScaledHeight = terrain->data[(z + 1)* terrain->width + x] / terrain->scale;
float color = 0.5f + 0.5f * currScaledHeight / terrain->max_height; // 灰度值
float nextColor = 0.5f + 0.5f * nextScaledHeight / terrain->max_height; // 灰度值
glColor3f(color, color, color);
glTexCoord2f((GLfloat)x / terrain->width * 8.0f, (GLfloat)z / terrain->width * 8.0f);
glVertex3f((GLfloat)(x – terrain->width/2.0f), currScaledHeight, (GLfloat)(z – terrain->width / 2.0f));
glColor3f(nextColor, nextColor, nextColor);
glTexCoord2f((GLfloat)x / terrain->width * 8.0f, (GLfloat)(z + 1.0f) / terrain->width * 8.0f);
glVertex3f((GLfloat)(x – terrain->width/2.0f), nextScaledHeight, (GLfloat)(z + 1.0f – terrain->width/2.0f));
}
glEnd();
}
// 繪畫水面
glBindTexture(GL_TEXTURE_2D, terrain->water.ID);// 綁定紋理
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);// 重複的紋理
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);// 紋理座標
glVertex3f(-terrain->width / 2.1f, terrain->water_height, terrain->width / 2.1f);// 頂點座標
glTexCoord2f(terrain->width / 4.0f, 0.0f);
glVertex3f(terrain->width / 2.1f, terrain->water_height, terrain->width / 2.1f);
glTexCoord2f(terrain->width / 4.0f, terrain->width / 4.0f);
glVertex3f(terrain->width / 2.1f, terrain->water_height, -terrain->width / 2.1f);
glTexCoord2f(0.0f, terrain->width / 4.0f);
glVertex3f(-terrain->width / 2.1f, terrain->water_height, -terrain->width / 2.1f);
glEnd();
}
演示程式:下載
『天幕』其是就在一个大立方体的内侧贴上图像,从而绘画出远景地屏线效果,用于增加远景真实感有效而简单的方法,天幕的中心位于『相机』当前位置,所以当『相机』移动时『天幕』的中心点也同时移动.
演示程式:下载
『天幕』的算法
渲染天幕C代码
void Render_SKYBOX(SKYBOX_PTR skybox,float xPos,float yPos,float zPos)
{
glPushMatrix();// 当前矩阵堆栈压栈
glTranslatef(xPos, yPos, zPos);// 移动相机位置
glPushAttrib(GL_FOG_BIT | GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT);// 压入当前属性
glDisable(GL_DEPTH_TEST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);// 设定纹理复盖模式为”重复纹理”
// 面紋理座標和頂點座標
// 天
glBindTexture(GL_TEXTURE_2D, skybox->texture[SKY_TOP].ID);// 绑定纹理
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-skybox->size, skybox->size, -skybox->size);
glTexCoord2f(0.0f, 1.0f); glVertex3f(skybox->size, skybox->size, -skybox->size);
glTexCoord2f(0.0f, 0.0f); glVertex3f(skybox->size, skybox->size, skybox->size);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-skybox->size, skybox->size, skybox->size);
glEnd();
// 地
glBindTexture(GL_TEXTURE_2D, skybox->texture[SKY_BOTTOM].ID);
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 1.0f); glVertex3f(skybox->size, -skybox->size, -skybox->size);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-skybox->size, -skybox->size, -skybox->size);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-skybox->size, -skybox->size, skybox->size);
glTexCoord2f(1.0f, 0.0f); glVertex3f(skybox->size, -skybox->size, skybox->size);
glEnd();
// 前
glBindTexture(GL_TEXTURE_2D, skybox->texture[SKY_FRONT].ID);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-skybox->size, -skybox->size, -skybox->size);
glTexCoord2f(1.0f, 0.0f); glVertex3f(skybox->size, -skybox->size, -skybox->size);
glTexCoord2f(1.0f, 1.0f); glVertex3f(skybox->size, skybox->size, -skybox->size);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-skybox->size, skybox->size, -skybox->size);
glEnd();
// 后
glBindTexture(GL_TEXTURE_2D, skybox->texture[SKY_BACK].ID);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(skybox->size, -skybox->size, skybox->size);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-skybox->size, -skybox->size, skybox->size);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-skybox->size, skybox->size, skybox->size);
glTexCoord2f(0.0f, 1.0f); glVertex3f(skybox->size, skybox->size, skybox->size);
glEnd();
// 右
glBindTexture(GL_TEXTURE_2D, skybox->texture[SKY_RIGHT].ID);
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 0.0f); glVertex3f(skybox->size, -skybox->size, skybox->size);
glTexCoord2f(1.0f, 1.0f); glVertex3f(skybox->size, skybox->size, skybox->size);
glTexCoord2f(0.0f, 1.0f); glVertex3f(skybox->size, skybox->size, -skybox->size);
glTexCoord2f(0.0f, 0.0f); glVertex3f(skybox->size, -skybox->size, -skybox->size);
glEnd();
// 左
glBindTexture(GL_TEXTURE_2D, skybox->texture[SKY_LEFT].ID);
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-skybox->size, -skybox->size, -skybox->size);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-skybox->size, skybox->size, -skybox->size);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-skybox->size, skybox->size, skybox->size);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-skybox->size, -skybox->size, skybox->size);
glEnd();
glPopAttrib();// 弹出当前属性
glEndList();
glPopMatrix();// 当前矩阵堆栈出栈
}
讓旗幟飄揚的核心是波浪算法,使用sin()函式將旗幟頂點初此化為波紋,然後每幀移動波紋
演示程式:下載
算法如下:
旗幟結構 | 簡介 |
typedef struct FLAG_TAG{ | |
float points[36][20][3]; | 頂點數據36*20個頂點,每個頂點3(xyz)個分量 |
int time_start; | 啟動時間用於控制波浪移動速度 |
}FLAG, *FLAG_PTR; |
初此化旗幟使用sin()波紋函式來初此化旗幟
bool Init_Flag(FLAG_PTR flag)
{
if (flag == NULL)
return false;
for (int x = 0; x < 36; ++x)
{
for (int y = 0; y < 20; ++y)
{
flag->points[x][y][0] = (float)x;// X軸
flag->points[x][y][1] = (float)y;// Y軸
float value = (x*20.0f / 360.0f) * 2.0f * 3.14159f;
flag->points[x][y][2] = (float)sin(value);// 正弦計算
}
}
return true;
}
繪畫旗幟
void Draw_Flag(FLAG_PTR flag,float xPos,float yPos,float zPos,float xRot,float yRot,float zRot)
{
int x, y;
float left, right, top, bottom;
glPushMatrix();// 當前矩陣堆棧壓棧
glBindTexture(GL_TEXTURE_2D, Flag_Texture.ID);// 綁定紋理
glTranslatef(xPos, yPos, zPos);// 移動旗幟座標
glRotatef(xRot, 1.0f, 0.0f, 0.0f); // 繞X軸旋轉旗幟
glRotatef(yRot, 0.0f, 1.0f, 0.0f); // 繞Y軸旋轉旗幟
glRotatef(zRot, 0.0f, 0.0f, 1.0f); // 繞Z軸旋轉旗幟
glBegin(GL_QUADS);// 繪畫木箱頂面紋理座標和頂點座標
// 遍歷旗幟所有頂點,除了方向上的最後兩個點
// 因為只是用它來繪製每個方向上最後的GL_QUAD
for (x = 0; x < 35; ++x)
{
for (y = 0; y < 18; ++y)
{ // 為當前的四邊形計算紋理座標
left = (float)x / 35.0f; //四邊形左則的紋理座標
bottom = (float)y / 18.0f ;//四邊形頂則的紋理座標
right = (float)(x+1) / 35.0f; //四邊形右則的紋理座標
top = (float)(y+1) / 18.0f ; //四邊形底則的紋理座標
// 設定四邊形左下角的紋理座標與頂點座標
glTexCoord2f(left, bottom);// 紋理座標
glVertex3f(flag->points[x][y][0],
flag->points[x][y][1],
flag->points[x][y][2]);//頂點座標
// 設定四邊形右下角的紋理座標與頂點座標
glTexCoord2f(right, bottom);// 紋理座標
glVertex3f(flag->points[x+1][y][0],
flag->points[x+1][y][1],
flag->points[x+1][y][2]);//頂點座標
// 設定四邊形右上角的紋理座標與頂點座標
glTexCoord2f(right, top);// 紋理座標
glVertex3f(flag->points[x+1][y+1][0],
flag->points[x+1][y+1][1],
flag->points[x+1][y+1][2]);//頂點座標
// 設定四邊形左上角的紋理座標與頂點座標
glTexCoord2f(left, top);// 紋理座標
glVertex3f(flag->points[x][y+1][0],
flag->points[x][y+1][1],
flag->points[x][y+1][2]);//頂點座標
}
}
glEnd();
DWORD tick = GetTickCount(); //返回從操作系統啟動所經過的毫秒數
if ((tick – flag->time_start) > 100)
{
flag->time_start = tick;
float wrap;
// 生成旗幟的運動
// 訪問旗幟中的每一點,將每點的Z軸座標向右移動一位來模擬波浪運動
for (y = 0; y < 19; ++y)// Y軸方向循環
{// 存儲此行中最右端頂點的Z座標
wrap = flag->points[35][y][2];
for (x = 35; x >= 0; –x)// X軸方向循環
{// 將當前頂點Z座標設為前一頂點的Z座標值
flag->points[x][y][2] = flag->points[x-1][y][2];
}
// 將最左端頂點的Z座標設為先前所存儲的wrap
flag->points[0][y][2] = wrap;
}
}
glPopMatrix();// 當前矩陣堆棧出棧
}
你必須登入才能發表留言。