
很多網友都發現『星海爭霸II』的對戰AI非常嚴重延遲,每格幾秒就出現停頓,遊戲運行『延遲』無非是三個原因:
- 硬件問題:
- 網絡延遲:
- 3D模型多邊形複雜度:
而星海爭霸 II 的AI對戰是需要聯網的『UPD連接』,如果網絡延遲較大則造成運行網絡,你需要重新選擇鏈接的『伺服器』
- 啟動net(無需切換帳號)
- 地區/帳號:分別有『美洲』,『歐洲』,『亞洲』.選擇速度快的『伺服器』
- 再啟動遊戲
www.bookcard.net

多紋理地形使用『草地紋理(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 | 反色 |

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();// 當前矩陣堆棧出棧
}

紋理繪畫多邊形當遠離視點,會出現視覺失真或炫目問題,原因是OpenGL對兩個相鄰的像素進行采樣時,從紋理圖中的一些差別較大的部分進行采樣所產生的,在靜止場景問題並不明顯,若運動時紋理紋理采樣發生改變時炫目問題將會更特出,使用mipmap『紋理鏈』能消除部分失真,因為低解釋度的紋理被應用到遠距離的多邊形上時采樣更加精准.因為紋理一級級縮小時紋理可以保持平滑,使用mipmap『紋理鏈』的另一個好處是較小的紋理更容易保存在顯卡『記憶體』中提高命中率.
OpenGL的gluBuild2DMipmaps()可以全自動的生成mipmap『紋理鏈』.但不仿自已動手生成.
mipmap『紋理鏈』演示程式下載:
紋理結構定義可梯『OpenGL之紋理映射』OpenGL根據目標圖像的大小選擇紋理圖像,『紋理鏈』索引從0開始到分辨率1*1 結束,所以你要對紋理近行縮小:
生成紋理鏈代碼
1.不斷縮小紋理圖,直到分辨率1*1
while (width != 1 && height != 1){
2.獲取索引
index = texture->count;
3.縮小紋理
texture->image[index] = Decrease_Image_Texture(texture->image[index-1], width, height);
4.紋理個數加一
++texture->count;
5.寬與高減一半
width = width / 2;
height = height / 2;
}
紋理圖像的寬和高均縮小一半的函式
image:紋理圖
width,height:寬和高
PBYTE Decrease_Image_Texture(PBYTE image,int width,int height){
int r1,r2,r3,r4, g1,g2,g3,g4, b1,b2,b3,b4 ;
int index,x,y;
//分配縮小紋理記憶體
buffer = (PBYTE)malloc(width/2 * height/2 * 3);
for (y = 0; y < height; y = y + 2) //遍歷Y座標
{
for (x = 0; x < width; x = x + 2)//遍歷X座標
{
index = Get_Index_Texture(x, y, 0, 0, width, height) * 3;// 提取紋理像素
r1 = image[index + 0];
g1 = image[index + 1];
b1 = image[index + 2];
index = Get_Index_Texture(x, y, 1, 0, width, height) * 3;// 提取紋理像素
r2 = image[index + 0];
g2 = image[index + 1];
b2 = image[index + 2];
index = Get_Index_Texture(x, y, 0, 1, width, height) * 3;// 提取紋理像素
r3 = image[index + 0];
g3 = image[index + 1];
b3 = image[index + 2];
index = Get_Index_Texture(x, y, 1, 1, width, height) * 3; //提取紋理像素
r4 = image[index + 0];
g4 = image[index + 1];
b4 = image[index + 2];
index = (y/2 * width/2 + x/2) * 3;
//加權平均計算目標紋理像素
buffer[index + 0] = (r1 + r2 + r3 + r4) / 4;
buffer[index + 1] = (g1 + g2 + g3 + g4) / 4;
buffer[index + 2] = (b1 + b2 + b3 + b4) / 4;
}
}
計算紋理像素索引函式
x,y:座標
dx,dy:座標增量
wdith,height:紋理圖的寬和高
int Get_Index_Texture(int x, int y, int dx, int dy, int width, int height) {
if (x + dx >= width) //判斷x座標是否越界
dx = 0;
if (y + dy >= height) //判斷y座標是否越界
dy = 0;
int index = (y + dy) * width + (x + dx); 計算紋理像素索引
return index; 返回索引
}
綁定mipmap『紋理鏈』代碼函式
bool Bind_Texture(TEXTURE_PTR texture){
glGenTextures(1, &texture->ID);//生成紋理
glBindTexture(GL_TEXTURE_2D, texture->ID);//綁定紋理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);// 重複紋理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);// 重複紋理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//放大紋理像素採樣線性插值(加權平均)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);//縮小紋理紋理鏈採樣使用NEAREST,過濾使用LINEAR
for (int index = 0, v = 1; index < texture->count; ++index, v = v * 2)//遍歷紋理鏈
glTexImage2D(GL_TEXTURE_2D, index, GL_RGB, texture->width / v, texture->height / v, 0, GL_RGB, GL_UNSIGNED_BYTE, texture->image[index]); //載入紋理
}

將紋理映射到3D模型是革命性技術,給人帶來照片般震撼逼真效果,簡單來講紋理映射就是將圖片附著於多邊形之上,這樣的圖片稱之為紋理,你可以將平鋪的地圖映射到球體上從而得到3D地球模型,下面以渲染木箱為例的演示程式:下載
| 生成紋理對像 | 簡介 |
| void glGenTextures(GLsizei n,GLuint *textures); | 生成紋理並返回紋理『索引』即ID編號 |
| n | 紋理個數 |
| textures | 數組,返回紋理ID |
| 綁定紋理 | 簡介 |
| void glBindTexture(GLenum target,GLuint texture); | 生成紋理之後要進行綁定 |
| Target | GL_TEXTURE_1D:1維紋理
GL_TEXTURE_2D:2維紋理 |
| Textures | 紋理ID數組 |
| 過濾紋理 | 簡介 |
| void glTexParameteri(GLenum target,GLenum pname,GLint param); | 綁定之後要設定紋理過濾 |
| target: | GL_TEXTURE_1D:1維紋理
GL_TEXTURE_2D:2維紋理 |
| pname: | GL_TEXTURE_MIN_FILTER:縮小過濾
GL_TEXTURE_MAG_FILTER:放大過濾 GL_TEXTURE_WRAP_S:紋理S座標 GL_TEXTURE_WRAP_T:紋理T座標 |
| param:
|
GL_REPEAT:重複(平鋪)紋理
GL_CLAMP:夾持紋理 GL_LINEAR:像素採樣線性插值(加權平均) GL_NEAREST:像素採樣最接近中心紋理 GL_NEAREST_MIPMAP_NEAREST:紋理鏈採樣使用NEAREST,過濾使用NEAREST GL_NEAREST_MIPMAP_LINEAR:紋理鏈採樣使用NEAREST,過濾使用LINEAR GL_LINEAR_MIPMAP_NEAREST:紋理鏈採樣使用LINEAR,過濾使用NEAREST GL_LINEAR_MIPMAP_LINEAR:紋理鏈採樣使用LINEAR,過濾使用LINEAR |
| 載入紋理 | 簡介 |
| void glTexImage2D(GLenum target,GLint level,GLint internalformat,GLsizei width,GLsizei height,GLint border,GLint format,GLenum type,const GLvoid *pixels); | 設定紋理過濾後需要把紋理載入OpenGL
type:最常用GL_UNSIGNED_BYTE(無符號8Bit) pixels:紋理數據 |
| target: | GL_TEXTURE_1D:1維紋理
GL_TEXTURE_2D:2維紋理 |
| level: | 紋理鏈索引,若只有單個紋理則設為0 |
| internalformat: | 最常用GL_RGBA或GL_RGB |
| width: | 紋理寬度 |
| height: | 紋理高度 |
| border: | 紋理是否有變框,0沒有邊框,1有邊框 |
| format: | 最常用GL_RGBA或GL_RGB |
| 紋理座標與頂點座標 | 簡介 |
| void glTexCoord2f (GLfloat s, GLfloat t); | 設定紋理座標(s,t), s軸紋理的x座標,t軸為紋理的y座標,紋理座標必需在頂點座標之前設定 |
| void glVertex3f(GLfloat x,GLfloat y,GLfloat z); | 設定頂點座標,並與紋理座標匹配 |
| 定義紋理結構 | 簡介 |
| typedef struct TEXTURE_TYP{ | 此結構用於保存紋理位圖信息 |
| int width; | 紋理寬度 |
| int height; | 紋理高度 |
| int bitCount; | 位圖像素的bits (8BIT,16BIT,24BIT,32BIT) |
| int size; | 紋理數據的大小 |
| PALETTEENTRY paletteentry; | 位圖調色板 |
| GLuint ID; | 紋理ID |
| PBYTE images[32]; | mipmap紋理鏈 |
| int count; | 紋理鏈個數 |
| }TEXTURE, *TEXTURE_PTR; |
紋理映射代碼示例
1.啟用深度緩衝測試,可保證多邊形被正確繪製
glEnable(GL_DEPTH_TEST);
2.啟動漸變效果
glShadeModel(GL_SMOOTH);
3.啟動多邊形隱面裁剪(消除隱藏面)如果要穿越實體則無需啟動
glEnable(GL_CULL_FACE);
4.設背面為隱面
glCullFace(GL_BACK);
5.多邊形正面使用逆時針
glFrontFace(GL_CCW);
6.啟用2D紋理映射
glEnable(GL_TEXTURE_2D);
7.載入位圖紋理(.bmp圖檔)( .tga圖檔)
TEXTURE texture;
Load_File_Texture(&texture,path);
8.生成1個紋理
glGenTextures(1, &texture->ID);
9.綁定紋理
glBindTexture(GL_TEXTURE_2D, texture->ID);
10.放大紋理像素採樣最接近中心紋理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
11.縮小紋理過濾像素採樣最接近中心紋理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
12.載入紋理
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture->width, texture->height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture->image[0]);
從指定的磁盤路徑載入紋理函式
bool Load_File_Texture(TEXTURE_PTR texture,const char * path)
{
char drive[_MAX_DRIVE] = { 0 };// 驅動器盤符
char dir[_MAX_DIR] = { 0 }; // 目錄
char fname[_MAX_FNAME] = { 0 };// 文件名
char ext[_MAX_EXT] = { 0 }; // 擴展名!
BITMAP_FILE bitmap;
TARGA_FILE targa;
PCX pcx;
if (texture == NULL || path == NULL)
return false;
//將路徑名分拆為組件!
_splitpath(path, drive, dir, fname, ext);
if (stricmp(ext, “.bmp”) == 0)// 載入位圖
{
Load_Bitmap(&bitmap, path);// 載入BMP文檔
Load_Bitmap_Texture(texture, &bitmap);// 載入紋理
}
else
if (stricmp(ext, “.tga”) == 0)// 載入位圖
{
Load_Targa(&targa, path); // 載入tga文檔
Load_Targa_Texture(texture, &targa);// 載入紋理
}
else
if (stricmp(ext, “.pcx”) == 0)// 載入位圖
{
Load_PCX(&pcx, path); // 載入PCX文檔
Load_PCX_Texture(texture, &pcx);// 載入紋理
}
return false;
}
激活紋理
void Bind_Texture(TEXTURE_PTR texture){
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture->ID);// 綁定紋理
}

近日將WinXP升為Win10,將VC6升為VS2016.按Menu後在WindowProc()會接收到兩個重複的WM_COMMAND Message.而Accelerator更會接收多個WM_COMMAND Message.同樣的代碼在WinXP和VC6重未出現.不斷查看Winmain()事件循環.因為遊戲引擎是實時驅動而非消息驅動的.所以使用PeekMessage ()而非GetMessage()讀取消Message.當改為while(GetMessage (&msg, NULL, 0, 0))消息驅動後問題無在出現.梯來是MSG這個Message沒有被清空道至.調用memset(&msg, 0, sizeof(MSG)); 問題得到完美解決.
主事件循環代碼簡介
1.WINDOWS消息的存儲器
MSG msg;
2.Accelerator加速鍵表的句柄
HACCEL hAccel;
hAccel =::LoadAccelerators(main_instance,”ACCEL”);
2.主事件循環非消息驅動的
while(true) {
3.清空MSG
memset(&msg, 0, sizeof(MSG));
4.從事件對列中獲得消息
PeekMessage(&msg,NULL,0,0,PM_REMOVE);
5.由 PostQuitMessage(0) 發送的WM_QUIT消息,被PeekMessage()檢測到跳出主循環
if(msg.message == WM_QUIT)
break;
6.處理加速鍵表
if(!::TranslateAccelerator(main_window,hAccel,&msg)) {
7.處理和轉換加速鍵.
TranslateMessage(&msg);
3.調用WinProc對消息進行處理,從MSG結構取的參數並傳遞.
DispatchMessage(&msg);
}
}

『Targa』與『Bitmap』同樣支持RLE編碼,單編碼方式有點不同.而且『索引、RGB、灰度』均支持RLE編碼.RLE編碼無非兩種方式『重複』與『不重複』的像素.渲染演示程式下載:
| 讀取ID | 簡介 |
| if(ID>=128)
Len = (BYTE)(ID – 127); |
重複像素
LEN像素量 |
| if(ID<128)
Len = BYTE(ID + 1); |
不重複的像素
LEN=像素量 |
RLE解碼的C代碼
image:輸出
data:輸入RLE圖像數據
image_size:image圖像數據長度
pixel_size:像素大細
void Load_RLE_Targa(PBYTE image, PBYTE data, int image_size,int pixel_size)
{
BYTE id;//重複像素ID>=128,不重複的像素ID<128
BYTE length; //像素個數
BYTE pixel[4] = {0,0,0,0};// 像素
int image_index = 0;//圖像索引
int data_index = 0;//RLE索引
while (image_index < image_size)
{
id = data[data_index++];// 讀取ID
if (id >= 128)// 重複的像素
{ // 像素個數
length = (BYTE)(id – 127);//像素個數
memcpy(pixel, &data[data_index], pixel_size); // 讀重複像素
data_index = data_index + pixel_size;//移動RLE索引
while (length > 0)// 重複寫入數據
{
memcpy(image+image_index, pixel, pixel_size);//重複寫入數據
image_index = image_index + pixel_size;//移動圖像索引
–length;// 個數減一
}
}
else
{ // 不重複的像素
length = BYTE(id + 1); // 像素個數
// 拷貝像素
memcpy(image+image_index, data+data_index, length*pixel_size);
image_index = image_index + length * pixel_size;// 移動圖像索引
data_index = data_index + length * pixel_size; //移動RLE索引
}
}
}

