星海爭霸 II之AI對戰延遲問題

星海爭霸 II之AI對戰延遲問題

很多網友都發現『星海爭霸II』的對戰AI非常嚴重延遲,每格幾秒就出現停頓,遊戲運行『延遲』無非是三個原因:

  1. 硬件問題:
  2. 網絡延遲:
  3. 3D模型多邊形複雜度:

而星海爭霸 II 的AI對戰是需要聯網的『UPD連接』,如果網絡延遲較大則造成運行網絡,你需要重新選擇鏈接的『伺服器』

  1. 啟動net(無需切換帳號)
  2. 地區/帳號:分別有『美洲』,『歐洲』,『亞洲』.選擇速度快的『伺服器』
  3. 再啟動遊戲

OpenGL之多紋理地形

OpenGL之多紋理地形

多紋理地形使用『草地紋理(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的可選擴展實現,並非所有的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);

glBindTexture(GL_TEXTURE_2D, 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屏幕放大鏡

Windows之Microsoft屏幕放大鏡

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 反色

 

Microsoft 無線霸雷鯊7000鼠標修復

Microsoft 無線霸雷鯊7000鼠標修復

鼠標輕觸開關是最容易損耗的,當鼠標出現連擊時,鼠標要準備退役,即使是最貴的鼠標使用壽命也相差唔多,就算鼠標有五年保養也不會幫你更換輕觸開關,唔信你可以試下拿它返廠.

如果你很喜歡你的滑鼠可以通過更換滑鼠的輕觸開關(如上圖),延長滑鼠使用壽命.因位兩層上錫要將它熔焊有D難度.因為『霸雷鯊7000』只有兩隻腳所以無分正負,貼緊上錫即可.

如果『霸雷鯊7000』出現斷幀可以償試拔掉其它USB設備.

 

OpenGL之讀取PCX圖檔

OpenGL之讀取PCX圖檔

PCX圖檔較常用於3D紋理,你的3D遊戲引擎無任何理由拒絕支持PCX格式的圖檔.幸好PCX格式非常簡單.渲染演示程式下載:

它主要由三部份組成:

  1. 文檔頭部
  2. 經RLE編碼的圖像數據
  3. 調色板,只用於256色
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;

}

OpenGL之讀取PCX文檔-RLE解碼

OpenGL之讀取PCX文檔-RLE解碼

『PCX』與『BMP』同樣支持『RLE編碼』,而且支持8Bit和24Bit的『RLE編碼』渲染演示程式下載:

先講解8Bit (256色)『RLE編碼』算法:

  1. 首字節把『重複』像素的個數的最高兩BIT設為1保存, 0xC0&length
  2. 解碼時只要把首字節減192即等於『重複』個數,或者data& 0x3F提取最低六位.
  3. 次字節寫入重複的像素值
  4. 並且『重複』像素的最大長度為63,因為255(0xff)-192(0xc0)=63
  5. 如果像素大於192,把像數當成『不重複』的像素直接保存
  6. 『不重複』的像素直接保存

PCX的24BIT圖檔同樣使用『RLE編碼』,但網絡上PCX的24Bit『RLE解碼』算法大多都是不正確,

24Bit『RLE編碼』算法:

  1. 24BIT算法8Bit基本一致.最大分別是它對每行像素的RGB值進行分解後再進行RLE編碼
  2. 數據:RGB RGB RGB
  3. 分解:RRR GGG BBB
  4. RLE:3R 3G 3B

 

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;

}

 

OpenGL之紋理地形

OpenGL之紋理地形

地形文檔其實就是灰度圖,每一位灰度(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();

}

演示程式:下載

  1. 鼠標『左/右』鍵移動相機繞Y軸旋轉
  2. 鼠標『上/下』鍵移動相機繞X軸旋轉

OpenGL之天幕

OpenGL之天幕

『天幕』其是就在一个大立方体的内侧贴上图像,从而绘画出远景地屏线效果,用于增加远景真实感有效而简单的方法,天幕的中心位于『相机』当前位置,所以当『相机』移动时『天幕』的中心点也同时移动.

演示程式:下载

  1. 按『左/右』键观察『前、后、左、右』的效果
  2. 按『上/下』键观察『天、地』的效果

『天幕』的算法

  1. 准备六张照片分别为『天、地、前、后、左、右』并载入『记忆体』
  2. 绘画一个大『正方体』由六个四边形组成
  3. 在『正方体』的内则贴上图像
  4. 『天幕』中心点与『相机』位置同步移动

 

渲染天幕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();// 当前矩阵堆栈出栈

}

OpenGL之旗幟飄揚

OpenGL之旗幟飄揚

讓旗幟飄揚的核心是波浪算法,使用sin()函式將旗幟頂點初此化為波紋,然後每幀移動波紋

演示程式:下載

  1. 按方向鍵移動相機
  2. 按ESC鍵相機返回原點

算法如下:

  1. 將旗幟定義為長方形,橫向(x軸)36個頂點,縱向(y軸)20個頂點,35*19個網格
  2. 波紋算法: z = sin((x * height * 360) * 2 * π)
  3. 每幀沿Z軸往右移動波紋來模擬波浪運動,最右端的Z座標移動到最左端
  4. 在繪畫旗幟時從左下角開此,一次繪畫一個GL_QUAD按逆時針繪畫紋理座標與頂點座標
  5. 紋理座標:『s=x/width』『t=y/height』
  6. 旗幟『紋理』定義為正方形,分辨率越大越好,『紋理載入
旗幟結構 簡介
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();// 當前矩陣堆棧出棧

}