『Targa』是常用於3D紋理的『.tga』圖檔,它與『Bitmap』最大的分別是索引、RGB、灰度均支持RLE編碼,當色彩較單調時壓縮效果明顯. 渲染演示程式下載:
它的文檔結構主要由三部份組成:
| Targa圖檔結構 | 簡介 |
| HEADER header; | 頭部 |
| PALETTEENTRY palette[256]; | 調色板常用於256色模式,灰度模式和RGB模式均無調色板 |
| PBYTE buffer; | 位圖數據 |
| 頭部結構 | 簡介 |
| BYTE imageIDLength; | 圖像頭部的長度 |
| BYTE colorMayType | 調色板類型
0=無 1=使用調色盤 |
| BYTE imageTypeCode | 圖像類型
0=無圖像數據 1=索引模式 2= RGB模式 3=灰度模式 9=RLE壓縮索引模式 10=RLE壓縮RGB模式 11=RLE壓縮灰度模式 |
| WORD colorMapOrigin | 調色板偏移量 |
| WORD colorMapLength | 調色板的長度8bit圖檔這個值為256 |
| BYTE colorMapEntrySize | 調色板單個條目的大細
有本書居然寫錯左所占空間大細暈 |
| WORD imageXOrigin | 圖像左下角的X軸座標總為0 |
| WORD imageYOrigin | 圖像左下角的Y軸座標總為0 |
| WORD imageWidth | 圖寬 |
| WORD imageHeight | 圖高 |
| BYTE bitCount | 像素8BIT,16BIT,24BIT,32BIT |
| BYTE imageDescriptor | 圖像原點的位置 |
| 調色板結構 | 簡介 |
| BYTE red; | 紅色 |
| BYTE green; | 綠色 |
| BYTE blue; | 藍色 |
載入並分釋TARGA圖檔
bool Load_Targa(TARGA_FILE_PTR targa, PBYTE data,int size)
{
int index;
PBYTE image;
int image_size;// 圖像字節的長度
int pixel_size;// 像素大小
int pixel_count;// 像素個數(寬*高)
memcpy(&targa->header, data, sizeof(TARGA_HEADER));// 讀取頭部數據
image_size = targa->header.imageWidth * targa->header.imageHeight * targa->header.bitCount / 8;
pixel_size = targa->header.bitCount / 8;
pixel_count = targa->header.imageWidth * targa->header.imageHeight;
targa->buffer = (PBYTE)malloc(image_size);// 根據位圖影像的大小申請空間
if (targa->buffer == NULL)
return false;//出錯返回
if (targa->header.imageTypeCode == TARGA_TYPE_INDEXED ||
targa->header.imageTypeCode == TARGA_TYPE_INDEXED_RLE )// 壓縮索引
image = data + sizeof(TARGA_HEADER) + targa->header.imageIDLength + targa->header.colorMapOrigin + (targa->header.colorMapEntrySize / 8) * targa->header.colorMapLength;
else
image = data + sizeof(TARGA_HEADER) + targa->header.imageIDLength ;
if( targa->header.imageTypeCode == TARGA_TYPE_INDEXED ||// 索引
targa->header.imageTypeCode == TARGA_TYPE_RGB ||// RGB
targa->header.imageTypeCode == TARGA_TYPE_GRAYSCALE)// 灰度
{ // 讀取位圖的圖像
memcpy(targa->buffer, image, image_size);
}
else
if (targa->header.imageTypeCode == TARGA_TYPE_INDEXED_RLE ||// 壓縮索引
targa->header.imageTypeCode == TARGA_TYPE_RGB_RLE ||// 壓縮RGB
targa->header.imageTypeCode == TARGA_TYPE_GRAYSCALE_RLE )// 壓縮灰度
{ // RLE解碼
Load_RLE_Targa(targa->buffer, image, image_size, pixel_size);
}
if (targa->header.bitCount == 8)
{ // 計算調色板的入口地址
PBYTE palette = data + sizeof(TARGA_HEADER) + targa->header.imageIDLength + targa->header.colorMapOrigin;
// RGBQUAD結構與PALETTEENTRY結構的順序調轉了
for (index = 0; index < targa->header.colorMapLength; index++)
{//掉轉調色板的紅色和綠色
targa->palette[index].red = palette[index *targa->header.colorMapEntrySize/8 + 2];
targa->palette[index].green = palette[index *targa->header.colorMapEntrySize/8 + 1];
targa->palette[index].blue = palette[index *targa->header.colorMapEntrySize/8 + 0];
targa->palette[index].flags = PC_NOCOLLAPSE;
}
PBYTE temp_buffer = targa->buffer;
//根據圖像的寬高計算記憶體空間(24BIT)
targa->buffer = (UCHAR *)malloc(targa->header.imageWidth * targa->header.imageHeight * 3);
if (targa->buffer == NULL)
return false;//出錯返回
for (index = 0; index < image_size; index++)
{ // 現在將索引值轉為24位值
int color = temp_buffer[index];
if (targa->header.imageTypeCode == TARGA_TYPE_GRAYSCALE ||
targa->header.imageTypeCode == TARGA_TYPE_GRAYSCALE_RLE)
{// 處理灰度圖像
targa->buffer[index * 3 + 0] = color;
targa->buffer[index * 3 + 1] = color;
targa->buffer[index * 3 + 2] = color;
}
else
{
targa->buffer[index * 3 + 0] = targa->palette[color].red;
targa->buffer[index * 3 + 1] = targa->palette[color].green;
targa->buffer[index * 3 + 2] = targa->palette[color].blue;
}
}
targa->header.bitCount = 24;//最後將位圖位數變為24位
}
else
if (targa->header.bitCount == 16)//RGB555
{ // 根據位圖影像的大小申請空間
PBYTE temp_buffer = targa->buffer;
// 根據位圖影像的大小生請空間(位圖為16位但要生成24位空間來保存)
targa->buffer = (UCHAR *)malloc(targa->header.imageWidth * targa->header.imageHeight * 3);
if (targa->buffer == NULL)
{//分配內存空間失敗
free(temp_buffer); // 釋放資源
return false;//出錯返回
}
for (index = 0; index < pixel_count; ++index)
{
WORD color = (temp_buffer[index2 + 1] << 8) | temp_buffer[index2 + 0];
UCHAR red = (((color) >> 10) & 0x1f);
UCHAR green = (((color) >> 5) & 0x1f);
UCHAR blue = ((color) & 0x1f);
targa->buffer[index * 3 + 0] = (red << 3);
targa->buffer[index * 3 + 1] = (green << 3);
targa->buffer[index * 3 + 2] = (blue << 3);
}
targa->header.bitCount = 24;//最後將位圖位數變為24位
free(temp_buffer); // 釋放資源
}
else
if (targa->header.bitCount == 24)
{
for (index = 0; index < image_size; index = index + 3)
{
UCHAR blue = targa->buffer[index + 0];
UCHAR green = targa->buffer[index + 1];
UCHAR red = targa->buffer[index + 2];
targa->buffer[index + 0] = red ;
targa->buffer[index + 2] = blue;
}
}
else
if (targa->header.bitCount == 32)
{
for (index = 0; index < image_size; index = index + 4)
{
//DWORD color;//32的顏色
UCHAR blue = targa->buffer[index + 0];
UCHAR green = targa->buffer[index + 1];
UCHAR red = targa->buffer[index + 2];
targa->buffer[index + 0] = red;
targa->buffer[index + 2] = blue;
}
}
// 判斷圖像原點是否左下角,否則翻轉圖像
if ((targa->header.imageDescriptor & TARGA_ORIGIN_TOP_LEFT) == TARGA_ORIGIN_TOP_LEFT)
Flip_Targa(targa->buffer, targa->header.imageWidth * pixel_size, targa->header.imageHeight);
return true;//
}
將顛倒的圖像翻轉過來
image:指向位圖數據
bytes_per_line:圖像每行所占的字節數
height:圖像的高度
bool Flip_Targa(UCHAR *image, int bytes_per_line, int height)
{
UCHAR *buffer; //用於臨時保存位圖數據.
int index; //循環計數
//根據位圖影像的大小生請空間
buffer = (UCHAR )malloc(bytes_per_lineheight);
if (buffer == NULL)
return false;
// 位圖拷貝
memcpy(buffer, image, bytes_per_line*height);
// 垂直顛倒圖片
for (index = 0; index < height; index++)
memcpy(&image[((height – 1) – index)bytes_per_line], &buffer[indexbytes_per_line], bytes_per_line);
//釋放臨時空間
free(buffer);
return true;//返回
}

『Bitmap』若是8Bit圖檔則支持RLE編碼(run length encoding),但網絡上大多解釋器都不支持RLE解碼,實現它非常間單,但若顏色較多很可能壓縮後的尺寸比不壓縮之前還要大. Bitmap文檔的『compression』等於1則使用RLE8編碼: 渲染演示程式下載:
| 數值 | 簡介 |
| 00 00 | 本行結尾,座標移至下一行頭部 |
| 00 01 | 位圖結束完成解碼 |
| 00 02 x y | 第三第四字節分別當前位置的X與Y偏移量 |
| 00 len val val val 00 | 非壓縮不重複數據,此數值長度要以2對齊,不足以0補齊
若數值為:00 03 BB CC DD 00 則解壓後:BB CC DD |
| len val | 壓縮的重複數據
若數值為:04 88 則解壓後:88 88 88 88 |
BMP-8Bit模式的RLE解碼
image:輸出
data:輸入RLE圖像數據
data_size:RLE圖像數據長度
void Load_RLE8_Bitmap(PBYTE image, PBYTE data, int data_size, int width)
{
BYTE value;
BYTE length;// 像素個數
int image_index = 0;
int data_index = 0;
int x = 0;
int y = 0;
while (data_index < data_size)// 遍歷壓縮後數據
{
if (data[data_index] == 0 && data[data_index + 1] == 0)
{// 本行結尾
data_index = data_index + 2;
x = 0;
++y;
}
else
if (data[data_index] == 0 && data[data_index + 1] == 1)
{// 位圖結尾
data_index = data_index + 2;
return ;
}
else
if (data[data_index] == 0 && data[data_index + 1] == 2)
{// 當前位置的偏移
x = x + data[data_index + 2];
y = y + data[data_index + 3];
data_index = data_index + 4;
}
else
if (data[data_index] == 0)
{// 非壓縮不重複數據
length = data[data_index+1];
image_index = y * width + x;
memcpy(image + image_index, data+data_index+2, length);
x = x + length;
data_index = data_index + length + 2 + length%2;
}
else
if(data[data_index] > 0)
{// 壓縮的重複數據
length = data[data_index];
value = data[data_index + 1];
image_index = y * width + x;
memset(image + image_index, value, length);
x = x + length;
data_index = data_index + 2;// 重複的像素
}
}
}

『Bitmap』圖檔之副檔名使用『.bmp』它非常簡單易讀,記得在2005年學DirextX時寫圖檔分析器就是它.缺點不支持壓縮.8Bit(256色)支持RLF壓縮但只有色彩單調時才有效果否則文檔更大. 不要以為256色已經淘汰,通過更換調色板的顏色可以快速更換顏色依然大有用處.渲染演示程式下載:
BMP文檔由四部分組成:
| 『Bitmap』文檔結構 | 簡介 |
| FILE_HEADER file; | 圖檔的頭部 |
| INFO_HEADER info; | 圖檔的信息 |
| PALETTEENTRY palette[256]; | 調色板只用於256色.
16Bit、24Bit、32Bit均無調色板 |
| PBYTE buffer; | 圖像數據
要在OpenGL中渲染像素『Pixel』要倒轉排成RGB/RGBA順序 |
| FILE_HEADER文檔結構 | 簡介 |
| WORD type; | ‘MB’ 0x4d42 BMP文檔標記
用於判斷是否BMP文檔 |
| DWORD size; | 文檔大小,判斷文檔是否完整 |
| WORD reserved1; | 保留 |
| WORD reserved2; | 保留 |
| DWORD OffBits; | 圖像數據的偏移量(文檔的頭部) |
| INFO_HEADER圖檔信息頭部 | 簡介 |
| DWORD size; | 圖檔信息頭部的大小 |
| LONG width; | 圖檔寬度像素『Pixel』 |
| LONG height; | 圖檔高度像素『Pixel』 |
| WORD planes; | 平面量,總為1 |
| WORD bitCount; | 位圖像素尺寸:
8Bit(256色)支持RLF壓縮 16BIT(分為RGB555與RGB565), 24BIT, 32BIT |
| DWORD compression; | 壓縮類型:
0 = RGB 0 = RGB555 0x0RRRRRGGGGGBBBBB 3 = RGB565 0xRRRRRGGGGGGBBBBB 1 = RLE8 (run length encoding)壓縮 2 = RLE4 |
| DWORD sizeImage; | 圖檔數據所占空間,若使用RLE壓縮為壓縮後的大細 |
| LONG XPelsPerMeter; | X軸每米像素 |
| LONG YPelsPerMeter; | Y軸每米像素 |
| DWORD ClrUsed; | 圖像顏色量 |
| DWORD ClrImportant; | 圖像重要顏色量 |
| PALETTEENTRY調色板 | 簡介 |
| BYTE red; | 紅色 |
| BYTE green; | 綠色 |
| BYTE blue; | 藍色 |
| BYTE flags | 只用於DirectDraw
PC_EXPLICIT:映射到硬件 PC_NOCOLLAPSE:不要映射 PC_RESERVED:保留 |
載入BMP位圖C代碼
bool Load_Bitmap(BITMAP_FILE_PTR bitmap,PBYTE data,int size)
{
int index;
int line_size;// 圖像每行所占的字節數
int pixel_size ;// 像素大小
PBYTE image;
int width ;// 圖寬
int height;// 圖高
// 讀取頭部數據
memcpy(&bitmap->header, data, sizeof(BITMAP_FILE_HEADER));
// 判斷是否是位圖文件
if (bitmap->header.type != BITMAP_ID)
return false;//出錯返回
// 讀取位圖信息的頭部
memcpy(&bitmap->info, data + sizeof(BITMAP_FILE_HEADER), sizeof(BITMAP_INFO_HEADER));
if (bitmap->info.sizeImage == 0)
{
bitmap->info.sizeImage = size – sizeof(BITMAP_FILE_HEADER) – sizeof(BITMAP_INFO_HEADER);
if (bitmap->info.bitCount == 8)
bitmap->info.sizeImage = bitmap->info.sizeImage – MAX_COLORS_PALETTE * sizeof(BITMAP_PALETTEENTRY);
}
//定位圖像數據
image = data + size – (int)bitmap->info.sizeImage;// 相對於文件尾
line_size = bitmap->info.sizeImage / bitmap->info.height;// 圖像每行所占的字節數
pixel_size = bitmap->info.bitCount / 8;// 像素大小
width = bitmap->info.width;// 圖寬
height = abs(bitmap->info.height);// 圖高
// 讀取位圖8或16,32位圖
if (bitmap->info.bitCount == 8)
{ // 讀取位圖的調色板
PBYTE palette = data + sizeof(BITMAP_FILE_HEADER) + sizeof(BITMAP_INFO_HEADER);
// RGBQUAD結構與PALETTEENTRY結構的順序調轉了
for (index = 0; index < MAX_COLORS_PALETTE; index++)
{//掉轉紅色和綠色
bitmap->palette[index].red = palette[index * 4 + 2];
bitmap->palette[index].green = palette[index * 4 + 1];
bitmap->palette[index].blue = palette[index * 4 + 0];
bitmap->palette[index].flags = PC_NOCOLLAPSE;
}
//根據位圖影像的大小生請空間
bitmap->buffer = (UCHAR *)malloc(abs(bitmap->info.width * bitmap->info.height * 3));
if (bitmap->buffer == NULL)
return false;//出錯返回
PBYTE buffer = NULL;
if (bitmap->info.compression == BITMAP_COMPRESSION_RLE8)
{
buffer = (PBYTE)malloc(abs(bitmap->info.width * bitmap->info.height));
Load_RLE8_Bitmap(buffer, image, bitmap->info.sizeImage, bitmap->info.width);// RLE解碼
image = buffer;
}
// 現在將索引值值轉為24BIT
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
index = y * width + x;
int color = image[index];
bitmap->buffer[index * 3 + 0] = bitmap->palette[color].red;
bitmap->buffer[index * 3 + 1] = bitmap->palette[color].green;
bitmap->buffer[index * 3 + 2] = bitmap->palette[color].blue;
}
}
if (bitmap->info.compression == BITMAP_COMPRESSION_RLE8)
free(buffer);
//最後將位圖位數變為24位
bitmap->info.bitCount = 24;
}
else
if (bitmap->info.bitCount == 16)// 讀取16位圖
{ // 以24BIT分配記憶體空間
bitmap->buffer = (UCHAR *)malloc(abs(bitmap->info.width * bitmap->info.height * 3));
if (bitmap->buffer == NULL)
return false;//出錯返回
if (bitmap->info.compression == 3)
{// RGB565
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{// 現在將各個16位RGB值轉為32位值
index = y * line_size + x * 2;
WORD color = (image[index + 1] << 8) | image[index + 0];
UCHAR red = ((color >> 11) & 0x1f);
UCHAR green = ((color >> 5) & 0x3f);
UCHAR blue = (color & 0x1f);
index = y * width + x;
bitmap->buffer[index * 3 + 0] = (red << 3);
bitmap->buffer[index * 3 + 1] = (green << 2);
bitmap->buffer[index * 3 + 2] = (blue << 3);
}
}
}
else
{// RGB555
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
index = y * line_size + x * 2;
WORD color = (image[index + 1] << 8) | image[index + 0];
UCHAR red = (((color) >> 10) & 0x1f);
UCHAR green = (((color) >> 5) & 0x1f);
UCHAR blue = ((color) & 0x1f);
index = y * width + x;
bitmap->buffer[index * 3 + 0] = (red << 3);
bitmap->buffer[index * 3 + 1] = (green << 3);
bitmap->buffer[index * 3 + 2] = (blue << 3);
}
}
}
//最後將位16BIT變為24BIT
bitmap->info.bitCount = 24;
}
else
if (bitmap->info.bitCount == 24)// 讀取24BIT圖檔
{ // 根據位圖影像的大小申請空間
bitmap->buffer = (UCHAR *)malloc(bitmap->info.sizeImage);
if (bitmap->buffer == NULL)// 申請內存空間失敗
return false;//出錯返回
// 讀取圖像
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{// 轉換GL_RGB模式
index = y * line_size + x * 3;
bitmap->buffer[index + 0] = image[index + 2];//逐個像素地拷貝
bitmap->buffer[index + 1] = image[index + 1];
bitmap->buffer[index + 2] = image[index + 0];
}
}
}
else
if (bitmap->info.bitCount == 32)// 處理32BIT圖檔
{ // 根據位圖影像的大小申請空間
bitmap->buffer = (UCHAR *)malloc(bitmap->info.sizeImage);
if (bitmap->buffer == NULL)//若不能申請空間
return false;//出錯退出
// 像素轉為BGRA 32Bit肯定是4字節對齊
for (index = 0; index < (int)bitmap->info.sizeImage-4; index=index+4)
{
bitmap->buffer[index + 0] = image[index + 2];//逐個像素地拷貝
bitmap->buffer[index + 1] = image[index + 1];
bitmap->buffer[index + 2] = image[index + 0];
bitmap->buffer[index + 3] = image[index + 3];
}
}
else
{
return false;//嚴重文提
}
if (bitmap->info.height < 0)// height為負時表示圖片顛倒
Flip_Bitmap(bitmap->buffer, bitmap->info.width*(bitmap->info.bitCount / 8), bitmap->info.height);
bitmap->info.height = abs(bitmap->info.height);
return true;
}
將顛倒的BMP文件翻轉過來
bool Flip_Bitmap(UCHAR *image, int bytes_per_line, int height)
{
UCHAR *buffer; //用於臨時保存位圖數據.
int index; //循環計數
//分配單行空間
buffer = (UCHAR )malloc(bytes_per_lineheight);
if (buffer == NULL)
return false;
// 單行拷貝
memcpy(buffer, image, bytes_per_line*height);
// 垂直顛倒圖片
for (index = 0; index < height; index++)
memcpy(&image[((height – 1) – index)bytes_per_line], &buffer[indexbytes_per_line], bytes_per_line);
//釋放空間
free(buffer);
return true;//返回
}

在屏幕上渲染圖檔與模型貼上紋理總會令人興奮,幸好在OpengGL繪畫圖像文檔也並不困難,並且實現左示例程式.下載:
| Format | 簡介 |
| GL_RGB | 24Bit |
| GL_RGBA | 32Bit |
| GL_COLOR_INDEX | 調色板索引 |
| type | 簡介 |
| GL_UNSIGNED_BYTE | 像素『Pixel』分量的尺寸 |
| GL_BITMAP | 點陣位圖1位『Bit』1像素『Pixel』 |
| 常見圖檔格式讀取與分析 | RLE解碼 |
| Bitmap文檔 | 8Bit的bitmap支持RLE編碼 |
| Targa圖檔 | 索引模式、RGB模式、灰度模式均支持RLE編碼 |
| PCX圖檔 | 8Bit的PCX支持RLE編碼 |
| PNG圖檔 |
渲染位圖C代碼演示:

之前一值無法使用ALT鍵,因查找唔到其VK_ALT對應的『VIRTUAL CODE』,近日把WinXP切底拋棄改用Win10,發現很多程式均使用ALT鍵展示或隱藏MENU. 細想VK_ALT即等於VK_MENU,下面是使用ALT鍵展示與隱藏MENU源代碼
1.在resource.h加入
#define IDALT 1000
2.在resource.rc加入加速表,一定要加入ALT否則MENU會獲得焦點要按量下ALT鍵,另不要用VK_LMENU和VK_RMENU
ACCEL ACCELERATORS
BEGIN
VK_MENU, IDALT, VIRTKEY, ALT, NOINVERT
END
3.在main()修改消息循環
MSG msg;//消息 msg是WINDOWS放置下一個消息的存儲器,
HACCEL hAccel; // 加速鍵表的句柄
hAccel =::LoadAccelerators(main_instance,”ACCEL”); // 加速鍵表的句柄
while(GetMessage(&msg,NULL,0,0)) //從事件對列中獲得消息
{ //由 PostQuitMessage(0) 發送的WM_QUIT消息,被PeekMessage()檢測到
if(msg.message == WM_QUIT)
break;// 跳出主循環
if(!::TranslateAccelerator(main_window,hAccel,&msg))// 處理加速鍵表
{
TranslateMessage(&msg);//處理和轉換加速鍵.
DispatchMessage(&msg);//調用WinProc對消息進行處理,從MSG結構取的參數並傳遞.
}
}
4.在WinProc加入
if(msg == WM_COMMAND)
{ // 處理菜單命令
if (LOWORD(wParam) == IDALT)
{ main_menu = ::LoadMenuA(NULL,”MAIN_MENU”);// 主菜單
if (::GetMenu(main_window) == NULL)
::SetMenu(main_window, main_menu);// 設定菜單
else
::SetMenu(main_window, NULL);// 設定菜單
}
}


近日終於遠離最愛的VC6安裝VC2017,新增專案時居然無發現C/C++的選項,暈難道C已被拋棄?經一番鑽然才悟個中方法

在OpenGL輸出文字可用繪畫好的文本位圖,再繪畫上屏幕.也是遊戲製作通用手法.將基本ASCII文本存為16Bit*16Bit(32Byte)二進制點陣字體
指定位圖的繪畫位置
void glRasterPos2i(GLint x, GLint y);
繪畫位圖
void WINAPI glBitmap(
GLSizei width, GLSizei height, ASCII文本的寬和高這裡均為16
GLfloat xorig, GLfloat yorig,當前繪畫位置的偏移
GLfloat xmove, GLfloat ymove, 下次繪畫位置的增量
const GLubyte *bitmap); 二進制點陣字體
以位畫字符B的函式為例:
點陣字體的程序示例:下載

『Windows10 Update Assistant』又稱為『Windows 10 更新小幫手』會自動彈出下載並安裝更新.但更新後卻把我的『港版Win10』變為『台版Win10』更新時還不能保留原有程式.最煩的是經常自動彈出.即使卸載Uninstall後還是會自動安裝. 經一番研究只禁『Windows10 Update Assistant』自動彈出,而『Windows10 Update』仍在後臺自動更新系統

Windows Defender是Windows10內置免費的既時保護查找惡意程式,但它卻經常錯報誤報影響日常工作,而且暫用大量資源,需然可然暫時關閉既時保護.『設定/更新與安全性/Windows Defender/既時保護/關閉』但更新病毒資料庫後又會開啟,你需要停止系統服務


記憶體(RAM)容量對計算機性能起直接的影響,而另一個關鍵參數記憶體(RAM)帶寬(Bandwidth) ,帶寬越高與CPU之間交換數據更快,更有利提高計算機性能.
計算公式如下:
帶寬(Bandwidth)=工作頻率(DRAM Frequency)*位寬(DataWidth)
| 公式 | 簡介 | |
| 工作頻率Frequency | 工作頻率=記憶體頻率*通道數量 | 相當於車速 |
| 位寬(DataWidth) | SDRAM、DDR和DDR2、DDR3、DDR4的總線位寬均為64位 | 相當於路寬 |
可以通過命令行模式輸入『wmic memorychip』查看記憶體(RAM)的所有參數如上圖
| ConfiguredClockSpeed | DataWidth | Voltage | Speed | TypeDetail |
| 時鐘速度 | 位寬 | 電壓 | 工作頻率 | 記憶體容量 |
以DDR4-2400為例:
工作頻率:2400 MHz
位寬:64Bit=8Byte
例計算DDR4-2400記憶體(RAM)帶寬
2400MHz*64bit/8=19200 Mbyte/s=PC19200
例計算DDR400記憶體(RAM)帶寬
400MHz*64bit/8=3200Mbyte/s=PC3200

SolidWorks安裝後是默認是英文版,若想更改為正體字,但在Options卻沒有設定語言選項,因為SolidWorks是跟據Windows的地區格式(註冊表)而顯示語言
若想更改為英文版則更簡單無需獨立安裝語言包






SATA端口固態硬盤(Solid State Drives)大多為2.5寸.可直接在筆記本電腦上使用.但在臺式機使用3.5寸硬碟.有些廠家會附帶有2.5寸轉3.5寸硬碟托架,但有些廠家就無提供.我地可以通過3D打印機做出硬碟托架.另需要準備六粒3mm六角螺母.產品的效果比在網上買的金屬版還要好





安裝電源
連接機箱前置面板插頭
| JFP1針腳 | 簡介 | JFP1針腳 | 簡介 |
| 1 | HDD LED+硬碟燈正極(紅色) | 2 | Power LED+電源燈正極(紅色) |
| 3 | HDD LED-硬碟燈負極 | 4 | Power LED-電源燈負極 |
| 5 | Reset Switch
重置開關 |
6 | Power Switch
電源開關 |
| 7 | 8 | ||
| 9 | Reserved(保留腳) | 10 | No Pin(無針腳) |
| JFP2針腳 | 簡介 | JFP2針腳 | 簡介 |
| 1 | Speaker-負極 | 2 | Buzzer-負極 |
| 3 | Buzzer-負極 | 4 | Speaker+正極(紅色) |
設定BIOS


本想購買華碩(Asus)PRIME B350-PLUS但細梯之下此主板尺寸是30.5CM23.7CM(6個鑼絲固定孔)非標準ATX大板,而華碩稍貴點的主板尺寸都是30.4CM24.3CM(9個鑼絲固定孔)標準ATX大板,光從這點就可梯出華碩小氣.有朋友總是迷信華碩認為它的質量就是最好.但多年經驗告訴我並非這樣.
之後注意到微星(MSI)B350 GAMING PLUS這塊標準的ATX大板.整塊主版以紅黑色設計給人感觀非常好.最重要介面齊全,連舊式的串口和並口都支援(需外接埠).小小失望的是缺小IDE介面,幸好有PCI介面可插IDE擴展卡.但我又怕拖慢開機速度.
| 介面 | 簡介 |
| CPU | 支持AM4封裝AMD RYZEN
我不太注重CPU速度.買左盒裝銳龍1500X/3.5GHz/4核. |
| 晶片組 | AMD B350晶片組 |
| 內存 | 4條DDR4內存插槽雙通道內存構架.
安裝四條單條16GB剛好64GB內存 |
| 擴展插槽 | 一條PCIe3.0x16插槽(需要安裝RYZEN(銳龍)CPU)
一條PCIe2.0x16插槽 兩條PCIe2.0x1插槽 兩條PCI插槽.是我買它最重要原因,可以安裝我的PCI創新聲卡 |
| USB後置面板
|
兩個USB2.0
四個USB3.1 一個USB3.1 TYPE-C 加上內置的USB插口,就無需再使用USB分線器.因為它經常接觸不良 |
| 交叉火力 | 支援安裝兩個相同的AMD顯卡 |
| 存儲 | 四個SATA 6Gb/s埠,安裝兩個硬盤一個DVD光驅剛剛夠用.
一個M.2介面 |



現在的CPU尺寸小而且針腳多.SOCKET AM4就有1331腳.若裝反會損毀CPU.是吾是想起SOCKET-1和SOCKET-A的年代呢.
安裝SOCKET AM4步驟



以前好似無買過盒裝的CPU.都是買散裝CPU外加個散熱器.因為現在網購方便乾脆買個盒裝CPU.本想買個Intel八代酷睿I5但需要300系列主機板.CPU同樣是1151腳.但卻要新的晶片組支援.Intel這種作為實在吾好.
繼而把目光轉向AMD RYZEN(銳龍)5-1500X.擁有4個核芯.基頻3.5GHz而且不鎖頻.就這點就比Intel有誠意.
有很吾識多任務操作系統原理的人認為CPU速度比核芯個數重要.其實並非這樣.每個進程(Process)都有一個主線程(thread).瀏覽器+播放機+圖形介面+後臺任務.分分幾十個進程(Process)等代CPU時間片運行.如果你有多CPU核芯操作系統就會均勻的給CPU核芯分配時片.如果遊戲或程式創建多個線程(thread).更能充分的發繪多核CPU性能.最明顯的是兩核芯CPU一出現就令Windows假死現象消失,因為在單CPU年代如果某個進程嚴重佔用CPU資源,Windows圖形介面就變得很慢很慢.
所以在未有多核CPU出現前.解決辦法是購買雙CPU的主版加兩個CPU.但多核芯系統依然比不上多核芯多CPU系統,因為單個CPU的主版的多個核芯同時間只有單個可以訪問內存(RAM).而多CPU的主版則可以同時訪問內存(RAM).但多核的CPU核芯同時間依然只有單核個可以訪問內存(RAM).但我之前幾塊雙CPU的主版都無法啟動!_!
| 性能 | 產品規格 |
| CPU | AM4介面1331針
AMD RYZEN銳龍1500X不集成顯卡 |
| 核芯 | 4芯核8線程 |
| 頻率 | 基頻3.5GHz最高3.7GHz |
| 緩存 | L2=2MB
L3=16MB |
| 風扇 | 原裝幽靈風扇 |
| 電壓 | 1.264V |
| 基頻(CPU Base Clock) | 100MHZ(基頻只有100超133有D希望) |


在市面上無論原裝機、相容機還是平板電腦.最高我只見過是16GB的記憶體(RAM).用戶採用最強勁CPU最大硬盤.而往往忽略記憶體(RAM)容量嚴重不足的重要性.
在現代操作系統把記憶體(RAM)空間劃分成一頁(PAGE)一頁(PAGE)使用,如果沒有足夠記憶體(RAM)空間,操作系統會把以載入數據的記憶體(RAM)分頁(PAGE)寫入磁盤(DISK),以騰出足夠記憶體(RAM)空間.以免造成記憶體(RAM)溢出.但問題是磁盤(DISK)讀寫比記憶體(RAM)慢內太多.在多個程式(APP)切換時記憶體分頁(RAM PAGE)要在記憶體(RAM)和磁盤(DISK)之間不斷傳送.在瀏覽較大的PDF文檔時記憶體(RAM)不斷分配(malloc)和釋放(free)也會造成很大的延時.現代的網絡瀏覽器耗記憶體(RAM)更為利害,網絡影片再打開若干個網站.而後在各瀏覽頁面切換造成很大的延時. CPU再快都幫唔到你. 所以記憶體(RAM) 係多多益善.
VASEKY雖然是雜牌但勝在價格低廉,而且還裝有散熱器.但記憶體(RAM)發熱量不大對於超頻意義不大.只用作美觀和保護晶片.但比起其它大廠有誠意得多.安裝四條16GB共64GB是我這塊B350主版的極限.
| 參數 | 簡介 |
| DDR4 | 2400MHZ(PC19200) |
| 電壓 | 1.2V |
| 容量 | 單條16GB |


本人對顯卡並無太多要求.能運行SolidWorks與Maya即可以.本想購買的XFX訊景R5-240無貨.而且又擔心4K影片支持.現在顯卡大多都被買去挖礦造成顯卡缺貨.幸好迪蘭恒進(ATALAND)RX550酷能4GB剛剛到貨馬上落單.
| 迪蘭恒進(ATALAND)RX550酷能4G | 簡介 |
| 晶片組 | RX550 |
| 內存 | 128BIT/DDR5/4GB |
| 輸出 | DVI-D/HDMI/DP |
現在的顯卡以4GB顯存起步,回想起我第一塊顯卡只有1MB的顯存.更加認證左.硬體只是過眼雲煙.演算法才能久流傳.
| 3DMARK11 | Entry(E) 1024×600 | Performance(P)1280×720 | Extrema(x)1920X1080 |
| 總分 | E9283 | P6332 | X1398 |
| 圖形分數 | 9527 | 5929 | 1241 |
| 物理分數 | 8958 | 8922 | 8960 |
| 結合分數 | 8390 | 6844 | 1688 |
| GT1 | 38.29幀 | 23.66幀 | 7.16幀 |
| GT2 | 45.03幀 | 26.75幀 | 6.94幀 |
| GT3 | 63.62幀 | 39.09幀 | 6.44幀 |
| GT4 | 30.75幀 | 20.04幀 | 3.31幀 |
| PT | 28.44幀 | 28.32幀 | 28.45幀 |
| CT | 39.03幀 | 31.83幀 | 7.85幀 |
大量的光照對於GPU要求還是有點高,Extrema測試基本吾上10幀.

地形文檔就是256色位圖文檔,因為位圖即點陣圖.點的數值越大地形越高,數值越低形成低窪地帶.最終生成高山、湖水.
地形演示程式:下載
繪畫地形思路如下:
成生地形RAW文檔
繪畫地形的函式示例
void Draw_Terrain (PBYTE data,int height,int width)
{
for(int z=0;z < height-1; ++z)
{
glBegin(GL_TRIANGLE_STRIP);// 繪畫三角形
for(int x=0;x < width; ++x)
{
float scaled_height1 = data[ z * width + x] / (256.0f / 10.0f);
float scaled_height2 = data[(z+1)* width + x] / (256.0f / 10.0f);
glColor3f(0.1f, 0.5f + 0.5f * scaled_height1 / 10, 0.1f);// 山地的顏色,地勢越高綠色分量越大
glVertex3f((x – width/2.0f), scaled_height1,(z – height/2.0f));
glColor3f(0.1f, 0.5f + 0.5f * scaled_height2 / 10, 0.1f);// 山地的顏色,地勢越高綠色分量越大
glVertex3f((x – width/2.0f), scaled_height2, ((z+1) – height/2.0f));
}
glEnd();
}
// 繪畫湖面
glColor3f(0.0, 0.0, 0.8f);// 藍色湖水
glBegin(GL_QUADS);
glVertex3f(-width/2.1f, 1, height/2.1f);
glVertex3f( width/2.1f, 1, height/2.1f);
glVertex3f( width/2.1f, 1, -height/2.1f);
glVertex3f(-width/2.1f, 1, -height/2.1f);
glEnd();
}

Windows程式就是單獨的.exe執行文件.但若程式需要讀取文檔才能運行.可以將其與.exe文件一起放置,但這樣就失去保密性.更好處理方法是將文檔編譯進.exe執行文件.這樣執行程式就無需附帶其它文檔.以VC為例:
把資源文檔編譯進.exe執行文件
| 定義資源文檔 | 簡介 |
| resources.rc
Heightmap RAW “res\\heightmap.raw” |
通過字符串名定義
RAW為類型名 Heightmap為資源名 |
| resource.h
#define ID_MAP 1500 resources.rc ID_MAP RAW “res\\heightmap.raw” |
通過整數標識符定義
ID_MAP為資源ID 也可直接使用數字 |
讀取資源文檔示例
C代碼版本
hInstance:程式的實例句柄
filename:資源文檔名
type:類型名
PBYTE Read_RES(HINSTANCE hInstance,char *filename,char * type)
{// 查找資源
HRSRC hRsrc =::FindResource((HMODULE)hInstance,filename,type);
if(hRsrc == NULL)
return false;
//加載資源
HMODULE hGlobal = (HMODULE)::LoadResource((HMODULE)hInstance,hRsrc);
// 獲取文檔的字節長度
int size =::SizeofResource((HMODULE)hInstance,hRsrc);
// 鎖定資源並獲取指針
PBYTE res = (PBYTE) ::LockResource(hGlobal);
// 分配內存空間
PBYTE data = (PBYTE)malloc(size);
// 拷貝數據
memcpy(data,res,size);
if(hGlobal)
FreeResource(hGlobal); // 釋放資源句柄
return data;
}

煙霧(Fog)使遠距離的物體變暗,近距離的物體變得清晰.是遊戲中常用特效.OpenGL煙霧將每個像素煙霧色進行混合處理.
煙霧演示程式:下載
煙霧函式演示:
OpenGL通過glFog()函式設定的距離、顏色、密度生成煙霧
| 參數 | 簡介 |
| glFogi(GL_FOG_MODE, GL_LINEAR); | 設定混合方程式
GL_LINEAR:線性混合 GL_EXP:指數混合(默認值) GL_EXP2:二次指數指數混合 |
| glFogf(GL_FOG_DENSITY, 1.2f); | 煙霧的單位密度,數值需為正,默認值為1.0 |
| glFogf(GL_FOG_START, start); | 煙霧距離視口的近端距離 |
| glFogf(GL_FOG_END, end); | 煙霧距離視口的遠端距離 |
| glFogi(GL_FOG_INDEX, 0); | 設定煙霧顏色8位調色版索引 |
| GLfloat fogColor[] = { 0.5f, 0.5f, 0.5f };
glFogfv(GL_FOG_COLOR, fogColor); |
設定煙霧顏色(RGB),默認為黑色. |
| glFogi(GL_FOG_COORD_SRC,
GL_FRAGMENT_DEPTH); |
深度值
GL_FRAGMENT_DEPTH:視口與煙霧的距離(默認值) GL_FOG_COORD:使用glFogCoordf();設定煙霧坐標 |
| 混合方程式 | 簡介 |
| Color=blendFactor*in + (1-blendFactor)*fog | 混合方程式 |
| blendFactor | 混合因子 |
| in | 輸入顏色 |
| fog | 煙霧顏色 |
| Color | 輸出顏色 |
| 混合因子方程式 | 簡介 |
| GL_LINEAR | 線性混合
blendFactor=(end-depth)/(end-start) |
| GL_EXP | 指數混合(默認值)
blendFactor= EXP (e, -density*depth) |
| GL_EXP2 | 二次指數指數混合
blendFactor= EXP(EXP (e, -density*depth),2) |

Windows菜單可以通過實現單選(點選)與多選(勾選)展現當前狀態.你無需更改MENU結構,只需運行時更改菜單項MENUITEM的標記.
| 菜單多選與單選(函式示例) | 簡介 |
| HMENU menu = GetSubMenu(main_menu,1); | 獲取子菜單句柄 |
| CheckMenuRadioItem(
menu, ID_FIRST, ID_LAST, ID_CHECK, MF_BYCOMMAND); |
單項選擇
ID_FIRST第一個菜單 ID_LAST最後一個菜單ID ID_CHECK單選(點選)菜單ID
|
| CheckMenuItem(menu,
ID_CHECK, MF_BYCOMMAND|MF_CHECKED); |
選擇多個菜單項(多項選擇) |
| CheckMenuItem(menu,
ID_CHECK, MF_BYCOMMAND|MF_UNCHECKED); |
取消選擇菜單項(多項選擇) |

在OpenGL之顏色混合alpha通過glBlendFunc()設定的混合因子生成透明效果,但通過其它的混合因子可產生更多不同的混合效果.令外OpenGL的顏色混合是無需啟用光照的glEnable(GL_LIGHTING); 但需要啟動多邊形隱面裁剪::glEnable(GL_CULL_FACE);否則會有一面會繪畫錯誤.
混合因子演示程式.下載
| glBlendFunc()的混合公式 | 簡介 |
| GL_FUNC_ADD | C =(Cs*S)+(Cd*D) 默認值 |
| GL_MIN | C =MIN(Cs,Cd) |
| GL_MAX | C =MAX(Cs,Cd) |
| GL_FUNC_SUBTRACT | C =(Cs*S)-(Cd*D) |
| GL_FUNC_REVERSE_SUBTRACT | C =(Cd*S)-(Cs*D) |
| 公式因子 | 簡介:混合因子的簡介可通過最下表查閱 |
| Cs: | 來源顏色 |
| S: | 來源混合因子 |
| Cd: | 目標顏色 |
| D: | 目標混合因子 |
| S來源混合因子 | 簡介 |
| GL_ZERO | sour (r,g,b,a) *{0,0,0,0} |
| GL_ONE | sour (r,g,b,a) * (1,1,1,1) |
| GL_DST_COLOR | sour (r,g,b,a) * dest (r,g,b,a) |
| GL_ONE_MINUS_DST_COLOR | sour (r,g,b,a) * ((1,1,1,1)- dest(r,g,b,a)) |
| GL_SRC_ALPHA | sour (r,g,b,a) * sour(alpha) |
| GL_ONE_MINUS_SRC_ALPHA | sour (r,g,b,a) * (1- sour(alpha)) |
| GL_DST_ALPHA | sour (r,g,b,a) * dest(alpha) |
| GL_ONE_MINUS_DST_ALPHA | sour (r,g,b,a) * (1- dest(alpha)) |
| GL_SRC_ALPHA_SATURATE | sour (r,g,b,a) * MIN(sour(alpha),1-dest(alpha)) |
| GL_CONSTANT_COLOR | sour (r,g,b,a)* Color(r,g,b,a) |
| GL_ONE_MINUS_CONSTANT_COLOR | sour (r,g,b,a)*( (1,1,1,1)- Color(r,g,b,a)) |
| GL_CONSTANT_ALPHA | sour (r,g,b,a)* Color(alpha) |
| GL_ONE_MINUS_CONSTANT_ALPHA | sour (r,g,b,a)*(1-Color(alpha)) |
| D目標混合因子 | 簡介 |
| GL_ZERO | dest (r,g,b,a) *{0,0,0,0} |
| GL_ONE | dest (r,g,b,a) * (1,1,1,1) |
| GL_SRC_COLOR | dest (r,g,b,a) * sour (r,g,b,a) |
| GL_ONE_MINUS_SRC_COLOR | dest (r,g,b,a) * ((1,1,1,1)- sour (r,g,b,a)) |
| GL_SRC_ALPHA | dest (r,g,b,a) * sour (alpha) |
| GL_ONE_MINUS_SRC_ALPHA | dest (r,g,b,a) * (1- sour (alpha)) |
| GL_DST_ALPHA | dest (r,g,b,a) * dest (alpha) |
| GL_ONE_MINUS_DST_ALPHA | dest (r,g,b,a) * (1- dest (alpha)) |
| GL_SRC_ALPHA_SATURATE | dest (r,g,b,a) * MIN(sour(alpha),1-dest(alpha)) |
| GL_CONSTANT_COLOR | dest (r,g,b,a)* Color(r,g,b,a) |
| GL_ONE_MINUS_CONSTANT_COLOR | dest (r,g,b,a)*( (1,1,1,1)- Color(r,g,b,a)) |
| GL_CONSTANT_ALPHA | dest (r,g,b,a)* Color(alpha) |
| GL_ONE_MINUS_CONSTANT_ALPHA | dest (r,g,b,a)*(1-Color(alpha)) |
上表的Color混合常量通過glBlendColor()設定
| 設定混合常量 | 簡介 |
| Void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha; | 混合常量默認為(0,0,0,0)作為混合權重係數 |

顏色混合(alpha)可實現透明的視角效果,可以模擬液體、玻璃等.當你啟動混合OpenGL將輸入源的顏色與和在幀緩存裏的顏色混合
| 函式 | 簡介 |
| glEnable(GL_BLEND); | 啟用混合 |
| glDisable(GL_BLEND); | 禁用混合 |
| 函式 | 簡介 |
| Void glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); | 設定顏色和透明度 |
| red, green, blue | 顏色 |
| alpha | 透明度數值範圍:0.0f-1.0f
0:全透明 1:完全不透明 |
函式示例
| 混合函式 | 簡介 |
| void glBlendFunc (GLenum sfactor, GLenum dfactor); | 設定來源和目標混合係數 |
| sfactor | 輸入源數據的混合係數 |
| dfactor | 當前幀緩存的混合係數 |
| 混合係數 | 簡介 |
| GL_ZERO | 將顏色設為{0,0,0,0} |
| GL_ONE | 不改變當前顏色(r,g,b,a)*(1,1,1,1) |
| GL_SRC_COLOR | 目標與來源相乘dest (r,g,b,a)* sour (r,g,b,a) |
| GL_DST_COLOR | 來源與目標相乘sour (r,g,b,a)* dest (r,g,b,a) |
| GL_ONE_MINUS_SRC_COLOR | (r,g,b,a)*((1,1,1,1)- sour(r,g,b,a)) |
| GL_ONE_MINUS_DST_COLOR | (r,g,b,a)*((1,1,1,1)- dest(r,g,b,a)) |
| GL_SRC_ALPHA | (r,g,b,a) * sour(alpha) |
| GL_DST_ALPHA | (r,g,b,a) * dest(alpha) |
| GL_ONE_MINUS_SRC_ALPHA | (r,g,b,a) * (1- sour(alpha)) |
| GL_ONE_MINUS_DST_ALPHA | (r,g,b,a) * (1- dest(alpha)) |
| GL_SRC_ALPHA_SATURATE | (r,g,b,a) *MIN (sour(alpha),1-dest(alpha)) |
| 函式 | 簡介 |
| glEnable(GL_DEPTH_TEST); | 打開深度檢測(Z軸緩存), 一定要調用.保證光照正確繪製和模形前後正確繪製 |
Alpha演示程式示例,分別有太陽,地球和月球.下載程式

鏡面反射產生閃耀的光輝,此光照效果很有趣,需要分別設置光源和材質.
| 光源和材質 | 簡介 |
| Light_Specular[] = {1.0f,1.0f,1.0f,1.0f};
glLightfv(light,GL_SPECULAR, Light_Specular); |
鏡面反射光顏色{r,g,b,a}分量 |
| Material_Specular[] = {1.0f,1.0f,1.0f,1.0f};
glMaterialfv(GL_FRONT,GL_SPECULAR, Material_Specular); |
材質的鏡面反射光顏色,反射出去的顏色不大於材質顏色
{1.0f,1.0f,1.0f,1.0f}:光照完全反射出去, |
| glMaterialf (GL_FRONT,GL_SHININESS,10); | 鏡面反射光聚焦度
0:不聚焦 128:高度聚焦 |
函式示例
鏡面反射光程式示例,行星發出閃耀的光輝,而衛星繞行星旋轉並且發光,分別使用聚光燈和定點光.但無使用環境光.下載程式

OpengGL為多邊形設置材質屬性,根據紅綠藍RGB的分量而確定反射光顏色:如下表:
| 材質 | 光源的光顏 | 反射光顏色, | 簡介 |
| 綠色(0,1,0) | 白色(1,1,1) | 綠色(0,1,0) | 只反射綠光 |
| 綠色(0,1,0) | 紅色(1,0,0) | 黑色(0,0,0) | 只反射黑光 |
| 材質 | 簡介 |
| void glMaterialf(GLenum face, GLenum pname, GLfloat param); | 為多邊形設置材質屬性用於光照計算,它是全局性的, 影響所有繪製的多邊形,直到在次調用 |
| void glMaterialfv(GLenum face, GLenum pname, const GLfloat *params); | glMaterialf()數組版本 |
| face | 簡介 |
| GL_FRONT | 材質僅應用於多邊形正面 |
| GL_BACK | 多邊形反面 |
| GL_FRONT_AND_BACK | 多邊形的正反面 |
| pname | 數值 | 簡介 |
| GL_AMBIENT | {r,g,b,a} | 材質的環境光顏色 |
| GL_DIFFUSE | {r,g,b,a} | 材質的散射光顏色 |
| GL_AMBIENT_AND_DIFFUSE | {r,g,b,a} | 同時設置材質的環境光和散射光顏色 |
| GL_SPECULAR | {r,g,b,a} | 材質的鏡面反射光顏色,反射出去的顏色不大於材質顏色
{1.0f,1.0f,1.0f,1.0f}:光會完全反射出去 |
| GL_SHININESS | 0-128 | 鏡面反射光指數
0:不聚焦 128:高度聚焦 |
| GL_EMISSION | {r,g,b,a} | 材質的反射光顏色 |
設置多邊形材質的環境光和散射光,函式示例:

聚光燈:在定點光源的基礎加上光線輻射方向,因此光線呈圓錐.
| 聚光燈參數 | 範圍 | 簡介 |
| GL_SPOT_DIRECTION | (x,y,z) | 聚光燈指向/方向3D矢量
默認值:(0,0,-1) |
| GL_SPOT_EXPONENT | 0-128 | 焦點/指數:光線從焦點到圓錐的邊界,光的強度不斷衰減,直到邊界消失.
指數越大焦點越小 指數越小焦點越大 |
| GL_SPOT_CUTOFF | 0.1-90 | 圓錐面與指向軸的角度(圓錐角),所以聚光燈的張角為圓錐角的2倍.
張角=圓錐角*2 |
聚光燈函式使用示例
聚光燈程式示例,可分別設定張角與焦點.下載

法線:即垂直於其表面的單位矢量
OpenGL在進行光照運算前需先計算法線,光與表面相交的角度利用法先計算出反射角度,結合光照與材質計算表面的顏色
| 法線函式 | 簡介 |
| void glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz); | 設置法線
(nx,ny,nz):法線坐標分量 |
法線函式示例
glBegin(GL_POLYGON);
glNormal3f(0.0f,1.0f,0.0f);// 法線
glVertex3f( 0.5f, 0.5f, 0.5f);
glVertex3f( 0.5f, 0.5f,-0.5f);
glVertex3f(-0.5f, 0.5f,-0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glEnd();
| 法線的計算 | 簡介 |
| A*B=(Ax*Bz-Az*By, Az*Bx-Ax*Bz, Ax*By-Ay*Bx) | 矢量A和B叉積方程 |
計算兩個3D向量的法線(叉積)
void Cross(float pa[3], float pb[3], float pc[3],float normal[3])
{
float va[3],vb[3];//矢量1,矢量2
float length;//矢量長度
// 計算矢量1
va[0] = pa[0] – pb[0];
va[1] = pa[1] – pb[1];
va[2] = pa[2] – pb[2];
// 計算矢量2
vb[0] = pb[0] – pc[0];
vb[1] = pb[1] – pc[1];
vb[2] = pb[2] – pc[2];
// 計算叉積
normal[0] = va[1]vb[2] – va[2]vb[1];
normal[1] = va[2]vb[0] – va[0]vb[2];
normal[2] = va[0]vb[1] – va[1]vb[0];
// 計算長度
length = sqrt(normal[0]normal[0] + normal[1]normal[1] + normal[2]*normal[2]);
// 計算單位矢量
normal[0] = normal[0] / length;
normal[1] = normal[1] / length;
normal[2] = normal[2] / length;
}
法線演示程式,旋轉正方體觀看光照的變化如上圖:下載

OpenGL光的顏色由紅綠藍(RGB)的分量確定,當光照到表面時,由表面的材質的確定反射的光的顏色(RGB).
| OpenGL光有四類 | 簡介 |
| 環境光 | 無特定照射方向,太陽光 |
| 漫射光 | 有照射方向的光,遇表面光將朝反方向照射, 如燈光不受相機位置影響. |
| 反射光 | 有照射方向,遇表面光將朝反方向照射.如亮光但受相機位置影響. |
| 放射光 | 只增加物體的亮度 |
| OpenGL光照影響因素 | 簡介 |
| 光源 | 環境光、漫射光、反射光、放射光 |
| 材質 | 確定反射光RGBA值 |
| 法線 | 由表面的頂點計算方向 |
| 相機位置 | 當光折射時, 相機位置影響反射光的計算 |
| 光的顏色、位置、方向的設定: |
| void glLightfv (GLenum light, GLenum pname, const GLfloat *params); |
| glLightfv()參數 | 簡介 |
| light | GL_LIGHT0~ GL_LIGHT7
OpenGL最多可以指定8個光源 |
| pname | 光源的參數名見下表 |
| Params | 光源的參數值 |
| 光源的參數名Pname | 簡介 |
| GL_AMBIENT | 環境光向所有方向照射 |
| GL_DIFFUSE | 漫射光具有方向,碰撞到FACE會進行反射 |
| GL_SPECULAR | 鏡面反射光 |
| GL_POSITION | 光的位置 |
| GL_SPOT_DIRECTION | 聚光燈的矢量方向 |
| GL_SPOT_EXPONENT | 聚光燈的指數 |
| GL_SPOT_CUTOFF | 聚光燈的邊界 |
| GL_CONSTANT_ATTENUATION | 衰減值係數常量 |
| GL_LINEAR_ATTENUATION | 線性衰減值係數 |
| GL_QUADRATIC_ATTENUATION | 二次衰減值係數 |
| 環境光 | 簡介 |
| Pname=GL_AMBIENT | 環境光 |
| Params=(x,y,z,w) | 顏色分量 |
| w=alpha | w=0透明
w=1實體 |
| float Light_Ambient[] = { 1.0f,1.0f,1.0f,1.0f};
glLightfv(GL_LIGHT0,GL_AMBIENT, Light_Ambient); |
亮白的環境光示例 |
| 漫射光 | 簡介 |
| GL_DIFFUSE | 漫射光 |
| Params=(x,y,z,w) | 顏色分量 |
| w=alpha | w=0透明
w=1實體 |
| float Light_Diffuse[] = { 1.0f,1.0f,1.0f,1.0f};
glLightfv(GL_LIGHT0,GL_DIFFUSE, Light_Diffuse); |
亮白的漫射光示例 |
| 光源的位置 | 簡介 |
| Pname= GL_POSITION | 設定光源的位置 |
| Params=(x,y,z,w) | 向量或坐標 |
| w=0 | 定向光源(x,y,z)為向量定義光的照射方向,如太陽. |
| W=1 | 定點光源(x,y,z)為坐標定義光源的位置,如燈泡. |
| float Light_Position[] = {0.0f,0.0f,0.0f,1.0f };
glLightfv(GL_LIGHT0,GL_POSITION,Light_Position); |
光源的位置設在原點 |
| 光衰減(OpenGL支持三類光衰減) | 默認值 | 簡介 |
| ATTENUATION | 光線隨著距離的增大其強度減小,但只適用於定點光源,但定向光源是無窮遠的設置衰減是無意義.
衰減因子*光強度 = 光衰減 |
|
| GL_CONSTANT_ATTENUATION | 1.0f | 衰減值係數常量 |
| GL_LINEAR_ATTENUATION | 0.0f | 線性衰減值係數 |
| GL_QUADRATIC_ATTENUATION | 0.0f | 二次衰減值係數 |
光照亮度演示程式,定點光源並且全方位輻射.如上圖:下載

OpenGL可對每個頂點設定不同的顏色,令模型產生漸變效果.
漸變函式示例:
| 漸變 | 簡介 |
| void glShadeModel (GLenum mode); | 設定模型漸變模式(mode) |
獲取當前使用的漸變模式
| 漸變模式(mode) | 簡介 |
| GL_FLAT | 單調,只應用單色 |
| GL_SMOOTH | 漸變(默認) |
| GL_FLAT單調顏色應用 | 簡介 |
| GL_POINTS | 應用最後一個頂點的顏色 |
| GL_LINES | |
| GL_TRIANGLES | |
| GL_QUADS | |
| GL_LINE_LOOP | 應用分段的第二個頂點的顏色 |
| GL_LINE_STRIP | |
| GL_TRIANGLE_STRIP | 應用最後一個頂點的顏色 |
| GL_TRIANGLE_FAN | |
| GL_ GL_QUAD_STRIP | |
| GL_POLYGON | 應用第一個頂點的顏色 |
漸變演示程式:下載

OpenGL擁有幾百個狀態,通過屬性堆棧的壓棧和出棧,保存與恢復狀態變量.
| 函式 | 簡介 |
| void glPushAttrib (GLbitfield mask); | 把當前狀態壓入堆棧
Mask:掩碼,保存指定屬性分組 |
| void glPopAttrib (void); | 屬性堆棧出棧並恢復狀態 |
| 掩碼mask | 屬性分組 |
| GL_ALL_ATTRIB_BITS | 所有屬性分組的OpenGL的狀態變量 |
| GL_VIEWPORT_BIT | 視口狀態變量 |
| GL_ENABLE_BIT | 以啟用的狀態變量 |
| GL_FOG_BIT | 煙霧狀態變量 |
| GL_LIGHTING_BIT | 燈光狀態變量 |
| GL_LINE_BIT | 直線狀態變量 |
| GL_POINT_BIT | 質點狀態變量 |
| GL_POLYGON_BIT | 多邊形狀態變量 |
| GL_TEXTURE_BIT | 紋理狀態變量 |

渲染3D場景時頂點在最終被渲染到屏幕上之前需經過四類變換
| 變換(TRANSFORMATION) | 簡介 |
| 視圖變換(VIEW transformation) | 設定攝影機CAMERA的位置 |
| 模型變換(MODEL transformation) | 移動、旋轉、縮放多邊型模型,將模型局部坐標轉為世界坐標. |
| 投影變換(PROJECTION transformation) | 設定視口面積和裁剪平面,用於確定那些多邊型模型位於視口之內. |
| 視口變換(Viewport transformation) | 將剪切坐標影射到2D視口(屏幕) |
| 模型視圖變換(modelview transformation) | OpenGL將視圖變換與模型變換組合成單獨的模型視圖變換 |
視圖變換(VIEW transformation) 即攝影機的設定有三種放法,先將觀察矩陣載入單位矩陣glLoadIdentity();相機為於原點,朝向為負Z軸,上方向量為正Y軸.
| 視圖變換(VIEW transformation) | 簡介 |
| gluLookAt () | 設定攝影機的位置和方向 |
| glRotatef ()與glTranslatef() | 攝影機CAMERA固定在原點,使用旋轉和平移在世界坐標中移動所有模型 |
模型變換(MODEL transformation)是指通過平移、縮放、旋轉將模型定位與定向
| 模型變換(MODEL transformation) | 簡介 |
| 平移glTranslatef() | 模型沿向量進行移動 |
| 旋轉glRotatef() | 模型繞向量進行旋轉 |
| 縮放glScalef() | 在XYZ三軸指定不同縮放係數放大或縮小模型 |
投影變換(PROJECTION transformation)是指設定視口面積和裁剪平面,它在模型變換與視圖變換之後執行,用於確定那些多邊型模型位於視口之內.在設定投影之前需要選擇投影矩陣堆棧glMatrixMode(GL_PROJECTION);和載入單位矩陣glLoadIdentity();OpenGL支持兩類投影
| 投影變換(PROJECTION transformation) | 簡介 |
| 透視投影
glFrustum() gluPerspective() |
用於3D世界的顯示與真實世界一樣, 多邊型模型呈現近大遠小 |
| 正交投影
glOrtho() gluOrtho2D() |
不考濾與攝影機的距離,顯示多邊型模型真實的大小,常用於CAD軟件和2D遊戲 |
視口變換(Viewport transformation)發生在投影變換之後,視口是指渲染的2D窗口的大小和方向
| 視口變換(Viewport transformation) | 簡介 |
| glViewport() | 每當窗口大小發生改變都要重設視口大小 |
OpenGL的變換運算均對矩陣堆棧的棧頂進行操作.
| OpenGL矩陣堆棧 | 簡介 |
| Modelview Matrix Stack | 模型視圖矩陣堆棧 |
| Projection Matrix Stack | 投影矩陣堆棧 |
| TEXTURE Matrix Stack | 紋理矩陣堆棧 |
| ATTRIB Matrix Stack | 屬性矩陣堆棧 |
| CLIENT ATTRIB Matrix Stack | 剪切屬性矩陣堆棧 |
| NAME Matrix Stack |

相機CAMERA固定在原點,通過旋轉和平移在世界坐標中移動所有模型
相機設定示例:
| 視圖變換(VIEW transformation) | 簡介 |
| void glRotatef (
GLfloat angle, GLfloat x, GLfloat y,GLfloat z); |
旋轉
Angle:旋轉角度 x,y,z:軸向量 |
| void glTranslatef (
GLfloat x, GLfloat y, GLfloat z); |
平移
x,y,z:偏移向量 |
| 旋轉 | 軸 | 角 |
| glRotatef(AngleZ,0.0f,0.0f,1.0f); | Z軸 | 橫搖/側滾 |
| glRotatef(AngleY+90,0.0f,1.0f,0.0f); | Y軸補償+90度 | 偏航/航向 |
| glRotatef(AngleX,1.0f,0.0f,0.0f); | X軸 | 傾斜/俯仰 |
| 平移 | 簡介 |
| glTranslatef(-posX,-posY,-posZ); | 反方向移動模型坐標 |
相機移動程式演示:下載

函式gluLookAt()允許你設定相機的位置、方向、朝上向量
相機設定示例:
| 函式 | 數值 | 簡介 |
| void gluLookAt ( | ||
| GLdouble eyex, | 0.0f,0.0f,0.0f, | 相機位置設為原點 |
| GLdouble eyey, | ||
| GLdouble eyez, | ||
| GLdouble centerx, | 0.0f,0.0f,-100.0f, | 相機朝向負Z軸 |
| GLdouble centery, | ||
| GLdouble centerz, | ||
| GLdouble upx, | 0.0f,1.0f,0.0f | 上方向量為正Y軸 |
| GLdouble upy, | ||
| GLdouble upz); |
| 位置和方向計算 | 簡介 |
| radians = 3.141592654f * angleA / 180.0f; | AngleA: Y軸角度
Radians: 弧度 |
| pos=pos – (speed * cos(radians)); | Pos:相機的位置
Speed:移動速度 |
| target=pos+dist*cos(radians); | Dist:視距
Target:相機的朝向 |
相機移動程式演示:下載

視口(Viewport)即渲染窗口大小,每當窗口大小發生改變都要使用glViewport()進行設定.視口變換在投影變換之後進行
| 視口設定 | 簡介 |
| void glViewport(
GLint x, GLint y, 視口的左下角坐標 GLsizei width, 視口的寬度 GLsizei height); 視口的高度 |
視口的左下角坐標設為0,0
視口的寬高設為窗口大寬高
|
重設窗口大小,在WM_SIZE(窗口大小發生改變)消息下調用

OpenGL的變換運算均使用4×4矩陣進行.OpenGL使用堆棧保存矩陣.各種變換運算均針對棧頂進行操作.
| 矩陣 | 簡介 |
| void glMatrixMode (GLenum mode); | 設定當前矩陣堆棧 |
| GL_MODELVIEW | 模型視圖矩陣 |
| GL_PROJECTION | 投影矩陣 |
| GL_COLOR | 顏色矩陣 |
| GL_TEXTURE | 紋理矩陣 |
示例
| 矩陣堆棧 | 簡介 |
| void glPushMatrix(void); | 複製當前矩陣(棧頂)並壓棧. |
| void glPopMatrix(void); | 當前矩陣堆棧出棧並丟棄. |
將當前坐標系統切換為新坐標系統,渲染完成後恢復原始坐標系統.示例:
不同矩陣堆棧有不同的深度,可通過glGetIntegerv()獲取
| 參數 | 深度 | 簡介 |
| GL_MAX_MODELVIEW_STACK_DEPTH | 32 | 模型視圖矩陣堆棧的深度 |
| GL_MAX_PROJECTION_STACK_DEPTH | 10 | 投影矩陣堆棧的深度 |
| GL_MAX_TEXTURE_STACK_DEPTH | 10 | 紋理矩陣堆棧的深度 |
| GL_MAX_ATTRIB_STACK_DEPTH | 16 | 屬性矩陣堆棧的深度 |
獲取模型視圖矩陣深度示例:

旋轉glRotatef()使模形圍繞軸向量進行旋轉.先設定旋轉矩陣後繪畫模型.
| 旋轉 | 簡介 |
| void glRotatef (
GLfloat angle, GLfloat x, GLfloat y, GLfloat z); |
單精度版本
angle:旋轉角度 逆時針旋轉:角度為正 順時針旋轉:角度為負 xyz:旋轉軸向量 |
| void glRotated (
GLdouble angle, GLdouble x, GLdouble y, GLdouble z); |
雙精度版本 |
函式示例分別對XYZ進行旋轉:
| 旋轉示例 | 簡介 |
| glRotatef(45.0f,1.0f,0.0f,0.0f); | 繞X軸以逆時針方向旋轉45度 |
| glRotatef(-90.0f,0.0f,1.0f,0.0f); | 繞Y軸以順時針方向旋轉45度 |
| glRotatef(135.0f,0.0f,0.0f,1.0f); | 繞Z軸以逆時針方向旋轉135度 |
旋轉演示程式如上圖:下載

縮放glScalef()可在XYZ三軸指定不同縮放系數,放大或縮小模型或坐標系統的大小.
放大: 縮放系數大於(>1),若設為2放大一倍
縮小: 縮放系數為(1.0~0.0),若設為0.5側縮小一倍
函式示例:
| 縮放 | 簡介 |
| void glScalef(
GLfloat x, GLfloat y, GLfloat z); |
單精度版本
對模型放大一倍: 1. 縮放glScalef(2,2,2) 2. 後再繪畫Draw() |
| void glScaled(
GLdouble x, GLdouble y, GLdouble z); |
雙精度版本 |
縮放演示程式如上圖:下載

平移Translate可將模型在3D世界中移動,先設定平移矩陣後繪畫模型:
函式示例:
| 平移 | 簡介 |
| void glTranslatef(
GLfloat x, GLfloat y, GLfloat z); |
單精度版本
如在3D世界(6,6,6)繪畫模型: 1. 先平移glTranslatef(6,6,6) 2. 後在繪畫Draw() |
| void glTranslated(
GLdouble x, GLdouble y, GLdouble z); |
雙精度版本 |
平移演示程式如上圖:下載

投影變換是指設定視口面積和剪切平面,它在模型變換與視圖變換之後執行,用於確定那些多邊型模型位於視口之內. OpenGL支持兩類投影
| 投影變換(PROJECTION transformation) | 簡介 |
| 透視投影
glFrustum() gluPerspective() |
用於3D世界的顯示與真實世界一樣, 多邊型模型呈現近大遠小 |
| 正交投影
glOrtho() gluOrtho2D() |
不考濾與攝影機的距離,顯示多邊型模型真實的大小,常用於CAD軟件和2D遊戲,文本顯示 |
| 透視投影 | 簡介 |
| void gluPerspective (
GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); |
直接指定視角和屏幕寬高比從而計算觀察截體. |
| Fovy | 可視角度一般設為45度至90度 |
| Aspect | 屏幕的寬高比(Width/Height) |
| zNear | 近端剪切面距離 |
| zFar | 遠端剪切面距離 |
| 透視投影 | 簡介 |
| void glFrustum (
GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); |
觀察截體的計算由近端剪切面與遠端剪切面確定 |
| Left right bottom top | 近端剪切面的範圍 |
| zNear | 近端剪切面距離 |
| zFar | 遠端剪切面距離 |
| 正交投影 | 簡介 |
| Void glOrtho (
GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); |
相同的模型不管與攝影機距離的遠近,其呈現的相同大小 |
| left right bottom top | 剪切面的範圍 |
| zNear | 近端剪切面距離 |
| zFar | 遠端剪切面距離 |
| 正交投影 | 簡介 |
| void gluOrtho2D (
GLdouble left, GLdouble right, GLdouble bottom, GLdouble top); |
相當於glOrtho(left,right,bottom,top,-1,1); |
| left right bottom top | 剪切面的範圍 |
投影示例
投影函式
void Set_Projection_OpenGL(bool Is3D)
{
::glViewport(0,0,OpenGL_Width,OpenGL_Height); // 重置視區尺寸
::glMatrixMode(GL_PROJECTION); // 設定投影矩陣
::glLoadIdentity();// 載入單位矩陣
if(Is3D == true) // 3D投影計算窗口尺寸比例
gluPerspective(54.0f,(GLfloat)OpenGL_Width/(GLfloat)OpenGL_Height,1.0f,1000.0f);
else // 設為2D投影
gluOrtho2D(0, OpenGL_Width, 0, OpenGL_Height);
::glMatrixMode(GL_MODELVIEW); // 設定模型視圖矩陣
::glLoadIdentity();// 載入單位矩陣
}
投影演示程式如上圖:下載

此程式用於查閱鍵消息的虛擬代碼(Virtual Code)與按鍵狀態(Key state)如上圖:下載程式
在Windows訪問鍵盤有五種方法
| Windows鍵盤消息 | 觸發條件 |
| WM_CHAR | 按下鍵盤 |
| WM_KEYDOWN | 按下鍵盤 |
| WM_KEYUP | 鬆開鍵盤 |
| GetAsyncKeyState() | 任何時候都可讀取鍵盤 |
| Windows鍵盤消息 | wParam | lParam |
| WM_CHAR | ASCII碼(ASCII Code) | 按鍵狀態Key state |
| WM_KEYDOWN | 虛擬代碼(Virtual Code) | |
| WM_KEYUP |
| 按鍵狀態Key state(BIT) | 變量 | 簡介 |
| 0~15 | 整數 | 按鍵重複次數(repeat count) |
| 16~12 | 整數 | 掃描碼(scan code) |
| 24 | bool | 擴展鍵標識,若為1為該鍵為擴展鍵,如右側的ALT鍵和CTRL鍵 |
| 25~28 | 無使用 | |
| 29 | bool | 若為1則ALT鍵被按下,否則為0 |
| 30 | bool | 前一個鍵的狀態 |
| 31 | bool | 若為1鍵被釋放,若為0鍵被按住. |

OpenGL多邊形的演示程式如上圖:下載
繪畫多邊形,頂點不能小於3個
多邊形其它設定請參考三角形

OpenGL四邊形的演示程式如上圖:下載
繪畫四邊形
| glBegin()繪畫四邊形參數 | 簡介 |
| GL_QUADS | 每四個頂點組成四邊形 |
| GL_QUAD_STRIP | 頂點相連的四邊形,需要把第三第四個頂點的渲染順序交換. |
四邊形也屬於多邊形其它設定請參考三角形

OpenGL三角形屬於多邊形.演示程式如上圖:下載
繪畫三角形
| glBegin()繪畫直線參數 | 簡介 |
| GL_TRIANGLES | 每三個頂點組成三角形 |
| GL_TRIANGLE_STRIP | 頂點相連的三角形 |
| GL_TRIANGLE_FAN | 第一個頂點作為三角形的共同頂點 |
因屏幕由像素組成,三角形邊緣會產生鋸齒,啟用抗鋸齒算法後會變得平滑,並修改邊緣像素的顏色:
多邊形模式(正面默認為填充模式)
void glPolygonMode(GLenum face,GLenum mode);
| 多邊形面(face) | 簡介 |
| GL_FRONT | 多邊形正面:頂點的走向為逆時針(默認) |
| GL_BACK | 多邊形背面:頂點的走向為順時針 |
| GL_FRONT_AND_BACK | 正面與背面 |
| 多邊形模式(mode) | 簡介 |
| GL_FILL | 填充模式:對多邊形內部進行顏色填充(默認) |
| GL_LINE | 線框模式:多邊形只繪畫直先不填充 |
| GL_POINT | 頂點模式:只繪畫多邊形頂點 |
設置多邊形隱面裁剪
3D圖形渲染的工作量非常大,把不可見的面剔除則可節減大量變換和渲染時間.
| 正面模式(MODE) | 簡介 |
| GL_CCW | 多邊形正面使用逆時針 |
| GL_CW | 多邊形反面使用順時針 |
多邊形點畫模式可用於簡單的填充(不太常用)
void glPolygonStipple(GLushort * mask);
mask:掩碼大小寬8byte*高16byte=128(byte)=1024(bit),1填充像素,0不填充

OpenGL支持基本幾何直線繪畫,使用glVertex()生成繪畫相連直線的演示程式如上圖:下載
繪畫直線
| glBegin()繪畫直線參數 | 簡介 |
| GL_LINES | 由兩個頂點組成的直線 |
| GL_LINE_STRIP | 由多個頂點組成的相連直線 |
| GL_LINE_LOOP | 由多個頂點組成相連的閉合直線,第一個與最後一個頂點自動相連 |
因屏幕由像素組成,直線邊緣會產生鋸齒,OpenGL有抗鋸齒算法,啟用後直線會變得平滑,並修改邊緣像素的顏色:
點畫模式通常用繪畫虛線
void glLineStipple(GLint factor,GLushort pattern);
factor:模板每BIT對應像素量
pattern:由2個Btye組成點畫模板
開啟直線抗鋸齒算法後,直線寬度範圍受到限制,若輸入不受支持直線寬度,會改為最接近的支持數值:

因為VC是最好用的基於Windwos的 C編譯器,但如果配合快捷鍵使用將會事半功倍
| 快捷鍵 | 簡介 |
| CTRL+U | 選擇文本轉為小寫 |
| CTRL+SHIFT+U | 選擇文本轉為大寫 |
| CTRL+F | Find(查找字符串),但Find In Files顯示輸出欄Output比Find更好用 |
| CTRL+H | 替換字符串,VC最好用的文本編輯工具 |
| CTRL+C | 拷貝文本 |
| CTRL+V | 粘貼文本 |
| CTRL+Z | 撤消文本 |
| CTRL+Y | 恢復文本 |
| CTRL+A | 全選文本 |
| CTRL+R | 編輯資源文檔resources.rc |
| CTRL+S | 保存文檔,使用率最高的快捷鍵.良好使用習慣 |
| F7 | 編譯所有文檔 |
| CTRL+F7 | 編譯當前文檔 |
| ALT+F7 | 工程設置(Project Settings)較小使用 |
| ALT+2 | 顯示輸出欄Output |
| ALT+0 | 打開並把焦點切換到workspace |
| CTRL+W | 嚮導無用 |
| CTRL+G | Go to很小使用 |
| ATL+F2 | BOOKMARKS書簽 |
| CTRL+F2 | 標記 |
| F11 | 單步調試 |
| CTRL+F10 | Run to cursor運行到遊標 |
| F5 | 把修改項編譯並執行程式,或斷點後繼續執行,常用鍵之一. |
| CTRL+F5 | 不重新編譯只執行程式,用於編譯時長耐工程 |
| CTRL+ SHIFT+8 | View whitespace在空格加入或刪除”~”無用的工能只會讓人困惑. |
| CTRL+SHIFT+SPACE | Parameter info查看函式的參數,最好用的快捷鍵之一 |
| CTRL+T | Type Info查看函式或變量的類型信息,最好的用快捷鍵之一 |
| CTRL+ALT+T | 自動彈出全局變量與全局函式,按::也可自動彈出,最好用的快捷鍵之一 |

終於可以成功啟用Jetpack梯來它有很強的系統依賴性,我之前是Godaddy的Windows的主機,我把所有舊版Jetpack都嘗試安裝無一成功,後更改為Linux主機後成功安裝. Jetpack是Wordpress的十全大補外掛,因此我刪除左三個重複外掛!你需要wordpress.com的帳號但很多服務需要收費,有點點雞肋的感覺.
| Jetpack提供 | 簡介 |
| 分享按扭 | 安裝Jetpack的原因之一,第三方分享按扭大多如人意,而且大多經過跳轉. |
| 第三方帳號登錄 | 安裝Jetpack的最大誘因,允許使用WordPress.com、Twitter、Facebook 或 Google+帳號登錄 |
| 自動分享 | 本人認為手動分享更零活 |
| 點贊按扭 | 對於Blog意義不大 |
| 相關文章 | Contextual Related Posts選項更多
Jetpack只可顯示三個相關文章,意義不大. |
| 網誌統計 | 流量統計分析與感覺一般,可以整合Google Analytics但需要收費. |
| 廣告ADS | 廣告需要收費暈 |
| 文章摘要 | Jetpack支持文章摘要,但只支持移動主題. |
| SEO | 暈又收費 |
| 網站地圖 | XML自動傳送至所有主要搜尋引擎進行索引。Wordpress本已內置同步訂閱,而且更好用. |
| 聯繫表單 | 意見反應,有一定作用,但可使用迴響替代. |
| 短網址 | 短網址對於Blog意義不大 |
| 電郵發表文章 | 智能手機時代意義不大 |
| 留言Markdown | 文本標記語言 |
| 留言點贊 | 有一定意義 |
| 大頭貼 | 無用過 |
| 惡意登入 | 主動阻擋惡意登入嘗試 |
| 備份 | 自動備份整個網站,收費 |
| 停機監控 | 郵件通知 |
| 無限捲動 | 手機移動閱覽時效果 |

OpenGL支持基本幾何頂點繪畫,使用glVertex()生成畫點的演示程式如上圖:下載
繪畫單個頂點
因屏幕由像素組成,縮放頂點時邊緣會產生鋸齒,OpenGL有其抗鋸齒算法,啟用後頂定會由方變圓,並修改邊緣像素的顏色:
if (glIsEnabled(GL_POINT_SMOOTH) == false)// 判斷抗鋸齒是否已啟用
{
glEnable(GL_POINT_SMOOTH); // 啟用頂點的平滑模式(抗鋸齒功能)
glEnable(GL_BLEND);// 需啟用混合
}
開啟抗鋸齒算法後,頂點尺寸範圍受到限制,若輸入不受支持頂點的尺寸,會改為最接近的支持數值:

因顯卡性能各異,所以OpenGL可給用戶在速度與質量之間作出選擇,參數在下表給出.並給出示例代碼:
Void glHint(GLenum target, GLenum hint);
| HINT | 簡介 |
| GL_FASTEST | 使用最快速度和效率,但畫面質量有所下降. |
| GL_NICEST | 使用最高畫面質量,但運行速度有所下降 |
| GL_DONT_CARE | 由顯卡的OpenGL驅動在速度和質量之間作出選擇 |
| TARGET | 簡介 |
| GL_POINT_SMOOTH_HINT | 指定在進行反鋸齒的操作中,點、線、多邊形的抽樣質量 |
| GL_LINE_SMOOTH_HINT | |
| GL_POLYGON_SMOOTH_HINT | |
| GL_FOG_HINT | 若hint設為GL_NICEST,煙霧的計算將以每像素的形式執行
若hint設為GL_FASTEST,煙霧的計算將以每頂點的形式執行 |
| GL_PERSPECTIVE_CORRECTION_HINT | 指定顏色和紋理坐標的插值品質 |
設置OpenGL性能
hint:建議性能
GL_FASTEST:速度優先
GL_NICEST:最高畫面質量
GL_DONT_CARE:由顯卡驅動決定
void Set_Hint_OpenGL(GLenum hint)
{
glHint(GL_POINT_SMOOTH_HINT, hint);// 點
glHint(GL_LINE_SMOOTH_HINT, hint);// 線
glHint(GL_POLYGON_SMOOTH_HINT, hint);// 多邊形
glHint(GL_FOG_HINT, hint);// 煙霧
glHint(GL_PERSPECTIVE_CORRECTION_HINT,hint);//紋理坐標
}

OpenGL內部有ERROR錯誤標記,若向OpenGL的函式傳遞不正確的參數,函式會設置錯誤碼並返回.錯誤代碼一但產生就不會修改,直到調用
GLenum error = glGetError();
它將返回下表的錯誤代碼,並把標記重設為GL_NO_ERROR:
| 錯誤碼 | 簡介 |
| GL_NO_ERROR | 無錯誤 |
| GL_INVALID_ENUM | 傳遞不被支持枚舉型(enum)數值時產生此錯誤 |
| GL_INVALID_VALUE | 傳遞超出可接受範圍的數值時產生此錯誤 |
| GL_INVALID_OPERATION | 傳遞參數未能運行或當前設置未能運行,這錯誤碼較上兩者更難以追查錯誤 |
| GL_STACK_OVERFLOW | 堆盞溢出 |
| GL_STACK_UNDERFLOW | 堆盞不平衡 |
| GL_OUT_OF_MEMORY | 內存耗盡或內存洩漏 |
| GL_TABLE_TOO_LARCE | 過多使用TABLE而產生 |

調用glGetString()可以很方便提取顯卡的OpenGL信息,但需在wglMakeCurrent()之後調用,否則返回NULL.我使用此函式製作讀取OpenGL版本程式:下載
函式原型:
const GLubyte * glGetString(GLenum name);
| name參數 | 簡介 |
| GL_VENDOR | 顯卡的製造商名稱 |
| GL_RENDERER | 顯卡型號和CPU的信息 |
| GL_VERSION | 顯卡驅動主輔版本號 |
| GL_EXTENSIONS | OpenGL擴展支持列表,以空格進行分割 |

從DOS,街機到手機遊戲都是以全屏出現,全屏顯示只需加入小量代碼
設定為全屏需在CreateWindow之前調用,下面是完整的代碼
bool Set_FullScreen_OpenGL(int screenWidth,int screenHeight,int screenBpp )
{
DEVMODE devmode;
memset(&devmode,0,sizeof(devmode));// 清零
devmode.dmSize = sizeof(DEVMODE);// 填充DEVMODE
devmode.dmPelsWidth = screenWidth;// 屏幕寬度
devmode.dmPelsHeight = screenHeight;// 屏幕高度
devmode.dmBitsPerPel = screenBpp;// 屏幕像素BIT數
devmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;// 設定有效字段
// 設為全屏
if(ChangeDisplaySettings(&devmode,CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL)
OpenGL_FullScreen = true;
else
OpenGL_FullScreen = false;
return OpenGL_FullScreen;
}
退出程序時恢復為窗口模式
bool Set_Window_OpenGL()
{ // 設為窗口模式
if(ChangeDisplaySettings(NULL,0) == DISP_CHANGE_SUCCESSFUL)
{
OpenGL_FullScreen = false;
ShowCursor(true);// 顯示鼠標
}
return OpenGL_FullScreen;
}

當OpenGL窗口的大小發生改變時,OpenGL窗口會變形需要重新設定以適應新的窗口大小.
給出重設窗口大小完整代碼
void Set_WindowSize_OpenGL(int width,int height)
{
if(height == 0)
height = 1;// 确保分母不为0
// 重置视区尺寸
::glViewport(0,0,width,height);
// 设定投影矩阵
::glMatrixMode(GL_PROJECTION);
// 载入单位矩阵
::glLoadIdentity();
// 计算窗口尺寸比例
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,1.0f,1000.0f);
// 设定模型视图矩阵
::glMatrixMode(GL_MODELVIEW);
// 载入单位矩阵
::glLoadIdentity();
}

繪製環境Context用於記錄OpenGL的設置和命令,但繪製環境必需在像素格式設置完成後調用.下麵是簡介和源碼
| 繪製環境 | 簡介 |
| HGLRC | 繪製環境的句柄 |
| HGLRC wglCreateContext(HDC hDC); | 創建繪製環境,並返回繪製環境句柄, 在WM_CREATE消息下調用 |
| BOOL wglDeleteContext(HDC hDC,HGLRC hRC); | 刪除繪製環境, 在WM_CLOSE消息下調用 |
| BOOL wglMakeCurrent(HDC hDC,HGLRC hRC); | 設置當前繪製環境,若HGLRC設為NULL則取消當前設定的繪製環境 |
創建繪製環境
void Create_Context_OpenGL(HDC hDC)
{ //創建繪製環境
hGLRC = ::wglCreateContext(hDC);
// 設置為繪製環境
::wglMakeCurrent(hDC,hGLRC);
}
刪除繪製環境
void Delete_Context_OpenGL(HDC hDC)
{ // 取消設定的為繪製環境
::wglMakeCurrent(hDC,NULL);
// 刪除繪製環境
::wglDeleteContext(hGLRC);
}

在OpenGL工作之前需要設定當前環境設備(顯卡)像素格式,只需三步:
| 設定像素格式 | 簡介 |
| PIXELFORMATDESCRIPTOR | 填充像素格式 |
| ChoosePixelFormat() | 獲取顯卡支持的像素格式,返回索引 |
| SetPixelFormat() | 設置當前環境設備(顯卡)的像素格式 |
PIXELFORMATDESCRIPTOR需要填補的數值較多
| 字段 | 填充數值 | 像素格式簡介 |
| iPixelType | sizeof(PIXELFORMATDESCRIPTOR) | 結構的大小 |
| nVersion | 1 | 版本號填1 |
| dwFlags | PFD_DRAW_TO_WINDOW | 支持在窗口繪畫 |
| PFD_DRAW_TO_BITMAP | 支持在位圖繪畫 | |
| PFD_SUPPORT_GDI | 緩存支持GUI繪畫 | |
| PFD_SUPPORT_OPENGL | 緩存支持OPENGL繪畫 | |
| PFD_GENERIC_ACCELERATED | ||
| PFD_GENERIC_FORMAT | ||
| PFD_NEED_PALETTE | ||
| PFD_NEED_SYSTEM_PALETTE | ||
| PFD_DOUBLEBUFFER | 支持雙緩存 | |
| PFD_STEREO | ||
| PFD_SWAP_LAYER_BUFFERS | 支持交換Layer緩存 | |
| PFD_DEPTH_DONTCARE | 支持Z緩存 | |
| PFD_DOUBLEBUFFER_DONTCARE | ||
| PFD_STEREO_DONTCARE | ||
| iPixelType | PFD_TYPE_RGBA | RGBA像素 |
| PFD_TYPE_COLORINDEX | 256色使用調色板索引 | |
| cColorBits | 32、24、16、8 | 每像素所占字節數 |
| cRedBits | 0 | 紅色所占Bit數 |
| cRedShift | 0 | 紅色偏移Bit量 |
| cGreenBits | 0 | 綠色所占Bit數 |
| cGreenShift | 0 | 綠色偏移Bit量 |
| cBlueBits | 0 | 藍色所占Bit數 |
| cBlueShift | 0 | 藍色偏移Bit量 |
| cAlphaBits | 0 | Alpha所占Bit數 |
| cAlphaShift | 0 | Alpha偏移Bit量 |
| cAccumBits | 0 | 累加緩存Bit數 |
| cAccumRedBits | 0 | 紅色累加緩存Bit數 |
| cAccumGreenBits | 0 | 綠色累加緩存Bit數 |
| cAccumBlueBits | 0 | 藍色累加緩存Bit數 |
| cAccumAlphaBits | 0 | Alpha累加緩存Bit數 |
| cDepthBits | 16 | z-buffer(Z緩存)大小 |
| cStencilBits | 0 | 模板緩存Bit數 |
| cAuxBuffers | 0 | 輔助緩存Bit數 |
| iLayerType | 0 | 繪製平面 |
| bReserved | 0 | 保留字段 |
| dwLayerMask | 0 | 不在使用 |
| dwVisibleMask | 0 | 透明色掩碼索引 |
| dwDamageMask | 0 | 不在使用 |
最後給出完整代碼
void Setup_PixelFormat_OpenGL(HDC hDC)
{
PIXELFORMATDESCRIPTOR pfd;
int index;
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);// 結構的大小
pfd.nVersion = 1;//版本號填1
pfd.dwFlags = PFD_DRAW_TO_WINDOW |// 支持窗口
PFD_SUPPORT_OPENGL |// 支持OPENGL
PFD_DOUBLEBUFFER ;// 支持雙緩存
pfd.iPixelType = PFD_TYPE_RGBA;// 像素數據類型RGBA
pfd.cColorBits = 16;// 16bit每像素所占字節數
pfd.cRedBits = 0;// 紅色所占Bit數
pfd.cRedShift = 0;// 紅色偏移Bit量
pfd.cGreenBits = 0;// 綠色所占Bit數
pfd.cGreenShift = 0;// 綠色偏移Bit量
pfd.cBlueBits = 0;// 藍色所占Bit數
pfd.cBlueShift = 0;// 藍色偏移Bit量
pfd.cAlphaBits = 0;// Alpha所占Bit數
pfd.cAlphaShift = 0;// Alpha偏移Bit量
pfd.cAccumBits = 0;// 累加緩存Bit數
pfd.cAccumRedBits = 0;// 紅色累加緩存Bit數
pfd.cAccumGreenBits = 0;// 綠色累加緩存Bit數
pfd.cAccumBlueBits = 0;// 藍色累加緩存Bit數
pfd.cAccumAlphaBits = 0;// Alpha累加緩存Bit數
pfd.cDepthBits = 16;// 16bit z-buffer(Z緩存)大小
pfd.cStencilBits = 0;// 模板緩存Bit數
pfd.cAuxBuffers = 0;// 輔助緩存Bit數
pfd.iLayerType = 0;// 無繪製平面
pfd.bReserved = 0;// 保留字段
pfd.dwLayerMask = 0;// 不在使用
pfd.dwVisibleMask = 0;// 透明色掩碼索引
pfd.dwDamageMask = 0;// 不再使用了
// 獲取當前環境設備(顯卡)支持的像素格式,返回索引
index = ::ChoosePixelFormat(hDC,&pfd);
// 設置當前環境設備(顯卡)的像素格式
::SetPixelFormat(hDC,index,&pfd);
}

在DOS時代因為當時硬件品類小,程序直接控制硬件,但當硬件品類不斷增加,要對所有硬件進行支持變得不現實.所以便有OpenGL的出現,把硬件的支持交還給硬件開發商.
最初OpenGL由Silicon Graphics,Inc(SGI)開發的一個可以跨平臺使用的圖形編程接口, GL就是Graphics library的縮寫,最初用於UNIX工作站.
1992年OpenGL Architecture Review Board(ARB)委員會創建,由ATI,Compaq,Evans&Sutherland,Hewlett-packard,IBM,Intel,Intergraph,Nvidia,Microsoft,Silicon Graphics組成
1995年微軟在遊戲廠商進逼下不得已在WinNT中支持OpenGL
| 程式庫 | 簡介 | 下載點 |
| OpenGL | 標準OpenGL | 下載1或下載2 |
| GLU | (實用庫)OpenGL的補充
|
|
| GLUT | (OpenGL Utility Toolkit)應用工具包,與系統無關且便於移於移植.作為AUX庫的的替代品.
|
下載 |
下載並解壓後,更據文檔的擴展名拷貝到各自對應的VC目錄
| 文件 | 文檔類形 | 目錄 |
| include files | .h文檔 | C:\Program Files\Microsoft Visual Studio\VC98\Include\GL |
| Library files | .lib文檔 | C:\Program Files\Microsoft Visual Studio\VC98\Lib |
| Executable files | .dll文檔 | C:\Program Files\Microsoft Visual Studio\VC98\Bin |
| 程式頭文檔 | 程式庫文檔 | 程式二進制文檔 | 簡介 |
| gl.h | Opengl32.lib | OPENGL32.DLL | 標準OpenGL頭文件 |
| glu.h | Glu32.lib | GLU32.DLL | OpenGL實用工具庫 |
| glaux.h | GLAUX.LIB | OpenGL輔助函數庫 | |
| glut.h | glut.lib | glut.dll | OpenGL應用工具包 |
| glut32.lib | glut32.dll |


對於愛書之人最疼心的你愛的書已絕版無緣一讀.但如果作者公開左它的(電子版)PDF文檔,則你還有一線希望,但電子版是無法替代紙質版(如果你真正讀書).下面將教你如何打印整本書

近日要實現文件拖放,如果只實現接收則較為簡單,下麵把實現原理記下.
int Query_DragAndDrop(HDROP hdrop,PROC_DRAGANDDROP Proc_DragAndDrop)
{
int File_Num;
TCHAR File_Path[2056];
int File_Index;
File_Num = ::DragQueryFile(hdrop,-1,NULL,0);// 獲取文件個數
for(File_Index = 0; File_Index < File_Num; ++File_Index)
{ // 獲取拖放文件
::DragQueryFile(hdrop,File_Index,File_Path,sizeof(File_Path));
Proc_DragAndDrop(File_Path);// 處理文件
}
::DragFinish(hdrop);// 釋放空間
return 1;
}

HASH(哈希碼)用於生成文件的指紋,如果兩個文件的哈希碼一致,則代表文件是相同的.令外也用於生成密碼指紋.此軟件用於生成文件的哈希碼,支持SHA1,SHA256,SHA384,SHA512,MD5,MD4,MD2,CRC32,CRC64.大部分的哈希校驗碼是我在2009年所寫.所以此軟件製作較順利.加入文件拖放和拷貝剪貼板,全部使用純C代碼所寫速度快,體積小.
Win32版下載:HASH

按扭BUTTON可以設定圖標,但有幾點需要需注意:

外鏈即指向您的網站的連結,它確定你從搜索引擎獲得多小流量.但要獲得大量的外鏈並不容易,而對於個人Blog(博客)應把精力集中于文章的質量而不是把精力用於增加外鏈.但個人博客主可以通過域名查詢網站增加外鏈,通過查詢域名流下查詢記錄,並產生一條指向你域名鏈接,而域名查詢網站首頁也會產生最新查詢鏈接,如果搜索引擎剛好來訪,你的外鏈就會增加.但天天查詢網站域名更會耗費大量精力,不過你可以製作全自動化域名查詢蜘蛛.下麵給出軟件的製作思路.

對話框圖標在模板中不能設置,需要
1. 準備一張512×512圖片
2. 使用IconWorkshop轉換成ICO圖標
3. 分辯率勾選48×48,32×32,24×24,16×16,位數32Bit(RGBA),16Bit,8Bit(256色)即可.
4. 在你VC工程目標下創建ICON文件夾,把ICO圖標複製於此.
5. 在RSOURCE.RC文件加上,要注意第一條記錄也是應用圖標
IDR_MAINFRAME ICON “ICO\\logo.ico”
6. 在對話框WM_INITDIALOG設置
HICON hIcon = LoadIcon(main_instance,_T(“IDR_MAINFRAME”));//載入圖標
::SendMessage(hWnd, WM_SETICON, true, (LPARAM)hIcon);//設置大圖標
::SendMessage(hWnd, WM_SETICON, false, (LPARAM)hIcon);//設置小圖標

如果程式需要讀取大量數據可使用SQLite3關係數據庫,但如果只是讀寫小量文本數據,則使用文本一行行地保存文本更方便,擴展名最好改為.db下麵給設計思路.
讀取文本數據
寫入文本數據
讀取文本的C代碼
bool Read_DB()
{
FILE * stream=NULL; // 文件流句柄
char text[2056];
stream = _wfopen(L”text.dat”,L”r+t”);// 以文本方式打開文件
if(stream == NULL)
return false;// 無法打開文件
// 讀取一行
while(fgets(text,sizeof(text),stream) != NULL)
{ //去除字符串中最後面 “換行符” “空格”
TrimRight(text);
// 處理文本
// ……….
}
fclose(stream); // 關閉文件句柄
return true;
}
追加一行文本的C代碼
bool Save_DB(char * text)
{
FILE * stream=NULL; // 文件流句柄
// 以文本方式打開文件
stream = _wfopen(L”text.dat”,L”a+t”);
if(stream == NULL)
return false;// 無法打開文件
::fputs(text,stream);// 寫入數據
::fputs(“\n”,stream);// 換行
fclose(stream);// 關閉文件句柄
return true;
}

對話框(DIALOG)模板可以設定顯示位置,但因屏幕大小不一,總想讓對話框在屏幕居中顯示.
下面給出C源代碼便於移值到其它工程
bool CentreWindow(HWND hWnd)
{
RECT rect;
int width,height;
int x,y;
if(IsWindow(hWnd) == false)
return false;
::GetWindowRect(hWnd,&rect); // 獲取窗口的
width = rect.right – rect.left; // 計算窗口寬度
height = rect.bottom – rect.top; // 計算窗口高度
x = GetSystemMetrics(SM_CXSCREEN)/2-width/2;// 計算窗口居中X
y = GetSystemMetrics(SM_CYSCREEN)/2-height/2;// 計算窗口居中Y
SetWindowPos(hWnd,HWND_TOP,x,y,0,0,SWP_NOSIZE); // 移動窗口
return true;
}

啟動瀏覽器最簡單的方法是使用ShellExecute(),但無返回進程句炳,要關閉瀏覽器只能查找窗口.更好的方法是CreateProcess()啟動瀏覽器新進程並打帶網頁,並返回進程句柄,方便關閉瀏覽器.
BOOL CreateProcess(LPCTSTR lpApplicationName,//執行模塊名填空NULL即可
LPTSTR lpCommandLine,//命令行填”瀏覽器Path+空格+網頁URL”
LPSECURITY_ATTRIBUTES lpProcessAttributes,//進程屬性 LPSECURITY_ATTRIBUTES lpThreadAttributes,//線程屬性
BOOL bInheritHandles, //繼承進程句柄false
DWORD dwCreationFlags, //創建標誌填CREATE_NEW_CONSOLE
LPVOID lpEnvironment, //進程環境
LPCTSTR lpCurrentDirectory,// 進程的工作路徑填NULL
LPSTARTUPINFO lpStartupInfo, //STARTUPINFO結構
LPPROCESS_INFORMATION lpProcessInformation//PROCESS_INFORMATION結構
);
下麵給打開網頁源代碼,便於
bool Open_Url(char * url,char * browser_Path)
{
int result = 0;
char CommandA[2056];
WCHAR CommandW[2056];
int length;
SECURITY_ATTRIBUTES Security_Attributes_Process;// 進程屬性
SECURITY_ATTRIBUTES Security_Attributes_Thread;// 線程屬性
STARTUPINFO Startup_Info;// 啟動信息
PROCESS_INFORMATION Process_Information;// 進程信息
strcpy(CommandA,browser_Path);
strcat(CommandA,” “);
strcat(CommandA,url);
// 轉換UNICODE
length = MultiByteToWideChar(CP_ACP, NULL, CommandA, strlen(CommandA), CommandW, sizeof(CommandW));
CommandW[length] = NULL;
// 進程屬性
Security_Attributes_Process.nLength = sizeof(SECURITY_ATTRIBUTES);
Security_Attributes_Process.lpSecurityDescriptor = NULL;
Security_Attributes_Process.bInheritHandle = true;
// 線程屬性
Security_Attributes_Thread.nLength = sizeof(SECURITY_ATTRIBUTES);
Security_Attributes_Thread.lpSecurityDescriptor = NULL;
Security_Attributes_Thread.bInheritHandle = true;
// 啟動信息
ZeroMemory( &Startup_Info, sizeof(STARTUPINFO) );
Startup_Info.cb = sizeof(STARTUPINFO);
ZeroMemory( &Process_Information, sizeof(PROCESS_INFORMATION) );
// 創建進程
result = CreateProcessW(NULL,
CommandW,
&Security_Attributes_Process,
&Security_Attributes_Thread,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&Startup_Info,
&Process_Information);
// 獲取進程句柄
if(result != 0)
return true;
else
return false;
}

Windows默認瀏覽器的路徑在註冊表(REGEDIT.EXE)的HKEY_CLASSES_ROOT\http\shell\open\command\,提取數據需要使用下面三個註冊表操作函數.並給出設計思路源碼
char Browser_Path[2056];// 瀏覽器路徑
// ————————————————————–
// 獲取系統默認瀏覽器路徑
// ————————————————————–
char * Get_WebBrowserPath_SEO(void)
{
long ret;
HKEY hKey;// 註冊表句柄
WCHAR path[2056];//
char data[2056];
DWORD length;
// 打開註冊表
// HKEY_CLASSES_ROOT\http\shell\open\command
ret = RegOpenKeyExW(HKEY_CLASSES_ROOT,L”http\\shell\\open\\command”,0,KEY_ALL_ACCESS,&hKey);
if(ret != ERROR_SUCCESS)
return NULL;
// 獲取REG_SZ
DWORD type = REG_SZ;//類型
length = sizeof(path);//
ret = RegQueryValueExW(hKey,NULL,NULL,&type,(PBYTE)path,&length);
if(ret != ERROR_SUCCESS)
return NULL;
// 關閉註冊表句柄
RegCloseKey(hKey);
// 轉換為UTF-8
length = WideCharToMultiByte(CP_UTF8, NULL, path, wcslen(path), data, sizeof(data), NULL, NULL);
data[length] = NULL;
// 清零
memset(Browser_Path,0,sizeof(Browser_Path));
// 提取路徑
for(int i = 0,index = 0;i<(int)length; ++i)
{
if(data[i] == ‘”‘ && index != 0)
break;// 讀取完畢跳出
if(data[i] != ‘”‘)
{
Browser_Path[index] = data[i];
++index;
}
}
return Browser_Path;// 返回路徑
}

因為Windows控件全部使用UNICODE編碼字符(或ANSI),而我文人卻喜歡使用UTF8編碼字符,所以需要在UTF8與UNICODE之間相互轉換,而我在2010年12月7日寫的一個字符轉換函式庫剛好派上用場.所有轉換MultiByteToWideChar()與WideCharToMultiByte()兩個函式完成,轉換後以NULL字符結尾.特將其簡化並記錄如下,便於以後移植使用.
多字節字符(ANSI/UTF-8)轉換寬字符(UNICODE)
int MultiByteToWideChar(
UINT CodePage, // 代碼頁
DWORD dwFlags, // 字符標誌, 默認設為0
LPCSTR lpMultiByteStr, //多字節字符
int cchMultiByte, //多字節字符個數
LPWSTR lpWideCharStr, // 寬字符輸出緩存
int cchWideChar //寬字符緩存大小
);
寬字符(UNICODE)轉換多字節字符(ANSI/UTF-8)
int WideCharToMultiByte(
UINT CodePage, //代碼頁
DWORD dwFlags, // 字符標誌, 默認設為0
LPCWSTR lpWideCharStr, // 寬字符
int cchWideChar, //寬字符長度
LPSTR lpMultiByteStr, //多字節字符輸出緩存
int cchMultiByte, //多字節字符緩存輸出大小
LPCSTR lpDefaultChar, // 自定義寬字符,默認設為NULL
LPBOOL lpUsedDefaultChar // 是否使用寬字符
);
//——————————————————————
// Ansi轉換Unicode
// Unicode:Unicode碼文本輸出
// Unicode_Size:Unicode碼輸出緩衝區WORD的長度
// Ansi:ANSI碼文本輸入,以NULL字符接尾
// Encode:ANSI文本字符編碼
// 返回值:Unicode 字符個數!(Unicode一個字符需兩個字節)
//——————————————————————
int Ansi_To_Unicode(WCHAR * Unicode,int Unicode_Size,char * Ansi,int Encode)
{
int Unicode_Length;// Unicode 字符個數!非字節數!
if(Unicode == NULL || Ansi == NULL)
return 0;
// Ansi轉換Unicode
Unicode_Length = MultiByteToWideChar(Encode, NULL, Ansi,strlen(Ansi), Unicode, Unicode_Size);// 轉換
Unicode[Unicode_Length] = 0;
return Unicode_Length;
}
//——————————————————————
//Unicode轉換Ansi
//Ansi:ANSI碼文本輸出
//Unicode_Size:Unicode碼輸出緩衝區WORD的長度
//Unicode:Unicode碼文本輸入
//返回值:Ansi所占字節數!
//——————————————————————
int Unicode_To_Ansi(char* Ansi,int Ansi_Size,PWORD Unicode)
{
int Ansi_Length;
if(Ansi == NULL || Unicode == 0)
return 0;
//Unicode轉換Ansi
Ansi_Length = WideCharToMultiByte(CP_ACP, NULL, Unicode, wcslen(Unicode), Ansi, Ansi_Size, NULL, NULL);
Ansi[Ansi_Length] = NULL;
return Ansi_Length;
}
//——————————————————————
// UTF-8 轉 Unicode
//Unicode:Unicode碼文本輸出
//Unicode_Size:Unicode碼輸出緩衝區WORD的長度
//Utf8:Utf-8文本輸入
//返回值:UTF-8字符個數!
//——————————————————————
int Utf8_To_Unicode(WCHAR * Unicode,int Unicode_SIze,char * Utf8)
{
int Unicode_Length;
if(Utf8 == NULL || Unicode == NULL )
return 0;
// UTF-8 轉 Unicode
Unicode_Length = MultiByteToWideChar(CP_UTF8, NULL, Utf8, strlen(Utf8), Unicode, Unicode_SIze);
Unicode[Unicode_Length] = NULL;
return Unicode_Length;
}
//——————————————————————
// UTF-8 轉 Unicode
//Utf8:UTF-8字符輸出
//Utf8_Size:UTF-8碼輸出緩衝區BYTE的長度
//Unicode:Unicode寬字符輸入
//返回值:Unicode所占字節數!
//——————————————————————
int Unicode_To_Utf8(char * Utf8,int Utf8_Size,WCHAR * Unicode)
{
int Utf8_Length;
if(Utf8 == NULL || Unicode == NULL)
return 0;
// UTF-8 轉 Unicode
Utf8_Length = WideCharToMultiByte(CP_UTF8, NULL, Unicode, wcslen(Unicode), Utf8, Utf8_Size, NULL, NULL);
Utf8[Utf8_Length] = NULL;
return Utf8_Length;
}
//—————————————————————————
//Ansi 轉 Utf8
//Utf8:UTF-8字符輸出
//Utf8_Size:UTF-8碼輸出緩衝區BYTE的長度
//Ansi:ANSI文本輸入
//Encode:ANSI文本字符編碼
//返回值:Utf8字節長度
//—————————————————————————
int Ansi_To_Utf8(char * Utf8,int Utf8_Size,char * Ansi,int Encode)
{
int Utf8_Length = NULL;
PWORD Unicode;
int Unicode_Size;
int Unicode_Length;
Unicode = (PWORD)Utf8;
Unicode_Size = Utf8_Size/2;
// Ansi 轉 Unicode
//Unicode_Length = Ansi_To_Unicode(Unicode,Unicode_Size,Ansi,Encode);
Unicode_Length = MultiByteToWideChar(Encode, NULL, Ansi,strlen(Ansi), Unicode, Unicode_Size);// 轉換
Unicode[Unicode_Length] = 0;
// Unicode 轉 Utf8
//Utf8_Length = ::Unicode_To_Utf8(Utf8,Utf8_Size,Unicode);
Utf8_Length = WideCharToMultiByte(CP_UTF8, NULL, Unicode, wcslen(Unicode), Utf8, Utf8_Size, NULL, NULL);
Utf8[Utf8_Length] = NULL;
return Utf8_Length;
}
//—————————————————————————
// Utf8 轉換 Ansi
// Ansi: ANSI文本輸出
// Ansi_Size:ANSI碼輸出緩衝區BYTE的長度
// Utf8: UTF-8字符輸入
// 返回值:Ansi字節長度
//—————————————————————————
int Utf8_To_Ansi(char * Ansi,int Ansi_Size,char * Utf8)
{
int Ansi_Length = NULL;
PWORD Unicode;
int Unicode_Size;
int Unicode_Length;
Unicode = (PWORD)Ansi;
Unicode_Size = Ansi_Size/2;
// Utf8 轉 Unicode
//Unicode_Length = Utf8_To_Unicode(Unicode,Unicode_Size,Utf8);
Unicode_Length = MultiByteToWideChar(CP_UTF8, NULL, Utf8, strlen(Utf8), Unicode, Unicode_Size);
Unicode[Unicode_Length] = NULL;
// Unicode 轉 Ansi
//Ansi_Length = ::Unicode_To_Ansi(Ansi,Ansi_Size,Unicode);
Ansi_Length = WideCharToMultiByte(CP_ACP, NULL, Unicode, wcslen(Unicode), Ansi, Ansi_Size, NULL, NULL);
Ansi[Ansi_Length] = NULL;
return Ansi_Length;
}
| 編碼類型 | 數值 | 說明 |
| CP_ACP | 0 | 默認ANSI代碼頁 |
| CP_OEMCP | 1 | 默認OEM代碼頁 |
| CP_MACCP | 2 | 默認MAC代碼頁 |
| CP_THREAD_ACP | 3 | 本線程ANSI代碼頁 |
| CP_SYMBOL | 42 | SYMBOL轉換 |
| CP_UTF7 | 65000 | UTF-7轉換 |
| CP_UTF8 | 65001 | UTF-8轉換 |
| CP_ANSI | 1252 | 通用ANSI 代碼頁 |
| ISO_8859_1 | 28591 | 西歐語言 |
| ARABIC | 1256 | 阿拉伯語 |
| GENERAL | 1252 | 通用 |
| DUTCH | 1252 | 荷蘭,德國 |
| CZECH | 1250 | 捷克斯洛伐克 |
| HUNGARIAN | 1250 | 匈牙利 |
| ICELANDIC | 1252 | 冰島 |
| NORDIC | 1252 | 北歐日耳曼民族的 |
| NORWDAN | 1252 | |
| POLISH | 1250 | 波蘭 |
| CYRILLIC | 1251 | 西裏爾字母 |
| SPANISH | 1252 | 西班牙 |
| SWEDFIN | 1252 | |
| TURKISH | 1254 | 土耳其語, 突厥語族 |
| GREEK | 1253 | 希臘 |
| HEBREW | 1255 | 希伯來語 |
| JAPANESE | 932 | 日本 |
| KOREAN | 949 | 朝鮮 |
| BIG5 | 950 | 正體中文 |
| GB2312 | 936 | 簡體中文 |
| GBK | 936 | 簡體中文 |
| GB18030 | 54936 | 簡體中文 |
| SLOVENIAN | 1250 | 未開發 |

近日寫SEO應用程式,因現在目錄路徑可能會有中文字符或非ASCII字符,所以使用寬字符版的程式,但出現_T()與_TEXT()宏指令無定義:
error C2065: ‘_T’ : undeclared identifier
又不想直接使用L()宏指令定義寬自字符,百思不得其解.原來_T()與_TEXT()宏指令的定義在TCHAR.H文件,添加#include <TCHAR.h>即可解決.
VC使用#define _MBCS定義則使用ANSI字符,而定義#define _UNICODE 則使用UNICODE寬字符.如過想全局定義_MBCS或_UNICOD可在Settings設置如上圖.
Project Settings/C/C++/Category/General/Preprocessor definitions/ WIN32,_DEBUG,_WINDOWS,_UNICODE

拍號由上下兩數組成,在五線譜調號後面,上面的數字表示一小節含幾拍,下面的數字表示音值比率
上面的數字表示一小節含幾拍
4/4表示一個小節後四拍,該小節所有音符加來是四拍, 4/4因為用得多所以使用大寫C表示.
3/4表示一個小節後三拍,該小節所有音符加來是三拍
下面的數字表示音符的音值比率
4是最常見音值比率,意即四分音符為一單位拍,按照這個比值算如下表
| 音符 | 拍 |
| 八分音符 | 1/2拍 |
| 四分音符 | 一拍 |
| 二分音符 | 二拍 |
| 符點二分音符 | 三拍 |
| 全音符 | 四拍 |
8意即八分音符為一單位拍,如下表
| 音符 | 拍 |
| 八分音符 | 一拍 |
| 四分音符 | 二拍 |
| 二分音符 | 四拍 |
| 符點二分音符 | 六拍 |
| 全音符 | 八拍 |
示例如上圖
3/4表示每一小節3拍,四分音符為一單位拍,第一小節3個C每個一拍,第二小節二分音符C兩拍,加上四分音符C一拍,一共三拍.

我果台雜牌平板更換電池後獲得新的生命.平板本為雙系統Win8+Android.因為硬盤只有64GB再分給兩系統使用可利用率實在太低.故想重新分區安裝Win10.在微軟下載Windows10 ISO官方鏡像.版本號為1709居然無法安裝第三應用,只可以安裝在應用商店下載的應用或者購買專業版…暈!也就想激活Win10都無可能.之後安裝早期的Win10-ISO鏡像才成攻激活.


黑鍵是根據相鄰白鍵所發音來命名,在白鍵的音名上加”升號”或”降號”
升號(sharp)
五線譜音符左側”#”稱為”升號”,它表示把音符的音級升高一個變化音級
不論彈奏高音譜表(右手)或低音譜表(左手),音符前的升號都對應該音符所示白鍵右側的黑鍵
沒有B#因為B和C之間沒有黑鍵
沒有E#因為E和F之間沒有黑鍵
降號(flat)
五線譜音符左側”b”稱為”降號”,它表示把音符的音級降低一個變化音級
不論彈奏高音譜表(右手)或低音譜表(左手),音符前的降號都對應該音符所示白鍵左側的黑鍵
沒有Cb因為B和C之間沒有黑鍵
沒有Fb因為E和F之間沒有黑鍵
還原記號(natural sign)
還原記號(“),即取消升號或降號,從升音或降音之後的黑鍵回到初始的白鍵,
在音符前標記的升號與降號,對本小節同音高的音起作用,直到出現還原記號,而到下一個小節又恢復原先的調號

在撰寫與音樂相關的文章時會大量用到五線譜音符,但系統自帶的字體一般都不帶五線譜音符.幸好有專用的五線譜字體



全音符(whole note)
全音符只有空心符頭沒有符幹(符頭延伸的直線).一個全音符時值為四拍,即按下琴鍵並保持四拍,上圖彈E並保持四拍.
二分音符(half note)
全音符由空心符頭和符幹組成.一個二分音符時值為兩拍,即按下琴鍵並保持兩拍,上圖彈兩個G每個兩拍
四分音符(quarter note)
四分音符由實心符頭和符幹組成.一個四分音符時值為一拍,即按下琴鍵並保持一拍,上圖彈四個F每個保持一拍
八分音符(eighth note)
八分音符由實心符頭于符幹和符尾組成,符尾為符幹旁側伸出的一條彎曲,一個八分音符時值為1/2拍,當兩個或以上的八分音符挨在一起時在它們上面加一條橫線進行標記, 上圖彈七個E每個保持1/2拍
符點二分音符(dotted half note)
音符後有一個點表示增加該音符時值1/2.符點二分音符時值為三拍,二分音符為兩拍加符點表示加1/2拍既三拍.
休止符(tacet)
休止符用於表示停頓的時間長短
全休止符(whole tacet)
全休止符表示停頓四拍
二分休止符(half tacet)
二分休止符表示停頓兩拍
四分休止符(quarter tacet)
四分休止符表示停頓一拍
八分休止符(eighth tacet)
八分休止符表示停頓1/2拍
|
音符 |
時值 |
符標 |
|
全音符 |
四拍 |
A |
|
二分音符 |
兩拍 |
B |
|
四分音符 |
一拍 |
C |
|
八分音符 |
1/2拍 |
D |
|
符點二分音符 |
三拍 |
H |
|
休止符 |
停頓時長 |
符標 |
|
全休止符 |
四拍 |
U |
|
二分休止符 |
兩拍 |
V |
|
四分休止符 |
一拍 |
W |
|
八分休止符 |
1/2拍 |
X |

鋼琴五線譜由兩組五線譜組成,把兩組五線譜用花括號連接起來構成大譜表.上方一組用右手彈奏,下方一組用左手彈奏.每組五線譜都包括了五條平行橫”線”和四個”間”.線和間用於記錄鋼琴音符.
高音譜號(treble clef)~
上方五線譜最左邊的記號”~”是”高音譜號”,用於指示它後面的音符用右手彈奏.它另有“G譜號”之稱.”高音譜號”的中段要畫在五線譜的第二線開始捲曲標記,這條線用於記錄音符G.
低音譜號(bass clef)`
下方五線譜最左邊的記號”`”是”低音譜號”,用於指示它後面的音符用左手彈奏.它另有“F譜號”之稱. ”低音譜號”的兩個圓點要畫在五線譜的第二線的上下兩個”間”,這條線用於記錄音符F.
小節(measure)和小節線(bar line)
“小節”是指小節線之間的部分,小節線把各小節隔開,不同節可以採用不同的拍子