Google-HK電話冧靶注冊Gmail郵箱

Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱

Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱
Google-注冊Gmail郵箱

注冊Google帳號即注冊Gmail郵箱,要諗定個名6字母,仲畀人注册到.仲有畫埋定logo 解像512px*512px. 揀『源界明朝』體. Google公司條列定明Gmail郵箱閑2年注消

  1. 撳手機Gmail
  2. 撳『新增郵地址』
  3. 撳『Google』
  4. 『請重新輸入pin碼』
  5. 撳『建立帳戶』, 揀『工作或管理個人業務用途』
  6. 填『姓氏』『名字』
  7. 填『姓別』『出生曰期』
  8. 填Gmail地址, 6個ascii字母『xxxxxx@gmail.com 』
  9. 填Gmail密碼, 分大細寫字母(A~Z)+字符().
  10. 用HK電話冧靶,绑定備援電話.

 

 

 

 

 

 

 

 

 

 

 

 

電話咭-3HK攻略

電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-3HK攻略
電話咭-3HK攻略
電話咭-3HK攻略
電話咭-3HK攻略
電話咭-3HK攻略
電話咭-3HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略
電話咭-HK攻略

之前買『Lucky2』齊上網,冇電話冧靶,冇得接打電話,冇得收發短信.要注册『instagram』就要買個有『電話冧靶』可『接打電話』『收發短信』『上網』叁合壹. 諗住『冧靶』長期持有.

和記旗下3HK.有超抵『365日數據通行證-大灣區 45GB』, $268蚊壹年係大陸有40GB數據, 3HK支持『esim』係官網畀銀後,下載寫入手機.部『piexl 5』係支持『esim』.但係鐘意實體『sim卡』. 3HK吾郵寄返大陸.祗有坐車落去『3HK舖頭』.係『郎豪坊』落車去『西洋菜街』或者太子落車去『始創中心』, 『3HK舖頭』有個大大3字.

旺角西洋菜街南1A號百寶利商業中心地下3及4號舖
彌敦道750號始創中心地下G07號舖

祗係两間舖冇貨,話係聖誕畀人搶矖,祇得係帮襯黃牛355蚊人仔,包順風寄埋上蒞.第日朝早送到.係3HK信封入面有張儲值卡.

  1. 首先『啟動』儲值卡,下載『MY3』 但係『Google play』有地域限制. 可去下面URL地址下載『MY3』.
https://apkcombo.app/zh/my3/hk.com.three.my3plus/
  1. 利用『USB磁碟』将『MY3』APP安將係手機.
  2. 係首頁撳『啟用已購買儲值卡』,掃大SIM卡背脊二維碼. 提示此SIM卡已激活.
  3. 之後收到3香港儲值卡服務生效通知短信.『儲值卡電話號碼:XXXX XXXX』有效期半年.
  4. 有條LINK要你實名登記. 撳入去後以『電話號碼』登入.
  5. 以『个人用戶(所有年龄)』登記.
  6. 『證件類別』揀『港澳通行證』.
  7. 撳『掃瞄』影『港澳通行證』.
  8. 顯示『實名登記成功』之後收短信話,『3香港儲值卡實名登記成功』
  9. 翻去『MY3』APP,填『電話號碼』撳登入.
  10. 撳『發送驗證碼(OPT)登入』, 登入欄填驗證碼(OPT).
  11. 撳『365日數據通行證-大灣區 45GB』.

Visual Studio-error LNK2001 unresolved external symbol __imp__glBegin

Visual Studio-error LNK2001 unresolved external symbol __imp__glBegin
Visual Studio-error LNK2001 unresolved external symbol __imp__glBegin

將代碼移稙去WIN10, 係Visual Studio报

error LNK2001: 無法解析的外部符號 __imp_glBegin

調用OpenGL函式,冇鏈结OpenGL庫引起, 係『.H』文檔加入下列代碼即可.

#pragma comment(lib, “OpenGL32.lib”)
#pragma comment(lib, “GLu32.lib”)

紋理拼圖

紋理拼圖
紋理拼圖

2D游戲動画幀, 由多幅關鍵幀(keyFrame) 組成. 逐幀渲染產生動画卡通.『幀動画』亦哎呌『紋理』texture. 将多幅『幀動画』存放係單壹紋理.OpenGL可提高渲染速度, 係显存記憶體1MB年代可能冇用. 事因渲染皆係電脑記憶體運作.

係显存記憶體大幅增長, OpenGL渲染係显存記憶體完成. 减小『紋理』載入量, 單壹紋理存放多幅『幀動画』,可提高渲染速度.

係3D游戲亦可用相同技術提飛渲染效率.

void zoom_textcoord_model3D(TEXTURE_PTR texture,int index,int size,VECTOR2D_PTR dest,VECTOR2D_PTR sour,int count,int flag)

 

texture 紋理
index 關鍵幀索引
size 關鍵幀寬高解像
dest 纹理隊列
sour 原始纹理隊列
count 纹理頂點量

計單元格

int cell = texture->width / size;

計索引

int  i  = index % cell;// 橫索引
int  j  = index / cell;// 行索引

計缩放率0.0f < zoom < 1.0f

float    zoom    = (float)size / (float)texture->width;

計『幀』偏移.

float offsetX = (float)zoom * (float)i;
float offsetY = (float)zoom * (float)j;

遍歴UV紋理頂㸃

    for (int p = 0; p < count; ++p){

缩細後移動

dest[p].u = sour[p].u * zoom + offsetX ;
dest[p].v = sour[p].v * zoom + offsetY;

纹理返转

if (flag & MODEL3D_TEXTURE_FLIP)

dest[p].v = 1 – (sour[p].v * zoom + offsetY);}

 

SKYBOX天幕

天幕SKYBOX
天幕SKYBOX
天幕SKYBOX
天幕SKYBOX

『天幕SKYBOX』指巨立方體, 係內籠貼天幕紋理,『天幕紋理』可能係『地平線』『室內』『宇宙』. 『天幕SKYBOX』原㸃與3D相機位置重合. 係遠睇時正确.

由陸幅紋理『顶』『底』『前』『後』『左』『右』組成. 以前『天幕SKYBOX』紋理分陸幅位圖存檔.

陸幅位圖存係單壹『紋理』效率更高. 将『紋理』平分拾陸等分. 足够擺两組『天幕』紋理. 『日頭』『晚黑』各壹. 似上圖咁.

紋理索引:0~15 『日頭』 『晚黑』
顶up 0 8
底dn 1 9
前ft 7 15
后bk 5 13
左lt 6 14
右rt 4 12

定義『天幕SKYBOX』

typedef struct SKYBOX_TYP {
VECTOR3D  pos; 位置
VECTOR3D  rot; 旋轉
TEXTURE_PTR texture; 天幕纹理
float     size; 天幕大细
//TEXTURE_REGION region[16]; 纹理区域
VECTOR3D  vertex_array[36]   ; 天幕顶点
VECTOR2D  texCoord_array[36] ; 天幕紋理
}SKYBOX, *SKYBOX_PTR;

手エ构建『天幕SKYBOX』立方體『3D頂㸃』同『UV紋理』. 正方形以两三角形組成. 紋理左上角[u0, v0], 紋理右下角[u1,v1].天幕大细『size』.

天『UV紋理』 『xyz頂㸃』
texCoord[0]=[u1, v1] vertex_array[0]=[-size, size, -size]
texCoord[1]=[u0, v1] vertex_array[1]=[size, size, -size]
texCoord[2]=[u0, v0] vertex_array[2]=[size, size, size]
texCoord[3]=[u1, v1] vertex_array[3]=[-size, size, -size]
texCoord[4]=[u0, v0] vertex_array[4]=[size, size, size]
texCoord[5]=[u1, v0] vertex_array[5]=[-size, size, size]

 

地『UV紋理』 『xyz頂㸃』
texCoord[6]=[u1, v1] vertex_array[6]=[size, -size, -size]
texCoord[7]=[u0, v1] vertex_array[7]=[-size, -size, -size]
texCoord[8]=[u0, v0] vertex_array[8]=[-size, -size, size]
texCoord[9]=[u1, v1] vertex_array[9]=[size, -size, -size]
texCoord[10]=[u0, v0] vertex_array[10]=[-size, -size, size]
texCoord[11]=[u1, v0] vertex_array[11]=[size, -size, size]

 

前『UV紋理』 『xyz頂㸃』
texCoord[12]=[u0, v0] vertex_array[12]=[-size, -size, -size]
texCoord[13]=[u1, v0] vertex_array[13]=[size, -size, -size]
texCoord[14]=[u1, v1] vertex_array[14]=[size, size, -size]
texCoord[15]=[u0, v0] vertex_array[15]=[-size, -size, -size]
texCoord[16]=[u1, v1] vertex_array[16]=[size, size, -size]
texCoord[17]=[u0, v1] vertex_array[17]=[-size, size, -size]

 

后『UV紋理』 『xyz頂㸃』
texCoord[18]=[u0, v0] vertex_array[18]=[size, -size, size]
texCoord[19]=[u1, v0] vertex_array[19]=[-size, -size, size]
texCoord[20]=[u1, v1] vertex_array[20]=[-size, size, size]
texCoord[21]=[u0, v0] vertex_array[21]=[size, -size, size]
texCoord[22]=[u1, v1] vertex_array[22]=[-size, size, size]
texCoord[23]=[u0, v1] vertex_array[23]=[size, size, size]

 

右『UV紋理』 『xyz頂㸃』
texCoord[24]=[u1, v0] vertex_array[24]=[size, -size, size]
texCoord[25]=[u1, v1] vertex_array[25]=[size, size, size]
texCoord[26]=[u0, v1] vertex_array[26]=[size, size, -size]
texCoord[27]=[u1, v0] vertex_array[27]=[size, -size, size]
texCoord[28]=[u0, v1] vertex_array[28]=[size, size, -size]
texCoord[29]=[u0, v0] vertex_array[29]=[size, -size, -size]

 

左『UV紋理』 『xyz頂㸃』
texCoord[30]=[u1, v0] vertex_array[30]=[-size, -size, -size]
texCoord[31]=[u1, v1] vertex_array[31]=[-size, size, -size]
texCoord[32]=[u0, v1] vertex_array[32]=[-size, size, size]
texCoord[33]=[u1, v0] vertex_array[33]=[-size, -size, -size]
texCoord[34]=[u0, v1] vertex_array[34]=[-size, size, size]
texCoord[35]=[u0, v0] vertex_array[35]=[-size, -size, size]

 

Android Studio NDK-OpenGL ES 觸屏坐標轉游㱆坐標

Android Studio NDK-OpenGL ES 觸屏坐標轉游㱆坐標
Android Studio NDK-OpenGL ES 觸屏坐標轉游㱆坐標

觸屏坐標』『x,y』坐標轉『正交投影』坐標, 『視錐體解像』寬高, 比例需手機解像寬高比壹致.

計屏幕寬高比

float aspect_ratio = (float)cam->real_width / (float)cam->real_height;

『視錐體解像』寬高,此時定義『高』800pix

float frustum_width    = 800 *aspect_ratio;
float frustum_height   = 800 ;

 

『正交投影』代碼

重置視區尺寸, 值係手機解像寬高

::glViewport(0,0,real_width,real_height);

設定投影矩陣

::glMatrixMode(GL_PROJECTION);

載入單位矩陣

::glLoadIdentity();

正交投影, 游戲坐標原點(0,0,0)為於屏幕中心

glOrthof(frustum_width / 2, frustum_width / 2, -frustum_height / 2, frustum_height / 2, pos.y – 10, far_clip_z);

設定模型視圖矩陣

::glMatrixMode(GL_MODELVIEW);

載入單位矩陣

::glLoadIdentity();

 

手指触摸手機屏幕onTouch() 所得坐標需轉游戲世界坐標,正交投影OpenGL游㱆+Z軸指向屏幕深處.

float touch3Dx = (touch2Dx / real_width) * frustum_width  ;
float touch3Dz = (touch2Dy /real_height) * frustum_height  ;

計3D相機位置

touch3Dx = touch3Dx + camPosX;
touch3Dz = touch3Dz + camPosX;

游戲坐標原點(0,0,0)為於屏幕中心

touch3Dx = touch3Dx – (frustum_width / 2.0f);
touch3Dz = touch3Dz – (frustum_height / 2.0f);

 

Android Studio NDK-OpenGL ES 漢字位圖字庫

Android Studio NDK-OpenGL ES 漢字位圖字庫
Android Studio NDK-OpenGL ES 漢字位圖字庫

『漢字字庫』同 『ASCII字庫』原理同, 字庫『竪排』, 漢字『32*32』pixel, 『竪』32漢字.

由上至下,由右至左排列.可填1024字符,每色8Bit. 即『索引色』『調色板』.

准備庫位圖

  1. 白紙黑字,
  2. 白色係透明色. 黑色係變換色.

Photoshop轉為『索引色』

  1. 『影像』->『模色』->『索引色』
  2. 『色盤』揀『正確』.
  3. 『顏色』量3
  4. 『强制』揀『黑白』
  5. 『透明』勾選
  6. 存為『.pcx』或『.bmp』

止時圖檔『調色板』共有三色『黑』『白』『透明』.

IMAGE-SIZE 1024*1024
FONT-SIZE 30pt
FONT 衡山毛筆フォント
FONT-PIXEL 32pixel*32pixel
影像-模色 索引色
色盤 正確
顏色 3
强制 黑白
透明 勾選

『調色板』結构同DirextX唔同, 將flags存alpha『透明值』0~255,0係『透明』,255係『實心』

typedef struct PALETTE_TYP {

BYTE red;

BYTE green;

BYTE blue;

BYTE flags;//alpha

} PALETTE,COLOR,* PALETTE_PTR,*COLOR_PTR;

設置『調色板』顏色

#define INIT_PALETTE(palette,r,g,b,a) {(palette).red=(r); (palette).green=(g); (palette).blue=(b); (palette).flags=(a);}

黑字『調色板』設置

index red green blue Alpha
253 0xff 0xff 0xff 0x00
255 0x00 0x00 0x00 0xFF*0.5f

白字『調色板』設置

index red green blue Alpha
253 0x00 0x00 0x00 0x00
255 0xff 0xff 0xff 0xFF*0.5f

半透明,激活混合

glEnable(GL_BLEND);

設混合模式, 渲染時Alpha值混合.

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

激活透明测试

glEnable(GL_ALPHA_TEST);

Alpha=0時, 過濾背影色

glAlphaFunc(GL_GREATER, 0);

 

Android Studio NDK-触摸onTouch()

Android Studio NDK-触摸onTouch()
Android Studio NDK-触摸onTouch()

初台Android手機己采用『單點』触摸, 直至Android2.0(SDK version 5) 先支持『多點』触摸.

  1. Android觸摸監聽『OnTouchListener』 監聽 『触屏』事件.
static class TounchListener implements OnTouchListener{
  1. 通過『onTouch()』獲取『触屏』信息.
@Override

public boolean onTouch(View v, MotionEvent event) {

  1. 0(ÉCLAIR)(API 5) 先支持多點触摸
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR)
  1. 處理『多點』触摸
MultiTouch(v, event); else
  1. 處理『單點』触摸
SingleTouch(v, event);
  1. 返回false, 繼續處理觸摸事件.
return false;}

 

  1. 生成觸摸監聽
private static TounchListener Tounch_Listener  = new TounchListener() ;
  1. 為OpenGL View注冊触摸监听,
view.setOnTouchListener(Tounch_Listener);
  1. 通過OnTouch()(View v, MotionEvent event) 獲取『触屏』坐標 . 『触屏』原點(0,0) 係左上角, 單位象素
MotionEvent.getX() X軸指右
MotionEvent.getY() Y軸指下
  1. 獲取『触屏』事件類型 . MotionEvent.getAction()
MotionEvent.getAction()  
MotionEvent.ACTION_DOWN 手指撳屏
MotionEvent.ACTION_POINTER_DOWN 手指撳屏
MotionEvent.ACTION_UP 手指鬆离
MotionEvent.ACTION_POINTER_UP 手指鬆离
MotionEvent.ACTION_CANCEL 手勢鬆
MotionEvent.ACTION_MOVE 移動手指
  1. 『触屏』類型簡化為『撳』『鬆』『拖』三類
public static int ACTION_UP =     1;
public static int ACTION_DOWN = 2;
public static int ACTION_DRAGGED = 3;

 

 

 

 

處理『單點』触摸

static void SingleTouch(View v, MotionEvent event){
  1. 提取『触屏』事件類型
int action = event.getAction() & MotionEvent.ACTION_MASK;
  1. 處理『撳』類型
if(action == MotionEvent.ACTION_DOWN ||

action == MotionEvent.ACTION_POINTER_DOWN)

        Lib.setTouch(ACTION_DOWN,event.getX(),event.getY());else
  1. 處理『撳』類型
if(action == MotionEvent.ACTION_UP ||

action == MotionEvent.ACTION_POINTER_UP ||

action == MotionEvent.ACTION_CANCEL)

        Lib.setTouch(ACTION_UP,event.getX(),event.getY());else
  1. 處理『拖』類型
if(action == MotionEvent.ACTION_MOVE)
Lib.setTouch(ACTION_DRAGGED,event.getX(),event.getY());}

 

 

處理『多點』触摸

  1. 處理多手指触摸, 提取触摸事件索引
int Pointer_Index = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK)>>MotionEvent.ACTION_POINTER_ID_SHIFT;
  1. 通過触摸事件索引,『触屏』原點(0,0) 係左上角, 單位象素
MotionEvent.getX(index) X軸指右
MotionEvent.getY(index) Y軸指下
  1. 提取『触屏』事件類型
int action = event.getAction() & MotionEvent.ACTION_MASK;
  1. 處理多手指触摸, 提取触摸事件索引
int Pointer_Index = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK)>>MotionEvent.ACTION_POINTER_ID_SHIFT;
  1. 提取觸摸事件量
int Pointer_Count = event.getPointerCount();
  1. 遍歷冚觸摸事件
for(int i=0; i< Pointer_Count; ++i)        {
  1. 跳過冇關觸摸事件
if (action != MotionEvent.ACTION_MOVE && i != Pointer_Index)

continue;

  1. 處理『撳』類型
if(action == MotionEvent.ACTION_DOWN ||

action == MotionEvent.ACTION_POINTER_DOWN)

        Lib.setTouch(ACTION_DOWN,event.getX(i),event.getY(i));else
  1. 處理『撳』類型
if(action == MotionEvent.ACTION_UP ||

action == MotionEvent.ACTION_POINTER_UP ||

action == MotionEvent.ACTION_CANCEL)

        Lib.setTouch(ACTION_UP,event.getX(i),event.getY(i));else
  1. 處理『拖』類型
if(action == MotionEvent.ACTION_MOVE)
Lib.setTouch(ACTION_DRAGGED,event.getX(i),event.getY(i));}

 

 

係C++緩存『触屏』信息Lib.setTouch();

typedef struct TOUCH_STR{  
int action; 触屏類型
int x,y; 触屏坐標
}TOUCH,*TOUCH_PTR;  

『触屏座標』轉『熒屏座標』

void TouchToScreen(CAMERA2D_PTR cam,VECTOR2D_PTR touch){
touch->x = (touch->x / (float)cam->real_width) * cam->frustum_width * cam->zoom;
touch->y = (1-touch->y /(float)cam->real_height) * cam->frustum_height * cam->zoom; }

 

Android Studio NDK-OpenGL ES 點陣字體

Android Studio NDK-OpenGL ES 點陣字體
Android Studio NDK-OpenGL ES 點陣字體

係游戲渲染文本,『點陣字體』係遠古技術, 係位圖繪畫ASCII字符, 哎『位圖字符glyph』,標准ASCII字符128,有96拉丁字母可渲染,從32到126.『32』係『吉格』.

位圖每行16字符. 位圖寬高符合2n.位圖『256256』每字符『1616』像素, 位圖『512512』每字符『3232』像素.

定義位圖字符

typedef struct FONT_TYP{
TEXTURE_PTR texture; 字庫紋理
DWORD   flag; 頂點標記
TEXTURE_REGION region[96]; 紋理區域
}FONT, * FONT_PTR;
  1. 拉丁字母
    for(int index = 0; index < 96; ++index){
  1. 生成ASCII字符UV 紋理坐標,『width, height』字符寬高,
Init_Region_Texture(&font->region[index],font->texture,x,y, width, height);
  1. 移動X坐標
x = x + width;
  1. 換行
if(x >= offsetX + width * row){

x = offsetX;

y = y + height;}

 

渲染位圖字庫

  1. 2D頂點座標數組
VECTOR2D  vertex_array[6 * 1024]   ;
  1. UV紋理座標數組
VECTOR2D  texCoord_array[6 * 1024] ;
  1. 啟用2D紋理映射
glEnable(GL_TEXTURE_2D);
  1. 禁用法線
glDisable(GL_NORMALIZE);
  1. 啓用透明測試
glEnable(GL_ALPHA_TEST);
  1. 指定透明色
    glAlphaFunc(GL_GREATER, 0);
  1. 綁定紋理
TEXTURE_PTR texture = font->texture;

glBindTexture(GL_TEXTURE_2D, texture->ID);

  1. 啟用頂點座標數組
glEnableClientState(GL_VERTEX_ARRAY);
  1. 啟用紋理座標數組
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

因為OpenGL『2D相機』原點『0,0』係『左下角』, 返䡛Y軸.

y = Font_frustum_height – y – height;
  1. 逐字渲染『ASCII碼』
for(int index = 0; index < length; ++index ){
  1. 『ASCII碼』轉『紋理索引』, 首ASCII碼係『吉格』,
int c = text[index] – ‘ ‘;
  1. 可渲染拉丁字母96, 『紋理索引』小於0大於96皆唔渲染.
if(c < 0 || c > 96)  continue;
  1. 得到紋理坐標
TEXTURE_REGION_PTR      region = &font->region[c];
  1. 計算2D頂點坐標, 雙『三角形』.
Init_VECTOR2D(&vertex_array[0 * 3 + 0], x, y + height);

Init_VECTOR2D(&vertex_array[0 * 3 + 1], x + width, y + height);

Init_VECTOR2D(&vertex_array[0 * 3 + 2], x + width, y);

Init_VECTOR2D(&vertex_array[1 * 3 + 0], x, y + height);

Init_VECTOR2D(&vertex_array[1 * 3 + 1], x + width, y);

Init_VECTOR2D(&vertex_array[1 * 3 + 2], x, y);

  1. 計算UV紋理坐標, 雙『三角形』.
Init_VECTOR2D(&texCoord_array[0 * 3 + 0], region->u1, region->v2);

Init_VECTOR2D(&texCoord_array[0 * 3 + 1], region->u2, region->v2);

Init_VECTOR2D(&texCoord_array[0 * 3 + 2], region->u2, region->v1);

Init_VECTOR2D(&texCoord_array[1 * 3 + 0], region->u1, region->v2);

Init_VECTOR2D(&texCoord_array[1 * 3 + 1], region->u2, region->v1);

Init_VECTOR2D(&texCoord_array[1 * 3 + 2], region->u1, region->v1);

  1. 指定頂點數組
glVertexPointer(2, GL_FLOAT, 0, vertex_array);
  1. 指定紋理座標
glTexCoordPointer(2, GL_FLOAT, 0, texCoord_array);
  1. 繪畫頂點數組,雙『三角形』.
glDrawArrays(GL_TRIANGLES, 0, 2 * 3 );
  1. 移動下壹渲染位, 分『橫排』同『縱排』
if(VH == FONT_HORIZONTAL)

x = x + width;

else

if(VH == FONT_VERTICAL)

y = y + height;}

  1. 禁用透明測試
glDisable(GL_ALPHA_TEST);
  1. 當前矩陣堆棧出棧
glPopMatrix();

 

ASCII數字 字符
0–31 控制字元,用于控制印表機等周邊設備
32-126 鍵盤字符
127 DELETE 命令
48-57 0-9
65-90 A-Z
97-122 a-z
128-255 擴展ASCII,增加特殊符號字符, 歐語字母和圖形符號

 

Photoshop位圖字符glyph
Pixel 32*32
Font Terminal Regular
Size 4pt
mid
Aa

 

十進制(DEC) 十六進制(HEX) ASCII字符 簡介
0 0x00 空格NUL(null)
1 0x01 標題開始 SOH(start of headling)
2 0x02 正文開始 STX (start of text)
3 0x03 正文結束 ETX (end of text)
4 0x04 傳輸結束 EOT (end of transmission)
5 0x05 請求 ENQ (enquiry)
6 0x06 收到通知 ACK (acknowledge)
7 0x07 響鈴 BEL (bell)
8 0x08 退格 BS (backspace)
9 0x09 水準跳位字元 HT (horizontal tab)
10 0x0A 換行鍵 LF (NL line feed, new line)
11 0x0B 垂直跳位字元 VT (vertical tab)
12 0x0C 換頁鍵 FF (NP form feed, new page)
13 0x0D 回車鍵 CR (carriage return)
14 0x0E 不用切換 SO (shift out)
15 0x0F 啟用切換 SI (shift in)
16 0x10 資料連結轉義DLE (data link escape)
17 0x11 設備控制1 DC1 (device control 1)
18 0x12 設備控制2 DC2 (device control 2)
19 0x13 設備控制3 DC3 (device control 3)
20 0x14 設備控制4 DC4 (device control 4)
21 0x15 拒絕接收 NAK (negative acknowledge)
22 0x16 同步空閒 SYN (synchronous idle)
23 0x17 傳輸塊結束 ETB (end of trans. block)
24 0x18 取消 CAN (cancel)
25 0x19 EM (end of medium)
26 0x1A 替補 SUB (substitute)
27 0x1B 溢出 ESC (escape)
28 0x1C 檔分割符 FS (file separator)
29 0x1D 分組符 GS (group separator)
30 0x1E 記錄分離符 RS (record separator)
31 0x1F 單元分隔符號 US (unit separator)
32 0x20 空格 (space)
33 0x21 ! 嘆號
34 0x22 雙引號
35 0x23 # 井號
36 0x24 $ 美元符
37 0x25 % 百分號
38 0x26 & 和號
39 0x27 閉單引號
40 0x28 ( 開括弧
41 0x29 ) 閉括弧
42 0x2A * 星號
43 0x2B + 加號
44 0x2C , 逗號
45 0x2D 減號/破折號
46 0x2E . 句號
47 0x2F / 斜杠
48 0x30 0
49 0x31 1
50 0x32 2
51 0x33 3
52 0x34 4
53 0x35 5
54 0x36 6
55 0x37 7
56 0x38 8
57 0x39 9
58 0x3A : 冒號
59 0x3B ; 分號
60 0x3C < 小於
61 0x3D = 等號
62 0x3E > 大於
63 0x3F ? 問號
64 0x40 @ 電子郵件符號
65 0x41 A
66 0x42 B
67 0x43 C
68 0x44 D
69 0x45 E
70 0x46 F
71 0x47 G
72 0x48 H
73 0x49 I
74 0x4A J
75 0x4B K
76 0x4C L
77 0x4D M
78 0x4E N
79 0x4F O
80 0x50 P
81 0x51 Q
82 0x52 R
83 0x53 S
84 0x54 T
85 0x55 U
86 0x56 V
87 0x57 W
88 0x58 X
89 0x59 Y
90 0x5A Z
91 0x5B [ 開方括號
92 0x5C \ 反斜杠
93 0x5D ] 閉方括號
94 0x5E ^ 脫字元
95 0x5F _ 下劃線
96 0x60 ` 開單引號
97 0x61 a
98 0x62 b
99 0x63 c
100 0x64 d
101 0x65 e
102 0x66 f
103 0x67 g
104 0x68 h
105 0x69 i
106 0x6A j
107 0x6B k
108 0x6C l
109 0x6D m
110 0x6E n
111 0x6F o
112 0x70 p
113 0x71 q
114 0x72 r
115 0x73 s
116 0x74 t
117 0x75 u
118 0x76 v
119 0x77 w
120 0x78 x
121 0x79 y
122 0x7A z
123 0x7B { 開花括弧
124 0x7C | 垂線
125 0x7D } 閉花括弧
126 0x7E ~ 波浪號
127 0x7F 刪除DEL(DELETE)
128 0x80 Ç Ccedil
129 0x81 ü uuml
130 0x82 é eacute
131 0x83 â circ
132 0x84 ä auml
133 0x85 à agrave
134 0x86 å aring
135 0x87 ç ccedil
136 0x88 ê ecirc
137 0x89 ë euml
138 0x8A è egrave
139 0x8B ï iuml
140 0x8C î icirc
141 0x8D ì igrave
142 0x8E Ä Auml
143 0x8F Å ring
144 0x90 É Eacute
145 0x91 æ aelig
146 0x92 Æ AElig
147 0x93 ô ocirc
148 0x94 ö ouml
149 0x95 ò ograve
150 0x96 û ucirc
151 0x97 ù ugrave
152 0x98 ÿ yuml
153 0x99 Ö Ouml
154 0x9A Ü Uuml
155 0x9B ¢ 美分(cent)
156 0x9C £ 英磅(pound)
157 0x9D ¥ 日元(yen)
158 0x9E
159 0x9F ƒ
160 0xA0 á aacute
161 0xA1 í iacute
162 0xA2 ó oacute
163 0xA3 ú uacute
164 0xA4 ñ ntilde
165 0xA5 Ñ Ntilde
166 0xA6 ª
167 0xA7 º
168 0xA8 ¿
169 0xA9
170 0xAA ¬
171 0xAB ½
172 0xAC ¼
173 0xAD ¡
174 0xAE «
175 0xAF »
176 0xB0
177 0xB1
178 0xB2
179 0xB3
180 0xB4
181 0xB5
182 0xB6
183 0xB7
184 0xB8
185 0xB9
186 0xBA
187 0xBB
188 0xBC
189 0xBD
190 0xBE
191 0xBF
192 0xC0
193 0xC1
194 0xC2
195 0xC3
196 0xC4
197 0xC5
198 0xC6
199 0xC7
200 0xC8
201 0xC9
202 0xCA
203 0xCB
204 0xCC
205 0xCD
206 0xCE
207 0xCF
208 0xD0
209 0xD1
210 0xD2
211 0xD3
212 0xD4 Ô
213 0xD5
214 0xD6
215 0xD7
216 0xD8
217 0xD9
218 0xDA
219 0xDB
220 0xDC
221 0xDD
222 0xDE
223 0xDF
224 0xE0 α 阿爾法(Alpha)
225 0xE1 ß 貝塔(beta)
226 0xE2 Γ Gamma
227 0xE3 π 圓周率(pi)
228 0xE4 Σ sigma
229 0xE5 σ sigma
230 0xE6 µ mu
231 0xE7 τ tau
232 0xE8 Φ PHi
233 0xE9 Θ Theta
234 0xEA Ω 歐米伽(Omega)
235 0xEB δ Delta
236 0xEC 無窮
237 0xED φ phi
238 0xEE ε epsilon
239 0xEF
240 0xF0
241 0xF1 ±
242 0xF2
243 0xF3
244 0xF4
245 0xF5
246 0xF6 ÷
247 0xF7
248 0xF8
249 0xF9
250 0xFA ·
251 0xFB
252 0xFC
253 0xFD ²
254 0xFE
255 0xFF ÿ

 

Android Studio NDK-OpenGL ES 透明glAlphaFunc()

Android Studio NDK-OpenGL ES 透明glAlphaFunc()
Android Studio NDK-OpenGL ES 透明glAlphaFunc()

係2D游戲位圖愛蒞做動畫, 要過濾『背景色』, 係Android OpenGL ES用alpha屏蔽『背景色』, 壹法位圖『A分量』Alpha = 0, 貳法黑色定為『背景色』係載入時將『A分量』Alpha = 0. 所以兩法要係『GL_RGBA』模式運運作.

RGBA8888位圖,RGB占24bit, Alpha 值A占8bit, 透明度範圍『0~255』. 『0』通透.『 255』實體.

設Alpha值 通透 實體
glAlphaFunc() 0.0 1.0
glAlphaFuncx() 0 255

若位圖冇Alpha 值, OpenGL ES 係載入時將Alpha值設1.

BGRA8888/ARGB8888 Alpha = 0
RGB656,RGB888,index R=0,G=0,B=0, 黑色
  1.  載入纹理圖形卡『RAM』, 設GL_RGBA模式
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width , height , 0, GL_RGBA, GL_UNSIGNED_BYTE,  image);
  1. 啓用透明測試
glEnable(GL_ALPHA_TEST);
  1. 設Alpha值過濾透明色
glAlphaFunc(GL_GREATER, 0);
glAlphaFuncx(GL_GREATER, 0);

 

Android Studio NDK-OpenGL ES-2D相機

Android Studio NDK-OpenGL ES-2D相機
Android Studio NDK-OpenGL ES-2D相機

OpenGL有『平行投影』同『透視投影』, 2D相機係『平行投影』生成方盒『視體』, 愛蒞剪裁物體, 唔係『視體』內唔『渲染』. 愛蒞『等比例游戲』『平面游戲』

OpenGL『2D相機』原點『0,0』係『左下角』, z軸遠端係負,近端正.

Android『熒屏』原點『0,0』係『左上角』,

  1. 首先指定『矩陣堆棧』為『投影矩陣堆棧』
glMatrixMode(GL_PROJECTION);
  1. 清除殘留矩陣并載入『單位矩陣』,.
glLoadIdentity();
  1. 重置視區尺寸,『視區』變換係『投影』變換之後,『x,y』左下角『0,0』. 『width,height』同視窗尺寸相應『熒屏像素』, 除非想係視窗一部分區域繪畫, 可設較定較細『視區』.
glViewport(x, y, window_width, window_height);
  1. 『平行投影』定方盒『視體』愛蒞剪裁物體, 而唔係3D相機錐形.『left, bottom』指左下角『0, 0』,『right, top』指右上角『width, height』. 熒屏尺寸設『800,600』或『640,480』. 游戲世界尺寸設『80,60』或『64,48』. OpenGL會自動放大與視區相配.『near, far』z軸剪裁面『1, -1』
glOrthof (left,right,bottom,top,near,far);
glOrthof (0, frustum_width, 0, frustum_height, 1, -1);
  1. 指定『視體』中心『x, y』.移動2D相機
glOrthof (x- width/2,  x+width/x, y-height/2, y+height/2, 1, -1);

 

glOrthof()  
『left, bottom』 左下角『0, 0』
『right, top』 右上角『width, height』
near z軸近端剪裁面
far z軸遠端剪裁面
  1. 恢復『矩陣堆棧』為『視圖矩陣堆棧』
glMatrixMode(GL_MODELVIEW);
  1. 載入『單位矩陣』,清除殘留矩陣
glLoadIdentity();

示例

void  Projection_Camera2D(CAMERA2D_PTR cam){

// 重置視區尺寸

::glViewport(0,0,cam->real_width,cam->real_height);

// 設爲投影矩陣

::glMatrixMode(GL_PROJECTION);

// 載入單位矩陣

::glLoadIdentity();

// 正交投影

glOrthof (0, cam->frustum_width, 0, cam->frustum_height, 1, -1);

// 設定模型視圖矩陣

::glMatrixMode(GL_MODELVIEW);

// 載入單位矩陣

::glLoadIdentity();

}

 

Android Studio NDK-OpenGL ES封裝SurfaceView

Android Studio NDK-OpenGL ES封裝SurfaceView
Android Studio NDK-OpenGL ES封裝SurfaceView

係『Android OpenGL ES』用『SurfaceView』蒞畀『OpenGL ES』渲染. 之前『SurfaceView』代碼擺係『MainActivity.java』.更佳方法係『SurfaceView』『Runnable』『SurfaceHolder.Callback』封裝成單獨『GameView calss』.以保持代碼『簡潔』同埋『重用』.

  1. 新建『java』
『File->New->Java Class』
  1. 封裝『Runnable』『Callback』
public class GameView extends SurfaceView implements Runnable,SurfaceHolder.Callback{
  1. 『SurfaceView』變量
int view_width,view_height;

public SurfaceHolder surfaceHolder;

public Surface surface;//用蒞渲染

  1. 構造函數
public GameView(Context context) {

super(context);

init(context);// 設置渲染

}

public GameView(Context context, AttributeSet attrs) {

super(context);

init(context);// 設置渲染

}

public GameView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context);

init(context);// 設置渲染

}

  1. 初此渲染设置
public void init(Context context){

this.surfaceHolder = getHolder();

this.surfaceHolder.addCallback(this);

this.surface = surfaceHolder.getSurface();

}

  1. 創建表層,啟動渲染線程 run()
@Override

public void surfaceCreated(SurfaceHolder surfaceHolder){

new Thread(this).start();

}

  1. 表層改變, 當手機方向改變触發, 係游戲要禁止旋轉方向.
 @Override

public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height){

view_width = width;

view_height = height;

}

  1. 表層銷毀
@Override

public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

}

  1. 游戲運算渲染線程
@Override

public void run() {

AssetManager assetManager = getContext().getAssets();

Lib.init(assetManager, surface, view_width, view_height);

while (true) {// 請求渲染

Lib.update();

}

}

 

Android Studio NDK-OpenGL ES 『正交投影』

Android Studio NDK-OpenGL ES 『正交投影』
Android Studio NDK-OpenGL ES 『正交投影』

3D游戲基于『透視投影』產生立體效果, 而2D游戲戲用『正交投影』產生平面效果, 生成等比例游戲.

  1. 指定視區像素尺碼, 『x』 『y』視區左下角,『width』 『height』視區寬高
glViewport(0,0,width, height);
  1. 設定『矩陣堆栈』為『投影矩陣堆栈』
glMatrixMode(GL_PROJECTION);
  1. 載入單位矩陣, 清除『堆栈』残留『矩陣』,以此唔受先前變换運算影响.
glLoadIdentity();
  1. 『正交投影』運算, Y軸視角『fovy』,Z軸視角『aspect』, 『zNear』近裁剪面距离约摸箱機方向Y軸加 『zFar』远裁剪面距离設較大数值.
gluOrtho( fovy, aspect, zNear, zFar );
  1. 設定『矩陣堆栈』為『模型視圖矩陣堆栈』
glMatrixMode(GL_MODELVIEW);
  1. 載入單位矩陣
glLoadIdentity();

 

基於視角『正交投影』

void gluOrtho(double  fovy,double aspect,double zNear,double zFar){

double  xmin, xmax, ymin, ymax;

ymax = zNear * tan(fovy * 3.141592654f / 360.0f);

ymin = -ymax;

xmin = ymin * aspect;

xmax = ymax * aspect;

glOrthof(xmin, xmax, ymin, ymax, zNear, zFar);

}

 

Android Fullscreen 全屏樣式

Android Fullscreen 全屏樣式
Android Fullscreen 全屏樣式

Android Studio設置全屏,通過編輯『themes.xml』

『themes.xml』
<style  name=”Theme.Fullscreen”  parent=”android:Theme.NoTitleBar.Fullscreen”>

編輯『AndroidManifest.xml』

『AndroidManifest.xml』
<application android:theme=”@style/Theme.Fullscreen”>

代碼係『eclipse』移稙過蒞, 『Android Studio』程式閃退. 因『MainActivity』繼承『AppCompatActivity』造成. 改為繼承『Activity』.

public class MainActivity extends  Activity

 

Android Studio NDK-OpenGL ES/EGL渲染

Android Studio NDK-OpenGL ES/EGL渲染
Android Studio NDK-OpenGL ES/EGL渲染

OpenGL EGL作為平臺冇関 API, 今程序冇視『Linux X Window』『Microsoft Windows』『Mac OS X Quatz』差異.用統壹接口同原生視窗聯接. 跨平臺API更易於移值. 所以OpenGL比DirectX更得人鐘意.

  1. 使用NDK
  2. 編譯OpenGL ES
  3. 布局OpenGL ES
  4. 先加入頭文檔
#include <GLES/gl.h> 標準OpenGL頭文檔
#include <GLES/glext.h> OpenGL實用架餐庫
#include <EGL/egl.h> EGL庫
#include <EGL/eglext.h> EGL架餐庫
#include <android/native_window_jni.h> 原生視窗庫ANativeWindow
  1. 冚辦爛EGL程式首先調用『eglGetDisplay()』, 得到『EGLDisplay』等同『原生視窗』, 『EGLDisplay』係整數索引.
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  1. 索引大於零即成功, 因為等於零失敗.
EGL_NO_DISPLAY 0失敗
  1. 初始EGL程式, 幷獲得『major』主版號 , 『minor』副版號
EGLint major,minor;

eglInitialize(display,&major,&minor)  ;

  1. 失敗返回EGL_FALSE, 查錯誤碼eglGetError()
EGL_BAD_DISPLAY 非有效EGLDisplay
EGL_NOT_INITALIZED 未能初始
  1. 穩『surface』渲染配置
EGLBoolean eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
  1. EglChooseConfig()函式參數
參數 簡介
EGLDisplay dpy 『原生視窗』索引
EGLint *attrib_list 指定屬性列表, 以EGL_NONE結屘
EGLConfig *configs 返回配置列表
EGLint config_size 指定配置列表長度
EGLint *num_config 返回配置長度
  1. 『EGLConfig』屬性
屬性 描述
EGL_SURFACE_TYPE EGL渲染表面類型 EGL_WINDOW_BIT
EGL_RENDERABLE_TYPE OpenGL版本 EGL_OPENGL_ES_BIT
EGL_RED_SIZE 紅色量位 8Bit
EGL_GREEN_SIZE 綠色量位 8Bit
EGL_BLUE_SIZE 藍色量位 8Bit
EGL_ALPHA_SIZE 透明量位 8Bit
EGL_DEPTH_SIZE 深度量位 16Bit
EGL_NONE 屬性列表以EGL_NONE結屘 0

屬性列表

EGLint    attrib_array[32];
  1. 設定『EGLConfig』屬性
attrib_array[0] = EGL_SURFACE_TYPE; attrib_array[1] = EGL_WINDOW_BIT;
attrib_array[2] = EGL_RENDERABLE_TYPE; attrib_array[3] = EGL_OPENGL_ES_BIT;
attrib_array[4] = EGL_RED_SIZE; attrib_array[5] = 8;
attrib_array[6] = EGL_GREEN_SIZE; attrib_array[7] = 8,
attrib_array[8] = EGL_BLUE_SIZE; attrib_array[9] = 8;
attrib_array[10] = EGL_ALPHA_SIZE; attrib_array[11] = 8;
attrib_array[12] = EGL_DEPTH_SIZE; attrib_array[13] = 16;
attrib_array[14] = EGL_NONE;  
  1. 據 attrib_array 返回最隹配置config
EGLint    config_number = 32;

EGLConfig config_array[32];

EGLConfig  config;// 配置

eglChooseConfig(display,attrib_array,config_array,config_number,&config_number);

  1. 匹配最佳配置
for(int i=0;i<config_number;++i)    {

eglGetConfigAttrib(openGL->display, config_array[i],EGL_RED_SIZE, &red);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_GREEN_SIZE, &green);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_BLUE_SIZE, &blue);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_ALPHA_SIZE, &alpha);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_DEPTH_SIZE, &depth);

if( red == 8 && green == 8 && blue == 8 && alpha == 8 && depth == 16){

openGL->config = config_array[i];// 最佳配置

break;

}

}

  1. 生成渲染視窗
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
  1. eglCreateWindowSurface()函式參數
參數 簡介
EGLDisplay dpy 『原生視窗』索引
EGLConfig config 最佳配置
EGLNativeWindowType win SurfaceView
const EGLint *attrib_list 屬性列表
  1. 渲染視窗屬性
視窗屬性 描述
EGL_RENDER_BUFFER 渲染緩存 EGL_SINGLE_BUFFER『單緩存』EGL_BACK_BUFFER『雙緩存』
EGL_NONE 屬性列表以EGL_NONE結屘 0
  1. 屬性
attrib_array[0] = EGL_RENDER_BUFFER; attrib_array[1] = EGL_BACK_BUFFER;
attrib_array[2] = EGL_NONE;  
  1. 生成渲染視窗
surface_draw = eglCreateWindowSurface(display,config,nativeWindow,attrib_array);
  1. 獲得錯誤碼
錯誤碼EGLint error = eglGetError(); 簡介
EGL_BAD_MATCH 視窗唔匹配EGLConfig唔支持渲染
EGL_BAD_CONFIG 視窗唔匹配EGLConfig系統晤支持
EGL_BAD_NATIVE_WINDOW 原生視窗句柄冇效
EGL_BAD_ALLOC 冇法分配資源
  1. 關聯描述表, 将『EGLContext』『EGLSurface』與當前線程關聯, 『EGL』同『OpenGL』代碼需擺同壹綫程. 唔系eglSwapBuffers()返回
eglMakeCurrent(display,surface_draw,surface_draw,context);
  1. 『Native』層用『ANativeWindow 』蒞渲染.通過『Surface』得到『ANativeWindow』.
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface)
  1. Android 『java』層用『SurfaceView』蒞渲染, 提取『Surface』畀『Native』
SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surface_view);
SurfaceHolder     surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
Surface             surface = surfaceHolder.getSurface();

Android Studio NDK-OpenGL ES布局

Android Studio NDK-OpenGL ES布局
Android Studio NDK-OpenGL ES布局

『Android Studio NDK』提供『EGL』連接『OpenGL』, 『EGL』被設計出來,作爲 OpenGL 和原生窗口系統之間的橋梁『Microsoft Windows』『Mac OS X Quatz』差異.用統壹接口同原生視窗聯接. 跨平臺API更易於移值. 所以OpenGL比DirectX更得人鐘意.

  1. 使用NDK
  2. 編譯OpenGL ES
  3. 載入共享庫”app.so”,『app』系你庫名.
static {

System.loadLibrary(“app”);

}

  1. 首先修改布局『xml』添加『SurfaceView』,而非『GLSurfaceView』.
 <android.view.SurfaceView

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:id=”@+id/surface_view” />

  1. 係『xml』添加『全屏樣式』
<style  name=”FullscreenTheme”  parent=”android:Theme.NoTitleBar.Fullscreen”  >
  1. 係『java』加『android.view.SurfaceHolder.Callback』用蒞行『SurfaceView』『創建』『改變』『銷毀』
        @Override

public void surfaceCreated(SurfaceHolder surfaceHolder) {

new Thread(this).start();//渲染,啟動線程  Runnable.run()

}

        @Override

public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

}

        @Override

public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

}

  1. 係『java』加『Runnable』用蒞行OpenGL 渲染線程, 『EGL』同『OpenGL』代碼需擺同壹綫程. 唔系eglSwapBuffers()返回EGL_BAD_SURFACE.
    @Override

public void run() {

init( );

while (true) {

update(0);

}

}

  1. 係『xml』為Google Play加版本過濾
<uses-sdk        android:minSdkVersion=”9″       android:targetSdkVersion=”19″ />
  1. 係『txt』穩庫文檔
find_library( OpenGL-lib libGLESv1_CM.so  )
find_library( OpenEGL-lib libEGL.so )
find_library( Android-lib libandroid.so )
  1. 係『txt』連連接庫文檔
target_link_libraries(app

${OpenGL-lib}

${OpenEGL-lib}

${Android-lib})

  1. 加入頭文檔
#include <GLES/gl.h> 標準OpenGL頭文檔
#include <GLES/glext.h> OpenGL架餐庫
#include <EGL/egl.h> EGL頭文檔
#include <EGL/eglext.h> EGL架餐庫
#include <android/native_window_jni.h> 原生視窗庫

 

Android Studio NDK-OpenGL ES-3D相機gluPerspective()

Android Studio NDK-OpenGL ES-3D相機gluPerspective()
Android Studio NDK-OpenGL ES-3D相機gluPerspective()

將『OpenGL』移稙過『Andord』. 但OpenGL ES偏偏冇gluPerspective()視錐投影.但有glFrustumf()設定視口.

通過glFrustumf()設置視口, 得到 gluPerspective()視錐投影.
void gluPerspective(double  fovy,double aspect,double zNear,double zFar){

double  ymax = zNear * tan(fovy * 3.141592654f / 360.0f);

double  ymin = -ymax;

double    xmin = ymin * aspect;

double    xmax = ymax * aspect;

glFrustumf(xmin, xmax, ymin, ymax, zNear, zFar);

}

3D相機投影代碼

glViewport(0,0,viewport_width,viewport_height); 重置視區尺寸
glMatrixMode(GL_PROJECTION); 設定投影矩陣
glLoadIdentity(); 載入單位矩陣
gluPerspective(fov,aspect_ratio,near_clip_z,far_clip_z); 設置視錐體投影
glMatrixMode(GL_MODELVIEW); 設定模型視圖矩陣
glLoadIdentity(); 載入單位矩陣

 

Android Studio NDK-OpenGL-交換緩存畫面eglSwapBuffers()返回EGL_BAD_SURFACE

Android Studio NDK-OpenGL-交换緩存画面eglSwapBuffers()返回EGL_BAD_SURFACE
Android Studio NDK-OpenGL-交换緩存画面eglSwapBuffers()返回EGL_BAD_SURFACE

將『風水羅盤』由『Windows』移稙過『Andord』. 係交換緩存畫面『eglSwapBuffers()』返回『EGL_FALSE』, 『eglGetError()』返回『EGL_BAD_SURFACE』.

EGLBoolean ret = eglSwapBuffers(display,surface);

if(ret == EGL_FALSE){

GLint error = eglGetError();

if(error == EGL_BAD_SURFACE){

}

}

係『Windows』有『Winmain()』入口. 可以控制冚個整游戲運作, 係 『Winmain()』 游戲『運行』『渲染』都係主線程行.

而 『Andord』冇『Winmain()』. 游戲『運行』『渲染』要係新線程『Thread()』行. 而『EGLContext』同『EGLSurface』需係同壹線程『Thread()』生成, 唔係『eglSwapBuffers()』投『EGL_BAD_SURFACE』錯誤碼.

知錯係邊就易整, 將egl()程式擺係新線程『Thread()』行,而非『 onSurfaceCreated()』.

新線程『Thread()』
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);
EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface);
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface);

 

 

Android Studio NDK- assets訪問讀

Android Studio NDK- assets讀係Android游戲唔將游戲資源擺係『res』而係『assets』, 事因『res』限制層次結構, 而『assets』允許任意檔案資料夾層次結構.位於『assets』檔案只讀唔寫. NDK需android 9版先支持.

係『CMakeLists.txt』添加『libandroid.so』庫

CMakeLists.txt
find_library( Android-lib libandroid.so )
target_link_libraries( geomanticcompass

${OpenGL-lib}

${OpenEGL-lib}

${Android-lib}

${log-lib})

係c檔案加入頭檔

#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>

『assets』檔通過『AssetManager』訪問

AssetManager assetManager = this.getAssets();

係『NDK』『assets』檔通過『AAssetManager』訪問, 『AAssetManager』接口

AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);

將『AssetManager』傳過去獲得『AAssetManager』

extern “C”

JNIEXPORT void JNICALL Java_net_bookcard_geomanticcompass_MainActivity_init(JNIEnv *env, jobject thiz, jobject egl_config, jobject asset_manager) {

AAssetManager *assetManager= AAssetManager_fromJava(env, asset_manager);

}

public native void init(AssetManager asset_manager);
init (getAssets());

 

開啟『assets』檔, 冇需畀絕對路徑, 而係畀『assets』相對路俓

AAsset *asset = AAssetManager_open(nativeasset, “name.bmp”, AASSET_MODE_BUFFER);

檔长度

int size = AAsset_getLength(asset)

分配記憶體

PBYTE data = (PBYTE)malloc(size);

讀『assets』檔

AAsset_read(asset, data, size);

載入分析

Load(data, size);

释放记忆体

free(data);

閂『assets』檔

AAsset_close(asset);

 

將游戲資源擺係『assets』下,『assets』『res』同位於『app\src\main』之下.

檔案資料夾 位置
assets D:\Android\game\app\src\main\assets
cpp D:\Android\game\app\src\main\cpp
java D:\Android\game\app\src\main\java
res D:\Android\game\app\src\main\res

 

 

Android Studio NDK-OpenGL ES替換gluLookAt()

OpenGL ES替換gluLookAt()
OpenGL ES替換gluLookAt()

gluLookAt()  UVN相機模型,設定視點(相機)位置和視綫方向(矩陣運算).

void gluLookAt( GLdouble eyeX,

GLdouble eyeY,

GLdouble eyeZ,

GLdouble centerX,

GLdouble centerY,

GLdouble centerZ,

GLdouble upX,

GLdouble upY,

GLdouble upZ);

OpenGL ES冇gluLookAt() , 手工生成『旋轉矩陣』『相機位置』函式

void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,

GLfloat centerx, GLfloat centery, GLfloat centerz,

GLfloat upx, GLfloat upy, GLfloat upz)

{

GLfloat m[16];// 旋轉矩陣

GLfloat x[3], y[3], z[3];

GLfloat mag;

//生成旋轉矩陣

// Z矢量

z[0] = eyex – centerx;

z[1] = eyey – centery;

z[2] = eyez – centerz;

mag = sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);

if (mag) {

z[0]/= mag;

z[1]/= mag;

z[2]/= mag;

}

 

// Y矢量

y[0] = upx;

y[1] = upy;

y[2] = upz;

 

// X 矢量 = Y 叉積 Z

x[0] = y[1] * z[2] – y[2] * z[1];

x[1] = -y[0] * z[2] + y[2] * z[0];

x[2] = y[0] * z[1] – y[1] * z[0];

 

// 重新計算 Y = Z 叉積 X

y[0] = z[1] * x[2] – z[2] * x[1];

y[1] = -z[0] * x[2] + z[2] * x[0];

y[2] = z[0] * x[1] – z[1] * x[0];

 

 

// 叉積給出了平行四邊形的面積,對于非垂直單位長度向量;所以在這裏標準化x,y

mag = sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);

if (mag) {

x[0]/= mag;

x[1]/= mag;

x[2]/= mag;

}

 

mag = sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);

if (mag) {

y[0]/= mag;

y[1]/= mag;

y[2]/= mag;

}

 

#define M(row,col)  m[col*4+row]

M(0, 0) = x[0];

M(0, 1) = x[1];

M(0, 2) = x[2];

M(0, 3) = 0.0;

M(1, 0) = y[0];

M(1, 1) = y[1];

M(1, 2) = y[2];

M(1, 3) = 0.0;

M(2, 0) = z[0];

M(2, 1) = z[1];

M(2, 2) = z[2];

M(2, 3) = 0.0;

M(3, 0) = 0.0;

M(3, 1) = 0.0;

M(3, 2) = 0.0;

M(3, 3) = 1.0;

#undef M

glMultMatrixf(m);

 

// 視點(相機)位置

glTranslatef(-eyex, -eyey, -eyez);

}

 

3ds Max導出MD2檔

3ds Max導出MD2檔啉住將3D模型導出『.OBJ』但寫『解碼』費時.更好方法導出『MD2』模型. 『MD2』仍名作『Quake2』專屬3D模型.有大量書籍講解『MD2』模型結構.

  1. 首先網絡下載『ms』與『md2Export.ms』.
  2. 『ms』與『md2Export.ms』屬於Script腳本.需複製到『C:\Program Files\Autodesk\3ds Max \scripts\startup』
  3. 重啟『3ds Max』
  4. 撳Q選擇3D模型.
  5. 撳『Edit mesh編輯網格
  6. 撳『Utilities』錘子圖標
  7. 撳『MAX Script』->『Utilities』選MD2 Exporter.激活『MD2 Exporter』Script腳本.
  8. 撳『Export』導出MD2模型檔.
 Save Animation 『保存動畫』勾選激活,導出動畫.
Frame Step 『幀步』.默認1.
Active Time Segment 導出『冚辦爛』活動時間段
Custom Time Segment 導出指定時間段
Generate Normals 『生成法線』勾選激活
Export 『導出』MD2模型檔.

 

3ds Max 2009 C:\Program Files\Autodesk\3ds Max 2009\scripts\startup
3ds Max 2018 C:\Program Files\Autodesk\3ds Max 2018\scripts\startup

 

OpenGL相機之神睇

OpenGL相機之神睇人睇世界為平視,神睇世界則高處俯視.OpenGL用UVN相機gluLookAt()用於指定相機視線. 神位於10米高,處垂直俯視地表即『視線』設(0.0f,0.0f,0.0f).將『頭頂指向』設(0.0f,0.0f,1.0f) .

void gluLookAt(0.0f, 10.0f, 0.0f,    0.0f, 0.0f, 0.0f,   0.0f,0.0f,1.0f);
void gluLookAt(GLdouble eyeX,GLdouble eyeY,GLdouble eyeZ,

GLdouble centerX,GLdouble centerY,GLdouble centerZ,

GLdouble upX,GLdouble upY,GLdouble upZ);

eyeX, eyeY, eyeZ 神目位置.即相機位置. 世界坐標位置
centerX, centerY, centerZ 『視點』指向位置.即『視線』. 世界坐標位置
upX, upY, upZ 神頭頂指向,設(0.0f,0.1f,0.0f)

用『歐拉』Euler相機模型則方便直接繞X軸轉90度.

glRotatef(90.0f, 1.0f, 0.0f, 0.0f); 傾斜/俯仰繞X軸旋轉的角度
glRotatef(0.0f, 0.0f, 0.0f, 1.0f); 橫搖/側滾繞Z軸旋轉的角度
glRotatef(0.0f, 0.0f, 1.0f, 0.0f); 偏航/航向繞Y軸旋轉的角度
glTranslatef(-x,-y,-z); 移動模型坐標

 

Android遊戲之紋理鏈Mipmap

Android遊戲之紋理鏈Mipmap

當相機遠離模型時,模型也會變小.其紋理渲染採樣時會顆粒狀失真.解決失真問題關鍵在於讓屏幕中體積較小物體或遠離視點時.使用低分辯率『紋理Texture』圖像.稱值為紋理鏈.首先獲取紋理尺寸.然後創建比小分辨率紋理圖,把分辨率縮小一半.重複這一過程直到分辨率為1.為了在OpenGL ES使用紋理鏈,需要執行以下兩步

  1. 將縮小係數過濾器設置為GL_XXX_MIPMAP_XXX這裡設置為如果不使用MIPMAP.只會使用首個紋理
  2. 通過縮小紋理創建圖鏈.並將圖片上傳提交給OpenGL ES.作為當前圖層.圖層從0開此,0圖層為原始圖層,然後上傳圖層.然後將其寬度與高除以2不斷創建縮小圖層並上傳.回收位圖.直到寬與高等於零.最後一張紋理1*1像素.則退出循環.並且紋理鏈只能綁定單一紋理
  3. 對於3D模形使用紋理鏈mipmap,而在2D材質無需應用
  4. 若啟用mipmap所繪製物較小.性能提升較明顯.因為GPU只需從小圖片中提取紋理元素
  5. 若啟用mipmap紋理鏈會比沒有使用多佔用33%記憶體.但可換來視角效果提升.特別對遠景物體可修正粒狀失真.
  6. mipmap紋理鏈僅對正方形紋理有效.並且僅在OpenGL ES 1.x獲得支持.

 

紋理位圖載入與綁定.在其基礎加入紋理鏈代碼

GL10 gl = GRAPHICS.gl;

生成空的紋理對象,並獲取id

gl.glGenTextures(1, ids,0);

int id = ids[0];

讀取紋理圖

Bitmap  bitmap = BITMAP.Read(file_name);

綁定紋理ID

gl.glBindTexture(GL10.GL_TEXTURE_2D, id);

設置紋理屬性,紋理過濾器,指定縮小倍數過濾器

gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_NEAREST);

指定放大倍數過濾器

gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

獲取紋理寬度與高度

int    width = bitmap.getWidth();

int    height = bitmap.getHeight();

原始圖層索引為0

int level = 0;

while(true) {

上傳紋理

GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);

圖層索引號加一

++level;

計算縮小一半紋理位圖寬與高

int newWidth = bitmap.getWidth()/2;

int newHeight = bitmap.getHeight()/2;

寬與高等於零時跳出循環

if(newWidth == 0)

break;

創建縮小一半紋理位圖

Bitmap  newBitmap = Bitmap.createBitmap(newWidth,newHeight,bitmap.getConfig());

Canvas canvas = new Canvas(newBitmap);

canvas.drawBitmap(bitmap,

new Rect(0,0,bitmap.getWidth(),bitmap.getHeight()),

new Rect(0,0,newWidth,newHeight),

null);

回收位圖資源

bitmap.recycle();

bitmap = newBitmap;

}

取消邦定紋理ID

gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);

回收位圖資源

bitmap.recycle();

 

Android遊戲之射燈

Android遊戲之射燈

射燈是OpenGL ES中燈光中最耗GPU資源燈光.但其3D效果十分逼真.睇上圖.射燈有多個參數需指定.『位置』『照射方向』『照射角度』小於180度、『衰減指數』小於零,若設為0則亮度不衰減,實制『衰減值』與遊戲3D空間尺寸有關.這裡設為0.01.以及三個光照顏色『環境色』『漫反射色』『鏡面反射色』

public class LightSpot {

private float[] ambient  = {1.0f, 1.0f, 1.0f, 1.0f};// 射燈-環境色

private float[] diffuse  = {1.0f, 1.0f, 1.0f, 1.0f};// 射燈-漫反射色

private float[] specular = {0.0f, 0.0f, 0.0f, 1.0f};// 射燈-高光顏色

private float[] position = {0.0f, 3.0f, 0.0f, 1.0f};// 射燈-位置

private float[] direction = {0.0f, -1.0f, 0.0f, 0.0f};// 射燈-方向

private float  cutoff    = 45;// 射燈-角度範圍,缺省為180.0

private float  exponent  = 0.01f;// 衰減指數,缺省為0.0f

int     id = 0;// 光照ID

// 射燈ID 輸入:GL10.GL_LIGHT0至GL10.GL_LIGHT7

LightSpot(int ID){

this.id = ID;

}

//設置射燈方向,轉換為方向向量.最後w分量設0,代表方向

public void setDirection(float x,float y,float z){

direction[0] = x – position[0];

direction[1] = y – position[1];

direction[2] = z – position[2];

direction[3] = 0;

}

// 設定射燈位置.w分量設為1代表位置向量

public void setPosition(float x,float y,float z){

position[0] = x;

position[1] = y;

position[2] = z;

position[3] = 1;

}

// 設定射燈顏色

public void setColor(float r,float g,float b){

ambient[0] = r;

ambient[1] = g;

ambient[2] = b;

ambient[3] = 1;

}

//使能射燈

public void enable(){

GL10 gl = GRAPHICS.gl;

gl.glEnable(id);//使能

gl.glLightfv(id,GL10.GL_AMBIENT, ambient, 0);// 射燈-環境色

gl.glLightfv(id,GL10.GL_DIFFUSE, diffuse, 0);// 射燈-漫反射色

gl.glLightfv(id,GL10.GL_SPECULAR, specular, 0);// 射燈-高光顏色

gl.glLightfv(id,GL10.GL_POSITION, position, 0);// 位置

gl.glLightfv(id,GL10.GL_SPOT_DIRECTION, direction, 0);// 方向

gl.glLightf(id,GL10.GL_SPOT_CUTOFF,cutoff); //  角度範圍

gl.glLightf(id,GL10.GL_SPOT_EXPONENT,exponent);// 衰減指數

}

// 屏蔽射燈

public void disable(){

GL10 gl = GRAPHICS.gl;

gl.glDisable(id);

}

}

Android遊戲之材質

Android遊戲之材質

物體都由特定材質構成.材質決定照射在物體上光返射方式並會改變反射光顏色.材質為多邊形設置材質屬性用於光照計算,它是全域性影響所有繪製多邊形,直到它在次調用.OpenGL ES中需要為每種材質指定『環境色』『漫反射色』『鏡面反射色』RGBA顏色值.此材質吸收光.只有『光源顏色』與『材質顏色』最小值運算(RGB值)得到『反射光顏色』

材質顏色 光源顏色 反射光顏色『最小值運算』
(0,1,0) (1,1,1) (0,1,0)
(0,1,0) (1,0,0) (0,0,0)
(0,0,1) (1,1,1) (0,0,1)

材質類代碼

public class Material {

private float[] ambient  = {1.0f, 1.0f, 1.0f, 1.0f};// 材質-環境色

private float[] diffuse  = {1.0f, 1.0f, 1.0f,  1.0f};// 材質-漫反射色

private float[] specular = {1.0f, 1.0f, 1.0f, 1.0f};// 材質-鏡面顏色

//設置材質環境色

public void setAmbient(float r,float g,float b){

ambient[0]=r;

ambient[1]=g;

ambient[2]=b;

ambient[3]=1;

}

// 設置材質漫反射色

public void setDiffuse(float r,float g,float b){

diffuse[0]=r;

diffuse[1]=g;

diffuse[2]=b;

diffuse[3]=1;

}

// 設置材質鏡面反射色

public void setSpecular(float r,float g,float b){

specular[0]=r;

specular[1]=g;

specular[2]=b;

specular[3]=1;

}

//使能材質

public void enable(){

GL10 gl = GRAPHICS.gl;

gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_AMBIENT,  ambient, 0);// 環境色

gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_DIFFUSE,  diffuse, 0);//  漫反射色

gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_SPECULAR, specular,0);//  鏡面反射色

}

}

Android遊戲之環境光

Android遊戲之環境光

環境光是一種特殊光.它沒有位置和方向.它只會均勻照射3D空間中所有物體.在OpenGL ES中啟用全域環境光.

啟用光照

gl.glEnable(GL10.GL_LIGHTING);

環境光純白色,色域範圍為0~1浮點數.影射對應0~255整數

float[] color = {1.0f,1.0f,1.0f,1f};// 環境光浮點數組

設定環境光最後參數color偏移量通常設為0

gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, color, 0);

全域環境光代碼

public class LightAmbient {

static private float[] color = {1.0f,1.0f,1.0f,1f};// 環境光

//設定環境光

static public void setColor(float r,float g,float b ){

color[0] = r;

color[1] = g;

color[2] = b;

color[3] = 1;

}

//使能環境光

static public void enable(){

GL10 gl = GRAPHICS.gl;

gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, color, 0);

}

}

Android遊戲之光照

Android遊戲之光照

光照系統它可以令3D遊戲更加逼真.要模擬光照需要光源發射光線.和被光照照射物.最後需要一台相機捕足光源發射光以及被物體反射光.光照會改變觀察者對被觀察者物體顏色感受.取卻於以下幾個因素

  1. 光源類型
  2. 光源顏色和強度
  3. 光源相對於被照射物體位置和方向
  4. 被照射物材質和紋理

被照射物體反射光強度取決於光照射到至物體平面時光與物體平面夾角.光與其所照射平面越接近垂直,物體表面反射光強度越大

一旦光照射到平面它會以兩種方式反射.鏡面反射會物體上表現出強光效果.物體鏡面反射效果取決於材質.

漫反射:大部分反射光線會因為物體不規則表面而隨機地發散開來,具有光滑表面

鏡面反射:光照射到光滑鏡面後返射回來,具有粗糙不平整表面是不可能形成

而光照射到表面時反射光顏色還取決於光源和材質顏色.

OpenGL ES可以創建出四種不同光源

環境光源:環境光本身非光源,而是由所在環境中其它光源發出光反射在周圍得到.這些環境光混合形成照明效果.環境光無方向並且被環境光照射所有物體都有共同亮度.

點光源:點光源在空間中有固定位置,並且向個方向照射.如燈泡

定向光源:定向光需要一個方向並延伸至無限遠.如太陽是標準定向光源

投射光源:在3D空間中有固定位置.並且有個照射方向.並且具有錐形照射區域.如街燈就是標準投射光.但很耗GPU資源.

OpenGL ES允許指定光顏色與強度,使用RGBA指定顏色

環境光色:被照射無體整體受到光色.物體將會接受這種顏色照射.並且與光源位置和方向無關.

漫反射色:物體反射時受到光色.背著光源另一面不會被照射到

鏡面反射色:鏡面反射色僅僅影響面向觀察者和光源面

投射色:僅影響錐型照射物

啟用光照,一旦開啟光照系統將被應用於所有渲染.你還需要指定光源與材質以及頂點法線確定最後光照效果

GL10.glEnable(GL10.GL_LIGHTING);

渲染完成必需關閉光照.否則影響之後GUI渲染.

GL10.glDisable(GL10.GL_LIGHTING);

OpenGL ES允許每個場景中最多使用8個光源,外加一個全域光源.光源ID從GL10.GL_LIGHT0至GL10.GL_LIGHT7

光源0使能並將其應用所有渲染物

GL10.glEnable(GL10.GL_LIGHT0);

若想單獨禁用某光源

GL10.glDisable(GL10.GL_LIGHT0);

光源實現代碼
環境光
定向光
燈光
射燈
材質

 

 

Android遊戲之矩陣和變換

Android遊戲之矩陣和變換

OpenGL ES矩陣提供以下運算能力

  1. 『矩陣』可將『頂點』移動glTranslatef()
  2. 『矩陣』縮放『頂點』,即將『頂點』個座標分量剩以縮放值glScalef()
  3. 『矩陣』可令『頂點』繞某軸旋轉glRotatef()
  4. 『頂點』剩以『單位矩陣』相當於剩 glLoadIdentity()
  5. 兩個『矩陣』相剩得到新『矩陣』剩以某個頂點.相當於兩『矩陣』剩以頂點後再相剩glMultMatrixf()

OpenGL ES提供有三種『矩陣』

投影矩陣:建立視錐體形狀和尺寸.它決定投影類型和睇到範圍

模型視圖矩陣:在模型空間中用該矩陣變換3D模型.並將其在3D空間中移動

紋理矩陣:用於動態操縱紋理矩陣

設置當前矩陣為『投影矩陣』

GL10.glMatrixMode(GL10.GL_PROJECTION);

在棧頂載入單位據陣

GL10.glLoadIdentity();

然後剩以正交投影矩陣

GL10.glOrthof(-1,1,-1,1, 1, -1);

設置當前矩陣『模型視圖矩陣』

GL10.glMatrixMode(GL10.GL_PROJECTION);

在棧頂載入單位據陣

GL10.glLoadIdentity();

3D空間中平移

GL10.glTranslatef(x,y,z);

繞Y軸旋轉

GL10.glRoate(angle,0,1,0);

將當前棧頂拷貝並壓入棧頂

GL10.glPushMatrix();

矩陣棧頂出棧

GL10.glPopMatrix();

Android遊戲之透明混合

Android遊戲之透明混合

OpenGL要啟用混合,要將每個頂點顏色ALPHA分量置設為0.5f.這樣模型後方對象都能透過模型睇到

1.       OpenGL ES 將在『深度緩存Z-Buffer』和『幀緩存』中渲染模型

2.       OpenGL ES結合z-buffer啟用混合時.必需確保所有透面對象跟據其距離照相機遠近按升序排序.並且從後往線渲染對象.所有非透明必須在透明對象之前被渲染,而非透明對象不需要經過排序

啟用混合:

1.       啟用深度檢測(Z軸緩存)一定要調用glEnable(GL_DEPTH_TEST);.保證光照正確繪製和模形前後正確繪製

2.       啟用混合gl.glEnable(GL10.GL_BLEND);

3.       設定混合方程式gl.glBlendFunc(GL10.GL_SRC_ALPHA,GL10.GL_ONE_MINUS_SRC_ALPHA);

4.       設定模型透明度gl.glColor4f(1.0f, 1.0f, 1.0f,0.5f);

5.       完成渲染後禁用混合glDisable(GL_BLEND);

當需要在OpenGL ES中啟用混合.需要按以下方式渲染

1.       首先渲染所有不透明對象

2.       將所有透明對象按照其與相機距離運近排序(由遠及近)

3.       渲染排好序透明對象(由遠及近)

混合係數

簡介

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

 

Android遊戲之z-buffer

Android遊戲之z-buffer

OpenGL中『幀緩存』用於儲存屏幕每個像素.而z-buffer『深度緩存』則儲存像素『深度值』.『深度值』為3D空間中對應點與相機『近裁剪面』距離.

OpenGL ES將z-buffer『深度緩存』為每個像素寫入深度值.OpenGL ES會初此『深度緩存』每個深度值為無窮大(大數).

gl.glEnable(GL10.GL_DEPTH_TEST);

每當渲染像素時將像素深度值和『深度緩存』深度值進行比較.如果深度值更小則表示它接近於『近裁剪面』則更新『幀緩存』像素與『深度緩存』深度值.這一過程稱為『深度測試』.如果未能通過『深度測試』則像素與深度值均不寫入『幀緩存』與『深度測試』

每幀渲染時『幀緩存』與『深度緩存』均需清零.否則上一幀數據會被保留下來

gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

在渲染所有3D場景後需關閉『深度測試』.因為渲染2D圖形UI元素無Z軸座標.更無需深度測試.渲染順序由代碼繪製順序決定.

gl.glDisable(GL10.GL_DEPTH_TEST);

Android遊戲之透視投影

Android遊戲之透視投影

2D遊戲使用『正交投影』這意味著模型與視點距離無論多遠,其屏幕尺寸大小總為一至.而3D遊戲則使用『透視投影』模型離視點越近其屏幕尺寸越大.而模型離視點越遠其屏幕尺寸越細.

在『正交投影』就像置身於『矩形盒』.而『透視投影』就像切掉『金字塔』頂部,頂部為『近裁剪面』底部為『遠裁剪面』.而另外四面則分別為『左裁剪面』『右裁剪面』『頂裁剪面』『底裁剪面』

透視錐體由四個參數組成

1.『近裁剪面』與相機矩離

2.『遠裁剪面』與相機矩離

3.視口縱橫比,即視口『近裁剪面』寬高比

4.『視場』指定視錐體寬,也就是它所容納場景

桌面OpenGL帶有GLU輔助函式庫.而Android系統也含有GLU庫.設置投影矩陣

GLU.gluPerspective(GL10 gl,float fieldOfView,float aspectRatio,float near,flat far);

該函式將『透視投影矩陣』與當前矩陣相乘.

gl:為GL10實例

fieldOfView:視場角度,人眼視場角大約67度.加減此值可調整橫向視察範圍

aspectRatio:視口縱橫比,此值為一浮點數

near:遠裁剪面與相機距離

far:近裁剪面與相機距離

『透視投影』代碼

GL10 gl = GRAPHICS.GetGL();

清屏

gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

設定視口

gl.glViewport(0,0, GRAPHICS.GetWidth(),GRAPHICS.GetHeight());

設定當前矩陣為投影矩陣

gl.glMatrixMode(GL10.GL_PROJECTION);

載入單位矩陣

gl.glLoadIdentity();

設置投視投影

GLU.gluPerspective(gl, fieldOfView, aspectRatio, near, far);

設定當前矩陣為模型視圖矩陣

gl.glMatrixMode(GL10.GL_MODELVIEW);

載入單位矩陣

gl.glLoadIdentity();

 

 

 

 

Android遊戲之3D頂點索引

Android遊戲之3D頂點索引

在進入豐富多彩3D世界中需要定以『視錐體』和『精靈頂點』.3D空間中頂點需有xyz座標.並且使用『透視投影』.距離相機越遠,物體越小.離相機較近對象覆蓋較進隊象.3D頂點包含『位置(xyz)』『顏色(rgba)』『法線(x,y,z)』並且完成3D頂點『模型』渲染

public class VERTICES3D {

擁有頂點顏色

boolean hasColor = false;

擁有紋理坐標

boolean hasTexCoord =false;

擁有法線

boolean hasNormal = false;

每個頂點所占空間

int vertex_size = 0;

最大頂點量

int vertex_max = 0;

最大索引量

int index_max = 0;

頂點數組

IntBuffer vertex_array = null;

索引數組

ShortBuffer index_array = null;

頂點緩存

int[] vertex_Buffer;

構造函式分配頂點記憶體,vertex_max為最大頂點量,index_max為最大索引量

VERTICES3D(int vertex_max,int index_max,boolean hasColor,boolean hasTexCoord,boolean hasNormal){

ByteBuffer buffer = null;

this.vertex_max    = vertex_max;

this.index_max     = index_max;

this.hasColor  = hasColor;//  是否擁有頂點顏色

this.hasTexCoord = hasTexCoord;// 是否擁有紋理坐標

this.hasNormal = hasNormal;// 是否擁有法線

計算每頂點所占大小.顏色(rgbs)占4單元.紋理座標(uv)占2單元.3D座標(xyz)占3單元.每個整數占4字節

this.vertex_size = (3 + (hasColor ? 4 : 0) + (hasTexCoord ? 2 : 0) + (hasNormal ? 3: 0)) * 4;

vertex_Buffer = new int[vertex_max * vertex_size / 4];

因為OpenGL ES是以C API結口提供.無法直接使用JAVA數組.因此你需要C數組系統堆棧記憶體.而非JAVA虛擬機記憶體.需要 FloatBuffer分配頂點記憶體.vertex_max為最大頂點量.

buffer = ByteBuffer.allocateDirect(vertex_size * vertex_max);

將『網絡字節』改為『主機字節』或稱為『CPU字節』

buffer.order(ByteOrder.nativeOrder());

獲取頂點整數數組

vertex_array = buffer.asIntBuffer();

每索引占兩BYTE.即OpenGL ES每次最多渲染65536個頂點.

if(index_max > 0){

每個短整形占兩個字節.index_max為最大索引量

buffer = ByteBuffer.allocateDirect(index_max * Short.SIZE/8);

將『網絡字節』改為『主機字節』

buffer.order(ByteOrder.nativeOrder());

獲取頂點短整數數組

index_array = buffer.asShortBuffer();

}

}

將頂點提交給OpenGL數組並觸發

public void setVertices(float[] vertices,int offset,int count){

清空緩存.設定當前位置

vertex_array.clear();

int len = offset + count;

for(int i=offset, j=0; i < len; i++, j++)

vertex_Buffer[j] = Float.floatToRawIntBits(vertices[i]);

寫入數據.移動當前位置

vertex_array.put(vertex_Buffer, 0, count);

觸發

vertex_array.flip();

}

將頂點索引提交給OpenGL數組

public void setIndices(short[] indices,int offset,int count) {

清空緩存.設定當前位置

index_array.clear();

寫入數據.移動當前位置

index_array.put(indices, offset, count);

觸發

index_array.flip();

}

 

獲取索引量

public int getNumIndices(){

return index_array.limit();

}

獲取頂點量

public int getNumVertices(){

return    vertex_array.limit() / (vertex_size/4);

}

綁定頂點數據

public void Bind(){

GL10 gl = GRAPHICS.gl;

啟用頂點數組

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

設置當前寫入位置0

vertex_array.position(0);

設置頂點指針,每個頂點包含xyz分量

gl.glVertexPointer(3, GL10.GL_FLOAT, vertex_size, vertex_array);

擁有頂點顏色

if(hasColor == true) {

啟用頂點顏色數組

gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

設置當前寫入位置3

vertex_array.position(3);

設置顏色指針,RGBA四分量

gl.glColorPointer(4, GL10.GL_FLOAT, vertex_size, vertex_array);

}

擁有紋理坐標

if(hasTexCoord == true){

啟用紋理坐標

gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

設置當前寫入位置

vertex_array.position(hasColor?7:3);

設置紋理坐標指針UV分量

gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertex_size, vertex_array);

}

擁有法線

if (hasNormal = true) {

啟用法線

gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);

int offset = 3;

if (hasColor)

offset += 4;

if (hasTexCoord)

offset += 2;

設置當前寫入位置

vertex_array.position(offset);

設置法線指針,xyz分量

gl.glNormalPointer(GL10.GL_FLOAT, vertex_size, vertex_array);

}

}

取消綁定數據

public void Unbind(){

GL10 gl = GRAPHICS.gl;

關閉頂點紋理數組

if(hasColor)

gl.glDisableClientState(GL10.GL_COLOR_ARRAY );

關閉頂點顏色數組

if(hasTexCoord)

gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

關閉法線數組

if (hasNormal)

gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);

}

繪畫3D模型.需要先綁3D頂點

public void Draw(int mode,int offset,int count){

GL10 gl = GRAPHICS.gl;

繪畫頂點

if(index_array != null) {// 繪畫

index_array.position(offset);

gl.glDrawElements(mode, count, GL10.GL_UNSIGNED_SHORT, index_array);

}

else {

gl.glDrawArrays(mode, offset, count);

}

}

}

Android遊戲之半透明混合處理

Android遊戲之半透明混合處理

2D遊戲紋理渲染必須將『背景色』去除.JPEG格式不支持存儲像素點alpha值.將透明色alpha值設為零.需使用PNG格式.若紋理圖像沒有alpha通道時OpenGL ES自動將alpha設為1.但混合開銷很大而目前手機上GPU都不能對大量像素禁行混合.所以在需要時才啟用Blend『混合』.在OpenGL ES中啟動半透明混合處理
gl.glEnable(GL10.GL_BLEND);
設定『來源色』和『目標色』組合方程.
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
啟用2D紋理渲染
gl.glEnable(GL10.GL_TEXTURE_2D);
渲染三角形

禁用2D紋理渲染
gl.glDisable(GL10.GL_TEXTURE_2D);
結束渲染後禁用混合
gl.glDisable(GL10. GL_BLEND);

Android遊戲之GLSurfaceView

Android遊戲之GLSurfaceView

遊戲設計中通常『更新』『渲染』放在同一線程中.在Windows可以在主線程將『消息驅動』改為『實時驅動』.把『更新』『渲染』放在主線程中.而Android卻無法做到這點.但提供GLSurfaceView可建立獨立線程在背後實現『更新』『渲染』.你需要實現監聽接口GLSurfaceView.Renderer.並註冊到GLSurfaceView中.監聽接口需要分別重寫『創建』『調整大細』『渲染』.GLSurfaceView.Renderer可獲得GL10.通過它方可訪問OpenGL ES API.而GL10中10代表OpenGL ES 1.0標準.可以將GLSurfaceView封裝成獨立控件.從而在layout『佈局』中嵌入.

public class RenderView extends GLSurfaceView implements GLSurfaceView.Renderer {

每當Activity恢復或啟動創建. EGLConfig設置Surface顏色與深度

public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig);

當view尺寸發生改變時調用,傳入寬與高

public void onSurfaceChanged(GL10 gl10, int width, int height);

調用『渲染』『更新』完成幀繪製.但每秒不超過60幀.

public void onDrawFrame(GL10 gl10);

令外還需重寫『恢復』『暫停』

『恢復』重啟渲染線程,在Activity恢復顯示時在Activity.onResume()中調用

public void onResume();

『暫停』退出渲染線程,當Activity進入後臺時在Activity.onPause()中調用

public void onPause();

}

編輯layout『佈局』文檔main.xml添加

<net.bookcard.aa.RenderView

android:id=”@+id/render_view”

android:layout_width=”fill_parent”

android:layout_height=”fill_parent” />

定義view狀態

public static final int       STATE_INIT       = 0;// 初此

public static final int  STATE_RUN       = 1;// 運行

public static final int  STATE_PAUSED    = 2;// 暫停

public static final int  STATE_FINISHED  = 3;// 結束

public static final int  STATE_IDLE       = 4;// 閒置

int view_width,view_height;// 寬與高

int state = STATE_INIT;// 初此狀態

long    startTime ;// 啟動時間

創建Surface獲取屏幕

public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig){

SCREEN  screen = GAME.getCurrentScreen();// 當前屏幕

if(state == STATE_INIT) {// 初此

Init(gl, this);

screen = GAME.getMainScreen();

GAME.setCurrentScreen(screen);

}

else {//  重新載入資源

ASSETS.reload();

}

screen = GAME.getCurrentScreen();

state = STATE_RUN;// 運行

startTime = System.nanoTime();// 獲取啟動時間

}

大小發生改變

public void onSurfaceChanged(GL10 gl, int width, int height){

this.view_width = width;// 寬

this.view_height = height;// 高

}

更新並渲染.System.nanoTime()返回納秒, 1000000000納秒=1秒.通過兩次時間測量計算間隔時間

public void onDrawFrame(GL10 gl){

SCREEN screen = GAME.getCurrentScreen();

if(state == STATE_RUN){// 運行

float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f;

startTime = System.nanoTime();// 獲取當前時間

screen.update(deltaTime);// 更新

screen.present(deltaTime);// 渲染

}

else

if(state == STATE_PAUSED) {// 暫停

screen.pause();

}

else

if(state == STATE_FINISHED) {// 結束

screen.pause();

screen.dispose();

}

}

恢復渲染在Activity.onResume()中調用

public void onResume(){

super.onResume();

MUSIC.Resume();

}

暫停渲染在Activity.onPause()中調用

public void onPause(){

state = STATE_PAUSED;// 暫停

super.onPause();

MUSIC.Pause();

}

初此遊戲系統個部件

void Init(GL10 gl, GLSurfaceView view){

GRAPHICS.Init(gl,view);

SYSTEM.Init(context);// 系統

FileIO.Init(context);// 文件讀寫

BITMAP.Init(context);// 位圖

SOUND.Init(context,100);// 聲音

MUSIC.Init(context,100);// 音樂

TOUCH.Init(this);// 觸摸模塊

GAME.Init();//  屏幕切換系統

BATCHER.Init(1500);// 精靈批處理

ASSETS.load();// 資源

}

Android遊戲之紋理區域

Android遊戲之紋理區域

『紋理區域』指『紋理』中『矩形區域』.2D紋理通常尺寸較小.可以將多個2D紋理放在單一個紋理圖中.以提升OpenGL ES渲染性能.將其像素座標到紋理座標轉進行封裝.u1,v1為紋理座標左上角.u2,v2為紋理座標右下角.以確定紋理區域.值範圍0~1

public class REGION {

public TEXTURE texture;// 紋理

public float u1,v1,u2,v2;// 紋理坐標

像素坐標轉紋理坐標.輸入『像素』左上角與『寬和高』

public REGION(TEXTURE texture,float x,float y,float width,float height){

u1 = x/(float)texture.width;

v1 = y/(float)texture.height;

u2 = u1 + width/(float)texture.width;

v2 = v1 + height/(float)texture.height;

this.texture = texture;

}

}

Android遊戲之精靈批處理

Android遊戲之精靈批處理

OpenGL ES盡可能每次渲染多個精靈.以提高渲染性能.為此需要知道精靈『位置』『尺寸』『紋理區域』.『批處理』將多個渲染合拼在一起.這對GPU有利.批處理設立浮點數『緩存』保存頂點.該緩存區初此時需清空.定義『批處理』結構BATCHER.

public class BATCHER {

成員Buffer為浮點型數組用於存儲頂點.

private static float[] buffer;

成員indices為短整形索引.以三角形排列用於儲存頂點.

public static short[] indices ;

頂點列表用於批處理渲染

private static VERTICES vertices;

緩存寫入索引初此為零

private static int index = 0;

精零數量初此為零

public static int num = 0;

初次批處理.max精靈為最大量.

public static void Init(int max){

分配2D精靈頂點緩存,每個精靈有4頂點,每頂點要4個浮點數(空間坐標x,y兩個,紋理坐標u,v兩個)

buffer = new float[max44];

分配頂點緩存.『頂點』『紋理座標』『索引』

vertices = new VERTICES(max * 4, max*6, false,true);

分配索引每個精靈6個索引

indices = new short[max*6];

int len = indices.length;// 索引數組長度

index = 0;// 緩存寫入索引

num = 0;//  精零數量

生成頂點索引

for (int i=0,j=0; i < len; i += 6, j += 4) {

indices[i + 0] = (short)(j + 0);

indices[i + 1] = (short)(j + 1);

indices[i + 2] = (short)(j + 2);

indices[i + 3] = (short)(j + 2);

indices[i + 4] = (short)(j + 3);

indices[i + 5] = (short)(j + 0);

}

設置頂點索引

vertices.SetIndices(indices, 0, indices.length);

}

準備進行批處理渲染.首先綁定紋理.並將『精零數量』『緩存索引』置零

public static void Begin(TEXTURE texture){

texture.Bind();// 綁定紋理

num = 0;//  精零數量

index = 0;// 緩存寫入索引

}

結束批處理渲染.將頂點提交給OpenGL數組並觸發.綁定頂點數據後進形三角形渲染.然後取消數據綁定.每次調用繪畫時.向緩存區中添加4個頂點參數為『位置』『顏色』『索引』『紋理區域』

public void boolean End(){

vertices.SetVertices(buffer,0,index);

vertices.Bind();

vertices.Draw(GL10.GL_TRIANGLES, 0, num * 6);

vertices.Unbind();

}

批處理繪畫計算精靈中心『位置』及『寬和高』.和『紋理區域』.生成精靈『左下角』與『右上角』『空間座標』與『紋理座標』

public static void Draw(float x,float y,float width,float height,REGION region){

float halfWidth = width/2.0f;// 寬度一半

float halfHeight = height/2.0f;//高度一半

float x1 = x – halfWidth;

float y1 = y – halfHeight;

float x2 = x + halfWidth;

float y2 = y + halfHeight;

buffer[index++] = x1;

buffer[index++] = y1;

buffer[index++] = region.u1;

buffer[index++] = region.v2;

buffer[index++] = x2;

buffer[index++] = y1;

buffer[index++] = region.u2;

buffer[index++] = region.v2;

buffer[index++] = x2;

buffer[index++] = y2;

buffer[index++] = region.u2;

buffer[index++] = region.v1;

buffer[index++] = x1;

buffer[index++] = y2;

buffer[index++] = region.u1;

buffer[index++] = region.v1;

++num;

}

}

在調用時首先清理緩存並存入紋理.但只能對使用同一紋理精靈進行批處理.最後結束渲染.如下:

BATCHER.Begin(Texture);

BATCHER.Draw(X,Y,WIDTH,HEIGHT,Region);

BATCHER.End();

Android遊戲之頂點索引

Android遊戲之頂點索引

OpenGL均採用三角形列表進行渲染.每個三角形都有三個頂點.在有些情況下兩個或多個三角形會共用頂點.如上圖有兩個頂點具有相同『位置』『顏色』『紋理座標』.但甘浪費空間.更好解卻方法是將所有頂點保存在列表.而三角形頂點保存索引.OpenGL ES要求索引值使用短整數或字節.即OpenGL ES每次最多渲染65536個頂點.

我地需要一個Vertices類,用於存儲每個頂點『位置』『顏色』與『紋理座標』,並且它需要兩個選項.頂點是否『顏色』與『紋理座標』

public class VERTICES {

boolean color_have = false;//  擁有顏色

boolean texture_have =false;// 擁有紋理坐標

int vertex_size = 0;// 每個頂點所占大小

使用FloatBuffer保存頂點

FloatBuffer  vertex_array = null;

使用ShortBuffer頂點索引

ShortBuffer index_array = null;

頂點隊列分配記憶體,vertex_max為最大頂點量, index_max為最大索引量

VERTICES(int vertex_max,int index_max,boolean color_have,boolean texture_have){

this.color_have  = color_have;//  擁有頂點顏色

this.texture_have = texture_have;// 擁有紋理坐標

計算每頂點所占大小.顏色占4單元.紋理座標占2單元.2D座標占2單元.每個整數占4字節

int vertex_size = (2 + (color_have?4:0) + (texture_have?2:0) ) * 4;

因為OpenGL ES是以C API結口提供.無法直接使用JAVA數組.因此你需要C數組系統堆棧記憶體.而非JAVA虛擬機記憶體.需要 FloatBuffer分配頂點記憶體.

vertex_max為最大頂點量.

ByteBuffer buffer = ByteBuffer.allocateDirect(vertex_size * vertex_max);

將『網絡字節』改為『主機字節』或稱為『CPU字節』

buffer.order(ByteOrder.nativeOrder());

獲取整數數組

vertex_array = buffer.asIntBuffer();

每個短整形占兩個字節.index_max為最大索引量

buffer = ByteBuffer.allocateDirect(index_max * Short.SIZE/8);

將『網絡字節』改為『主機字節』

buffer.order(ByteOrder.nativeOrder());

頂點短整數數組

index_array = buffer.asShortBuffer();

}

將頂點提交給OpenGL數組並觸發

public void SetVertices(float[] vertices,int offset,int count){

vertex_array.clear();// 清空緩存.設定當前位置

int len = offset + count;

for(int i=offset, j=0; i < len; i++, j++)

vertex_Buffer[j] = Float.floatToRawIntBits(vertices[i]);

vertex_array.put(vertex_Buffer, 0, count); // 寫入數據.移動當前位置

vertex_array.flip();// 觸發

}

將頂點索引提交給OpenGL數組

public void SetIndices(short[] indices,int offset,int count) {

index_array.clear();// 清空緩存.設定當前位置

index_array.put(indices, offset, count); // 寫入數據.移動當前位置

index_array.flip();// 觸發

}

綁定數據

public void Bind(){

GL10  gl = GRAPHICS.gl;

啟用頂點數組

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

設置當前寫入位置0

vertex_array.position(0);

設置頂點指針,每個頂點兩個元素.xy兩分量

gl.glVertexPointer(2, GL10.GL_FLOAT, vertex_size, vertex_array);

if(color_have == true){//顏色

啟用頂點顏色數組

gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

設置當前寫入位置2

vertex_array.position(2);

設置顏色指針,RGBA四分量

gl.glColorPointer(4, GL10.GL_FLOAT, vertex_size, vertex_array);

}

if(texture_have ){// 紋理坐標

啟用紋理坐標數組

gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

vertex_array.position(color_have?6:2);// 寫入位置

設置紋理坐標指針,UV兩分量

gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertex_size, vertex_array);

}

}

取消綁定數據

public void Unbind(){

GL10 gl = GRAPHICS.gl;

關閉頂點紋理數組

if(color_have)

gl.glDisableClientState(GL10.GL_COLOR_ARRAY );

關閉頂點顏色數組

if(texture_have)

gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

}

繪畫三角形

public void Draw(int mode,int offset,int count){

GL10 gl = GRAPHICS.gl;

繪畫頂點

if(index_array != null){// 繪畫

index_array.position(offset);//

gl.glDrawElements(mode, count, GL10.GL_UNSIGNED_SHORT, index_array);

}

else{

gl.glDrawArrays(mode, offset, count);

}

}

}

Android遊戲之2D相機

Android遊戲之2D相機

定義2D相機即設定『投影矩陣』與『視口』

『視口』用於控制輸出圖像尺寸與渲染幀緩存位置.OpenGL ES使用『視口』將投影到『近裁剪面』點座標變換為『幀緩存』像素座標

GL10.glViewport(int x,int y,int width,int height);

x和y座標為幀緩存區視口左上角.width和height為視口大小.單位為像素.幀緩存座標原點為屏幕左下角.通常將x和y設為0.width和height設為屏幕分辨率.即設為全屏.

gl.glViewport(0, 0, view.getWidth(), view.getHeight());

『投影矩陣』在2D相機中只使用『平行投影』.首先將當前矩陣設為『投影矩陣』

gl.glMatrixMode(GL10.GL_PROJECTION);

載入單位矩陣

gl.glLoadIdentity();

設置正交投影矩陣

GL10. glOrthof(int left,int right,int bottom,int top,int near,int far);

OpenGL ES座標系統正X軸指向右,正Y軸指向上.正Z軸指向我.left與right為X軸左右兩條邊.bottom與 top為Y軸上下兩條邊.near為近端裁剪面.far為遠端裁剪面.若設2D橫向相機左下角(0,0)右上角(480,320)視錐深度為2

gl.glOrthof(0,480,0,320,1, -1);

重設為模型視圖矩陣

gl.glMatrixMode(GL10.GL_MODELVIEW);

gl.glLoadIdentity();

2D相機將『觸屏』坐標轉『世界』坐標.首先設定視錐體寬為15高為10.

float frustum_width = 15;

float frustum_height = 10;

設縮放係數為1.用於放大屏幕大於1,縮小屏幕小於1

float zoom = 1;

首先將『觸屏』位置歸範圍(0~1).然後乘以視錐體寬和高

touch.x = (touch.x / view.getWidth()) * frustum_width * zoom;

因為觸屏座標原點位於屏幕左上角.需倒轉Y軸

touch.y = (1-touch.y /view.getHeight()) * frustum_height * zoom;

與相機位置相機

touch.add(position);

最後減於視錐體中心位置.得到遊戲『世界』坐標

touch.x = touch.x – frustum_width*zoom/2f;

touch.y = touch.y – frustum_height*zoom/2f;

 

Android遊戲之OpenGL ES

Android遊戲之OpenGL ES

OpenGL ES專為移動與嵌入設備設計之圖形編程接口. 它並非由ARB委員會定義.而是由Khronos組織定義.該組織包括ATI、NVIDIA、Intel等組成.雖然『OpenGL』與『OpenGL ES』來自兩個不同組織. 『OpenGL ES』係『OpenGL』刪減板.但你依然可寫出在兩種標準下都可運行之程式.這對於移植遊戲很重要.OpenGL ES同樣以C文檔發佈API.而代碼實現由硬件廠商完成.

如果AndroidManifest.xml中無配置glEsVersion,則表示支持OpenGL ES 1.0但所有Android系統都支持.若支持OpenGL ES 2.0需在AndroidManifest.xml中添加:

<uses-feature  android:required=”false”  android:glEsVersion=”0x00020000″ />

OpenGL ES版本號為32位整數.高16位表示OpenGL ES大版本,低16位表示OpenGL ES小版本.

required必須 簡介
true 必需支持否則不能運行
false 可選支持可能影響部分運行

 

OpenGL ES版本 glEsVersion
GLES 1.0 0x00010000
GLES 1.1 0x00010001
GLES 2.0 0x00020000
GLES 3.0 0x00030000
GLES 3.1 0x00030001
GLES 3.2 0x00030002

 

Android遊戲之紋理映射

Android遊戲之紋理映射

將『Bitmap』加載給OpenGL ES然後加載到圖形卡記憶體重,並最終交給GPU渲染.因為OpenGL最終以三角形為單位進行渲染.而將『Bitmap』映射到三角形上.需要為三角形每個頂點設定紋理座標(texture coordinates).3D座標(x,y,z)和2D座標(x,y)對應紋理座標(u,v)位圖頂點.而(u,v)座標相當於(橫,縱)座標.紋理寬與高必需是2倍數.

紋理映射 (u,v)
左上角 (0,0)
右下角 (1,1)即使寬與高不相等
右上角 (1.0)
左下角 (0,1)

生成紋理隊列,用於保存紋理ID

int[] ids = new int[1];

生成紋理並獲取id.紋理依然為空

gl.glGenTextures(1, ids,0);

紋理id

int id = ids[0];

讀取位圖

Bitmap bitmap = BITMAP.Read(file_name);

獲取位圖寬度

int   width = bitmap.getWidth();

獲取位圖高度

int   height = bitmap.getHeight();

綁定2D紋理

gl.glBindTexture(GL10.GL_TEXTURE_2D, id);

上傳2D紋理

GLUtils.texImage2D(GL10.GL_TEXTURE_2D,0,bitmap,0);

設置紋理屬性指定縮小倍數過濾器

gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_NEAREST);

設置紋理屬性指定放大倍數過濾器

gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MAG_FILTER,GL10.GL_NEAREST);

 

過濾 簡介
GL10.GL_NEAREST 像素採樣點最接近中心點『清晰』
GL10.GL_LINEAR 像素採樣點線性插值平均加權『模糊』

取消綁定紋理ID

gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);

回收位圖.以免浪費記憶體

bitmap.recycle();

每當Activity被暫停(APP切換)GLSurfaceView都會被銷毀.每當Activity恢復時調用GLSurfaceView. onResume()讓GLSurfaceView重構渲染界面.但OpenGL ES中上傳紋理都會丟失.你需要在onSurfaceCreated()中需重新載入並上傳紋理.

重新載入代碼見上文

Texture.load(filen_ame);

重新綁定紋理後設置紋理屬性

gl.glBindTexture(GL10.GL_TEXTURE_2D, id);

gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_NEAREST);

gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MAG_FILTER,GL10.GL_NEAREST);

取消綁定紋理

gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);

 

當遊戲退出.便要刪除紋理釋放記憶體.首先取消紋理邦定

gl.glBindTexture(GL10.GL_TEXTURE_2D, id);

刪除紋理

int[] ids = { id };

gl.glDeleteTextures(1,ids,0);

渲染三角形時要告知OpenGL要啟用紋理渲染

gl.glEnable(GL10.GL_TEXTURE_2D);

完成渲染後禁用紋理渲染

gl.glDisable(GL10.GL_TEXTURE_2D);

2D遊戲通常只佔用圖像一小片區域.需要將像素座標(x,y)轉換為紋理座標(u,v)

計算紋理座標左上角

u1 = x/texture.width;

v1 = y/texture.height;

計算紋理座標右下角

u2 = u1 + width/texture.width;

v2 = v1 + height/texture.height;

 

 

 

Android遊戲之Bitmap讀取

Android遊戲之Bitmap讀取

遊戲由『背景』與『角色』『植物』『房舍』等圖檔組成.在C時代要逐個寫圖檔分析器.而在java可以通過BitmapFactory讀取Bitmap『位圖』.幾乎支持所有常見圖檔『jpg』『png』『bmp』『png』.把所有『位圖』保存到『ASSETS』目錄下

資源管理器用於訪問『ASSETS』目錄

AssetManager asset_manager = context.getAssets();

指定『位圖名』並返回輸入流

InputStream input_stream = asset_manager.open(file_name);

讀取Bitmap默認轉換為RGB_565色

Bitmap bitamp = BitmapFactory.decodeStream(input_stream);

關閉輸入流

Input_Stream.close();

獲取位圖寬度

int width = bitmap.getWidth();

讀取位圖寬度

int height = bitmap.getHeight();

獲取位圖顏色格式.

Bitmap.Config config = bitamp.getConfig();

Bitmap.Config 位圖顏色格式
ALPHA_8 256色
ARGB_8888 32bit含ALPHA分量
RGB_565 16bit(默認)
ARGB_4444 16bit含透明度分量

以特定顏色格式進行讀取.但渲染時最終要與OpenGL ES顏色格式一致

設定顏色格式為ARGB_8888

BitmapFactory.Options options = new BitmapFactory.Options();

options.inPreferredConfig = Bitmap.Config.ARGB_8888;

指定『位圖名』並返回輸入流

InputStream Input_Stream = asset_manager.open(file_name);

讀取Bitmap並轉換為ARGB_8888色

Bitmap bitamp = BitmapFactory.decodeStream(Input_Stream, null, options);

關閉輸入流

Input_Stream.close();

OpenGL之準星

OpenGL之準星
準星紋理

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

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

 

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

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

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

更新相機位置與角度

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

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

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

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

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

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

player->rot.y = 0.0f;

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

player->rot.z = 60.0f;

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

player->rot.z = -60.0f;

3.角度轉弧度

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

計算移動速度

4.float speed = dx * 3 *deltaTime;

5.計算位置

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

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

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

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

6.UVN相機模型

LookAt_Camera3D();

最後製作準星紋理

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

OpenGL之粒子爆炸特效

OpenGL之粒子爆炸特效

遊戲中最常見『爆炸』特效.最常見做法是使用粒子系統模擬『爆炸』.你大約需要:

  1. 黑底白圓位圖『bmp』如上圖
  2. 爆炸特效音頻『wav』
  3. 粒子系統
  4. 廣告牌

其實所有『爆炸』特效都是圓心紋理位圖.只是它使用『廣告牌』技術讓其正對著你即『相機』. 渲染為紅色並讓其隨時間『下墜』『變暗』『縮小』直至消亡.之前模擬『飄雪』特效時使用三級系統.這次我將粒子結構大大簡化.其代碼我都在模擬『爆炸』特效中實現.W

粒子結構體

typedef struct PARTICLE_TYP {

VECTOR3D pos;// 位置

VECTOR3D pos_prev;// 之前位置

VECTOR3D velocity;// 速度與方向

VECTOR3D acceleration;// 加速度

float    energy;// 生命週期(秒)

float size;// 尺寸

float size_delta;//增量

float weight;// 重量

float wdight_delta;// 重量增量

float color[4];// 顏色

float color_delta[4];// 顏色增量

}PARTICLE,*PARTICLE_PTR;

爆炸特效結構體

typedef struct EXPLOSION_TPY {

int          state;//  狀態

PARTICLE_PTR array;// 數組

int          count;// 粒子數量/爆炸量

VECTOR3D     origin;// 原點

VECTOR3D     velocity;// 速度與方向

VECTOR3D     variation;// 速度變量

VECTOR3D     acceleration;// 加速度

float        energy;// 生命週期(秒)

float        size;// 尺寸 5.0f

float        size_variation ;// 尺寸變量 2.0f

float        spread;// 爆炸傳播範圍

float        color[4];// 顏色

VECTOR2D     texture_coord[4];// 紋理座標

}EXPLOSION,*EXPLOSION_PTR;

生成『爆炸』特效函式

count:爆炸量大約1-10即可.

pos:位置

spread: 爆炸傳播範圍 0.1即可

void Build_Explosion(EXPLOSION_PTR explosion,int count,VECTOR3D_PTR pos,float spread){

explosion->state = MODEL3D_STATE_ACTIVE;

explosion->count = count; // 粒子數量

explosion->array = (PARTICLE_PTR)malloc(sizeof(PARTICLE)*count);// 分配空間

explosion->spread = spread;// 傳播

explosion->size           = 5.0f;// 尺寸

explosion->size_variation = 2.0f;// 尺寸變量

explosion->energy = 1.5f + FRAND_RANGE1() / 2.0f;// 生命週期(秒)

Init_VECTOR3D(&explosion->origin, pos->x, pos->y, pos->z);// 源點

Init_VECTOR3D(&explosion->velocity, 0.0f, 2.0f, 0.0f);// 速度

Init_VECTOR3D(&explosion->variation, 4.0f, 4.0f, 4.0f);// 變量

Init_VECTOR3D(&explosion->acceleration, 0.0f, -5.0f, 0.0f);// 加速度

PARTICLE_PTR particle;// 粒子

for (int i = 0; i < count; ++i){

particle = &explosion->array[i];// 粒子

Buid_Explosion(explosion, particle);//生成粒子『爆炸』特效

}

Set_Pos_DirectSound(&explosion_sound3D, pos->x, pos->y, pos->z);// 設置音源位置

Play_DirectSound(&explosion_sound3D, false);//播放音頻數據

}

生成爆炸粒子函式

void Buid_Explosion(EXPLOSION_PTR explosion, PARTICLE_PTR  particle){

// 在發射區隨機位置生成粒子

particle->pos.x = explosion->origin.x + FRAND_RANGE1() * explosion->spread;// 傳播

particle->pos.y = explosion->origin.y + FRAND_RANGE1() * explosion->spread;

particle->pos.z = explosion->origin.z + FRAND_RANGE1() * explosion->spread;

//給粒子隨機速度

particle->velocity.x += FRAND_RANGE1() * explosion->velocity.x;

particle->velocity.y += FRAND_RANGE1() * explosion->velocity.y;

particle->velocity.z += FRAND_RANGE1() * explosion->velocity.z;

// 加速度

particle->acceleration = explosion->acceleration;

// 生命週期

particle->energy = 1.5f + FRAND_RANGE1() / 2.0f;

// 顏色

particle->color[0] = 1.0f;

particle->color[1] = 0.5f + FRAND_RANGE1() * 0.5f;

particle->color[2] = 0.0f;

particle->color[3] = 1.0f;

// 顏色增量

particle->color_delta[0] = 0.0f;

particle->color_delta[1] = -(particle->color[1] / 2.0f) / particle->energy;

particle->color_delta[2] = 0.0f;

particle->color_delta[3] = -1.0f / particle->energy;

// 設置粒子的大小

particle->size = explosion->size + FRAND_RANGE1() * explosion->size_variation;

particle->size_delta = -particle->size / particle->energy;

}

釋放『爆炸』特效函式

void Free_Explosion(EXPLOSION_PTR explosion){

if (explosion->array != NULL)

free(explosion->array);

explosion->state = MODEL3D_STATE_NULL;

memset(explosion, 0, sizeof(EXPLOSION));// 清空

}

更新『爆炸』特效函式

deltaTime:時間間隔

void Update_Explosion(EXPLOSION_PTR explosion, float deltaTime){

PARTICLE_PTR particle;// 粒子

for (int i = 0; i < explosion->count; ){

particle = &explosion->array[i];// 粒子

// 基於時間和速度更新粒子的位置

particle->pos = particle->pos + particle->velocity * deltaTime;

particle->velocity = particle->velocity + particle->acceleration * deltaTime;

particle->energy = particle->energy – deltaTime;

particle->size += particle->size_delta * deltaTime;

particle->color[3] += particle->color_delta[3] * deltaTime;

particle->color[1] += particle->color_delta[1] * deltaTime;

// 將最後一個粒子移動到當前位置,並減少計數.

if (particle->energy <= 0.0)

explosion->array[i] = explosion->array[–explosion->count];

else

++i;

}

if (explosion->count == 0)

Free_Explosion(explosion); // 釋放

}

渲染『爆炸』特效紋理函式

void Render_Explosion2(EXPLOSION_PTR explosion, BILLBOARD_PTR billboard){

float viewMatrix[16];

VECTOR3D right, up, pos;

GLfloat size;

right = billboard->right;

up = billboard->up;

// 壓入當前屬性

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_DEPTH_TEST);

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA);

glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, explosion_texture.ID);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glBegin(GL_QUADS);

for (int i = 0; i < explosion->count; ++i){

PARTICLE_PTR particle = &explosion->array[i];// 粒子

size = particle->size / 2;

pos = particle->pos;

glColor4fv(particle->color);

glTexCoord2f(0.0, 0.0); glVertex3fv((pos + (right + up) * -size).M);

glTexCoord2f(1.0, 0.0); glVertex3fv((pos + (right – up) * size).M);

glTexCoord2f(1.0, 1.0); glVertex3fv((pos + (right + up) * size).M);

glTexCoord2f(0.0, 1.0); glVertex3fv((pos + (up – right) * size).M);

}

glEnd();

glDisable(GL_TEXTURE_2D);

glDisable(GL_BLEND);

glEnable(GL_DEPTH_TEST);

glPopAttrib();// 彈出當前屬性

}

OpenGL之曲棍球

OpenGL之曲棍球

『曲棍球』是碰撞算法最好演示,在遊戲中『球臺』寬300長500,並且有四條圍邊,確保『曲棍球』在『球臺』圍邊範圍內移動. 『球臺』會給『曲棍球』帶來『磨擦』後慢慢慢落來並最後停低.而『玩家球』則通過鼠標移動控制.通過與『曲棍球』相碰給『曲棍球』帶來動力.演示程式:下載.我會給出遊戲設計重點,並給出關鍵代碼

1.『冰球』運動核心是碰撞算法.你試想下當『冰球』高速運動時有可能會穿越圍邊(圍邊使用平面定義).所以你需要準確計算碰撞時間.碰撞代碼睇『碰撞時間

2.控制『玩家角色』通過獲取滑鼠標偏移量與位置相加移動『玩家角色』.你需要通過DirectInput獲得滑鼠偏移量. 並且確保不要移出球臺圍邊.若與『冰球』則重新計算返射方向與速度

根據鼠標運動計算速度::Init_VECTOR3D(&player->velocity, diffX, 0, diffY);

計算位置player->position = player->position + player->velocity;

『玩家角色』與『冰球』碰撞使用『邊界球』進行檢測並重新計算返射方向與速度

puck->velocity = Reflection_VECTOR3D(&puck->velocity,&(puck->velocity ^ player->velocity)) + player->velocity*500;

3.設定『球臺』結構與『圍邊平面

typedef struct TABLE_TPY {

TEXTURE   texture;// 球臺紋理

VECTOR3D  position;// 球臺位置

VECTOR3D  corner[4];//球臺角落

PLANE3D    wall[4];// 球臺圍邊平面

float width;// 寬度

float depth;// 深度

float height;// 高度

}TABLE,*TABLE_PTR;

球臺寬300*長500載入紋理Load_File_Texture()綁定紋理Bind_Image_Texture().圍邊使用Init_PLANE3D()定義為平面

4.定義『冰球』結構

typedef struct PUCK_TPY {

VECTOR3D     position;// 位置

VECTOR3D  acceleration;// 加速度

VECTOR3D  velocity;// 速度

float     radius;// 半徑

}PUCK,*PUCK_PTR;

5.定義『玩家角色』結構

typedef struct PLAYER_TPY {

VECTOR3D  position;// 位置

VECTOR3D  velocity;// 速度

float     radius;// 半徑

float     mouseX, mouseY;

}PLAYER,*PLAYER_PTR;

 

 

OpenGL之陰影

OpenGL陰影

『陰影』有很多種方法,而『投射陰影』技術已被大量的應用於遊戲,從光源位置透視投影3D物體,從而將陰影投射到平面. 而且視覺效果佳.算法思路如下:

  1. 啟用『模板緩存』,將陰影繪畫限制在相應的區域中
  2. 構造投影矩陣,此矩陣根據『光源位置』和被『投影平面』位置來設定,並且與模型視圖矩陣相乘.
  3. 禁用光照和紋理,並設置顏色為黑色.
  4. 禁用深度測試.
  5. 啟用透明混合
  6. 繪畫陰影

落雪陰影演示下載:

構造投影矩陣

void Build_Matrix_Shadow(MATRIX4X4_PTR destMat, VECTOR4D_PTR lightPos, PLANE3D_PTR plane)

{

float dot;// 點積

dot = plane->M[0] * lightPos->M[0] +

plane->M[1] * lightPos->M[1] +

plane->M[2] * lightPos->M[2] +

plane->M[3] * lightPos->M[3];

// 第一列

destMat->_M[0] = dot – lightPos->M[0] * plane->M[0];

destMat->_M[4] = 0.0f – lightPos->M[0] * plane->M[1];

destMat->_M[8] = 0.0f – lightPos->M[0] * plane->M[2];

destMat->_M[12] = 0.0f – lightPos->M[0] * plane->M[3];

// 第二列

destMat->_M[1] = 0.0f – lightPos->M[1] * plane->M[0];

destMat->_M[5] = dot – lightPos->M[1] * plane->M[1];

destMat->_M[9] = 0.0f – lightPos->M[1] * plane->M[2];

destMat->_M[13] = 0.0f – lightPos->M[1] * plane->M[3];

// 第三列

destMat->_M[2] = 0.0f – lightPos->M[2] * plane->M[0];

destMat->_M[6] = 0.0f – lightPos->M[2] * plane->M[1];

destMat->_M[10] = dot – lightPos->M[2] * plane->M[2];

destMat->_M[14] = 0.0f – lightPos->M[2] * plane->M[3];

// 第四列

destMat->_M[3] = 0.0f – lightPos->M[3] * plane->M[0];

destMat->_M[7] = 0.0f – lightPos->M[3] * plane->M[1];

destMat->_M[11] = 0.0f – lightPos->M[3] * plane->M[2];

destMat->_M[15] = dot – lightPos->M[3] * plane->M[3];

}

MATRIX4X4 shadow_matrix; // 投影矩陣

GLfloat   light_pos[4] = { 150.0, 150.0, 150.0, 1.0 };// 『光照位置』

PLANE3D plane  = { 0.0, 1.0, 0.0, 0.0 };// 『投影平面』

構造投影矩陣

Build_Matrix_Shadow(&shadow_matrix,(VECTOR4D_PTR)&Light_Pos,(PLANE3D_PTR) &plane);

渲染陰影示例代碼

// 禁用對所有的然色分量的修改

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

// 禁用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為只讀

glDepthMask(GL_FALSE);

// 啟用模板測試

glEnable(GL_STENCIL_TEST);

// 設置模板比較程序

glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);

// 設置模板ref操作,

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);//

// 繪畫地板,將模板環存中相應的地板像素設為1

Draw_Floor();

// 啟用所有顏色分量的修改

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

//  啟用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為可讀寫

glDepthMask(GL_TRUE);

// 設置模板比較程序,只能在模板緩存中值為1的相應去區域繪製

glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);

// 設置模板操作

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);// 保持當前值

// 繪製”鏡像”

glPushMatrix();

// 反轉

glScalef(1.0f, -1.0f, 1.0f);

//結束鏡像繪畫

glPopMatrix();

// 繪畫地板

Draw_Floor();

// 以黑色繪製陰影,將其與相應表面進行混合,無光照不進行深度測試

glPushMatrix();

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_TEXTURE_2D);// 禁用2D紋理

glDisable(GL_LIGHTING);// 禁用光照

glDisable(GL_DEPTH_TEST);// 禁用深度測試

glEnable(GL_BLEND);// 啟用混合

// 確保不在任何一個光柵位置處進行重複繪製

glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);

// 黑色陰影

glColor4f(0.0f, 0.0f, 0.0f, blend);

// 以陰影進行投影

glMultMatrixf((float*)&shadow_matrix);// 投影矩陣

// 繪畫粒子暴風雪陰影

Draw_Snowstorm(&snowstorm, &Camera3D, &billboard);

glEnable(GL_TEXTURE_2D);// 禁用2D紋理

glEnable(GL_LIGHTING);// 禁用光照

glEnable(GL_DEPTH_TEST);// 禁用深度測試

glDisable(GL_BLEND);// 啟用混合

glPopAttrib();

glPopMatrix();

// 禁用模板測試

glDisable(GL_STENCIL_TEST);

// 繪畫粒子暴風雪

Draw_Snowstorm();

 

OpenGL之鏡像

OpenGL鏡像

『鏡像』並非是真實世界中由光粒子所產生.而式通過模擬『鏡像』技術.工作原理如下:

  1. 禁用顏色和深度緩存
  2. 啟用『模板緩存』後繪畫地板,對『模板緩存』進行寫操作
  3. 然後反轉『平面軸』渲染3D多邊形物體『鏡像』.
  4. 在3D空間中渲染平面
  5. 在平面的正面渲染3D多邊形物體

『鏡像』演示程式下載:

『鏡像』示例代碼:

// 禁用對所有的然色分量的修改

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

// 禁用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為只讀

glDepthMask(GL_FALSE);

// 啟用模板測試

glEnable(GL_STENCIL_TEST);

// 設置模板比較程序

glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);

// 設置模板ref操作,

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);//

// 繪畫地板,將模板環存中相應的地板像素設為1

Draw_Floor();

// 啟用所有顏色分量的修改

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

//  啟用深度測試

glEnable(GL_DEPTH_TEST);

// 深度緩存設為可讀寫

glDepthMask(GL_TRUE);

// 設置模板比較程序,只能在模板緩存中值為1的相應去區域繪製

glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);

// 設置模板操作

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);// 保持當前值

// 繪製”鏡像”

glPushMatrix();

// 反轉

glScalef(1.0f, -1.0f, 1.0f);

//繪畫正方體

Draw_Cube();

//結束鏡像繪畫

glPopMatrix();

// 繪畫地板

Draw_Floor();

// 禁用模板測試

glDisable(GL_STENCIL_TEST);

//繪畫正方體

Draw_Cube();

OpenGL之迷霧

OpenGL之迷霧

『迷霧』通過模糊遠端之物,而近端清晰.給3D世界帶來霧感.而且可以減小進場境中多邊形數量,從而提高渲染速度.演示程式按『+/-』鍵加減『迷霧』密度.下載『迷霧

OpenGL直接提供『迷霧』支持:

1.首先需要啟用『迷霧』

glEnable(GL_FOG);

2.將像素與『迷霧』顏色進行混合而實現,其混合因子的設定跟據其視點距離, 『迷霧密度』和『迷霧模式』,設定『迷霧』可通過以下函式:

void glFogf(GLenum pname,GLfloat param);

void glFogfv(GLenum pname,const GLfloat*params);

void glFogi(GLenum pname,GLint param);

void glFogiv(GLenum pname,const GLint*params);

Pname(參數名) Params(參數值)
GL_FOG_MODE 『迷霧』混合方程式,默認為GL_EXP

GL_LINEAR:factor=(end-z)/(end-start)

GL_EXP:factor=e(-density*depth)

GL_EXP2: factor=e(-density*depth)* (-density*depth)

GL_FOG_DENSITY 迷霧密度默認為1.0f
GL_FOG_START 迷霧離視點近端默認為0
GL_FOG_END 迷霧離視點遠端默認為1
GL_FOG_INDEX 迷霧顏色索引值默認為0
GL_FOG_COLOR 迷霧顏色默認為黑色(0,0,0,0).

啟用迷霧函是代碼

void Enable_Fog()

{

float fogColor[] = { 0.5, 0.5, 0.5, 1.0 };// 灰色

glEnable(GL_FOG);// 啟用霧

glFogfv(GL_FOG_COLOR, fogColor);// 霧色

glFogf(GL_FOG_MODE, GL_EXP2);// 顏色模式

glFogf(GL_FOG_START, 200);// 霧近端範圍

glFogf(GL_FOG_END, 1000);// 霧遠端範圍

glFogf(GL_FOG_DENSITY, 0.06f);// 密度

}

 

OpenGL之粒子系統

OpenGL之粒子系統

『粒子系統』由多個『粒子』,每個『粒子』都有其獨立『屬性』如『尺寸』『顏色』『速度』,每個『粒子』都可獨立運作.當大量『粒子』同時運作便可產生其特的3D效果.『飄雪』『流星』『爆炸』『飛機引擎火焰』.

『飄雪』演示程式在天空落雪,時用『廣告牌』技術另廣告牌總是對這鏡頭.下載『飄雪

粒子系統大約分為

1.初此化粒子系統

2.生成粒子

3.更新粒子系統

4.渲染粒子系統

基本『粒子』屬性 簡介
位置 3D空間中的座標
速度 基本上粒子都會移動,你需要方向向量再乘以時間而求得速度
尺寸 粒子的尺寸可能變化
重量 外力對粒子影響
顏色 為粒子設定渲染顏色
形態 常見『點』『直線』『紋理四邊形』
宿主 粒子擁有者
生命週期 粒子存活時間

定義粒子結構

typedef struct PARTICLE_TYP {

VECTOR3D pos;// 位置

VECTOR3D velocity;// 速度與方向

VECTOR3D acceleration;// 加速度

float energy;// 生命週期(秒)

float size;// 尺寸

float weight;// 重量

float color[4];// 顏色

}PARTICLE,*PARTICLE_PTR;

『粒子系統』屬性 簡介
粒子隊列 要管理的所有粒子
數量 粒子量
位置 活動範圍中心位置
範圍 粒子活動範圍
屬性 常見『點』『直線』『紋理四邊形』
紋理 把紋理存儲在『粒子系統』

Particle(粒子系統)

typedef struct PARTICLE_SYSTEM_TYP{

int          attr;// 屬性

PARTICLE_PTR array;// 數組

int          count;// 實制個數

VECTOR3D     origin;// 原點

VECTOR3D     velocity;// 速度與方向

VECTOR3D     velocity_variation;// 速度變量

float        energy;// 生命週期(秒)

//有效範圍

float        height;// 高

float        width; // 寬

float        depth; // 深

float        size;// 尺寸

TEXTURE_PTR  texture;// 紋理

}PARTICLE_SYSTEM, *PARTICLE_SYSTEM_PTR;

1.初此化粒子系統

bool Init_Particle(PARTICLE_SYSTEM_PTR system, int count,

VECTOR3D_PTR origin, VECTOR3D_PTR velocity, VECTOR3D_PTR variation,

float height, float width, float depth,float size,float energy,

TEXTURE_PTR texture)// 紋理

{

if (system == NULL)

return false;

memset(system,0,sizeof(PARTICLE_SYSTEM));// 清空

system->count = count; // 粒子數量

system->array = (PARTICLE_PTR)malloc(sizeof(PARTICLE)*count);// 分配空間

system->texture = texture;// 紋理

system->size    = size;// 尺寸

system->height  = height;// 高

system->width   = width;// 寬

system->depth   = depth;// 深

Copy_VECTOR3D(&system->origin, origin);// 原點

Copy_VECTOR3D(&system->velocity, velocity);//  速度與方向

Copy_VECTOR3D(&system->velocity_variation, variation);//  速度變量

PARTICLE_PTR particle;// 粒子

for (int i = 0; i < count; ++i)

{

particle = &system->array[i];// 粒子

Buid_Particle(system, particle);

}

return true;

}

2.生成粒子

void Buid_Particle(PARTICLE_SYSTEM_PTR  system, PARTICLE_PTR  particle)

{

particle->energy = system->energy;// 生命週期(秒)

particle->size   = system->size;// 尺寸

// 粒子位置

particle->pos.x = system->origin.x + FRAND_RANGE1() * (system->width /2.0f);// x

particle->pos.y = system->origin.y + FRAND_RANGE1() * (system->height/2.0f);// y

particle->pos.z = system->origin.z + FRAND_RANGE1() * (system->depth /2.0f);// z

// 粒子速度

particle->velocity.x = system->velocity.x  ;

particle->velocity.y = system->velocity.y  ;

particle->velocity.z = system->velocity.z  ;

}

3.更新粒子系統

void Update_Particle(PARTICLE_SYSTEM_PTR  system,float time)

{

PARTICLE_PTR         particle;// 粒子

for (int i = 0; i < system->count; ++i)

{

particle = &system->array[i];// 粒子

VECTOR3D vector;

Scale_VECTOR3D(&vector, &particle->velocity, time);

Add_VECTOR3D(&particle->pos, &particle->pos, &vector);// 移動粒子

// 判斷有無超出返圍

particle->energy = particle->energy – time;// 計算生命週期

if (particle->energy < 0 && particle->pos.y < 0)// 生命週期

Buid_Particle(system, particle);

}

}

4.繪畫貝粒子系統(紋理)

bool Draw_Texture_Particle(PARTICLE_SYSTEM_PTR system,

CAMERA3D_PTR camera, BILLBOARD_PTR billboard)

{

glPushMatrix();// 壓入矩陣

glPushAttrib(GL_ALL_ATTRIB_BITS);

glDisable(GL_LIGHTING);// 禁用光照

glDisable(GL_LIGHT0);// 禁用0號燈光

glEnable(GL_DEPTH_TEST);// 啟用深度測試

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glEnable(GL_ALPHA_TEST);// 啟用透面測試

glAlphaFunc(GL_GREATER, 0);// 透面色

PARTICLE_PTR         particle;// 粒子

for (int i = 0; i < system->count; ++i)

{

particle = &system->array[i];// 粒子

//檢查3D球體是否為於視錐體內

if (Compute_Sphere_In_Frustum(&camera->frustum, &particle->pos, particle->size) == true)

{   // 繪畫Billboard廣告牌

Draw_Billboard(billboard, &particle->pos, particle->size);

}

}

glPopAttrib();// 彈出屬性

glPopMatrix();// 彈出矩陣

return true;

}

 

OpenGL之廣告牌

OpenGL之廣告牌

當我地在游中渲染大量3D模型時,如果3D模型離視口很遠時,在屏幕上繪畫可能只是幾個像素,其中解卻方法是3D模型轉為2D圖像,然後作為紋理映射到四邊形.當視口靠近時再切換為3D模型.廣告牌作用是確保2D圖像此終面向視口.演示程式中按前後鍵移動相機,按左右鍵旋轉相機,仙人掌此終面向視口:下載

1.獲取當前『模型視圖矩陣』

typedef struct MATRIX4X4_TYP{

float M[4][4]; //以數組形式儲存

} MATRIX4X4, *MATRIX4X4_PTR;

glGetFloatv(GL_MODELVIEW_MATRIX, (float*)matrix.M);

2.提取視口右則向量

VECTOR3D  right;

Init_VECTOR3D(&right, matrix.M[0][0], matrix.M[1][0], matrix.M[2][0]);

3.提取視口右則向量

VECTOR3D  up;

Init_VECTOR3D(&up,matrix.M[0][1],matrix.M[1][1], matrix.M[2][1]);

4.移動廣告牌(四邊形)中心位置

glPushMatrix();

glTranslatef(position->x, position->y, position->z);

5.準備繪畫廣告牌(四邊形)

glBegin(GL_QUADS);

6.法線朝向

glNormal3f(0.0f, 0.0f, 1.0f);

7.重新計算廣告牌(四邊形)頂點位置,以實現廣告牌法線此終朝向視口

VECTOR3D  Vertex;

glTexCoord2f(0.0f, 0.0f); glVertex3fv(((right + up) * -size).M); // 左上角

glTexCoord2f(1.0f, 0.0f); glVertex3fv(((right – up) * size).M); // 右下角

glTexCoord2f(1.0f, 1.0f); glVertex3fv(((right + up) * size).M); // 右上角

glTexCoord2f(0.0f, 1.0f); glVertex3fv(((up – right) * size).M); // 左上角

8.完成繪畫廣告牌(四邊形)

glEnd();

glPopMatrix();// 彈出矩陣

OpenGL之NURBS(非均勻有理B樣條)

OpenGL之NURBS(非均勻有理B樣條)

貝賽爾(Bezier)曲線』(下面簡稱為『曲線』)跟據其『控制點』數目分類.擁有三個控制點『曲線』稱為『二次曲線』. 擁有四個控制點『曲線』稱為『三次曲線』.

隨著『曲線』控制點數目增加.曲線的平滑度可能會被大量控制點拉扯而被破壞.

所以引入NURBS(非均勻有理B樣條).『B樣條』其實就是『曲線』,只不過『B樣條』分為多個『曲線段』. 每個『曲線段』擁有四個控制點.將整條『曲線』分為若干『曲線段』,等於將多個『三次曲線』合併為『B樣條』.

『B樣條』有四個『控制點』,而每個『控制點』擁有兩個『節點』.其值可以取u和v域範圍任意值.『節點』影響『控制點』對『曲線』影響程度.這也是Bezier與NURBS區別之處.NURBS演示程式下載:

1.生成NURBS對象

GLUnurbsObj * object = gluNewNurbsRenderer();

2.定義八個『節點』

float knots[8] = { 0,0, 0,0, 1,1, 1,1 };

3.生成NURBS四個『控制點』

float nurbs[4][4][3] ;

4.設置NURBS曲面控制點,形成坨峰形

for (int u = 0; u < 4; ++u){

for (int v = 0; v < 4; ++v){

nurbs[u][v][0] = 3.0f*((float)u – 1.5f);

nurbs[u][v][1] = 2.0f*((float)v – 1.5f);

if ((u == 1 || u == 2) && (v == 1 || v == 2))

nurbs[u][v][2] = 3.0f;

else

nurbs[u][v][2] = -1.0f;

}

}

5.設置多邊形最大長度

gluNurbsProperty(object,GLU_SAMPLING_TOLERANCE,30.0f);

6.以多邊形繪製曲面

gluNurbsProperty(object,GLU_DISPLAY_MODE,GLU_FILL);

gluNurbsProperty()用於設置採樣以及如何繪畫NURBS

void gluNurbsProperty(GLUnurbs *nobj,GLenum property,GLfloat value);

參數 數值 簡介
nobj GLUnurbsObj * object; NURBS對象
property GLU_SAMPLING_TOLERANCE: 設置多邊形最大長度
GLU_DISPLAY_MODE: 多邊形渲染模式GLU_FILL:多變形實體填充
value   依property而定

7.設置NURBS曲面

gluBeginSurface(object);

8.準備繪製NURBS曲面

gluNurbsSurface(object,8,knots,8, knots,43,3,(GLfloat)&nurbs[0][0][0],4,4, GL_MAP2_VERTEX_3);

函式定義

void gluNurbsSurface(GLUnurbsnobj,GLint sknot_count,floatsknot,GLint  tknot_count,GLfloattknot,GLint s_stride,GLint t_stride,GLfloatctlarray,GLint sorder,GLint torder,GLenum type);

參數 簡介
nobj NURBS對象
sknot_count u節點個數
sknot u節點
tknot_count v節點個數
tknot v節點
s_stride u控制點距離
t_stride v控制點距離
ctlarray 指向控制點
sorder u方向控制點個數
torder v方向控制點個數
type 曲面類型

9.完成渲染

gluEndSurface(object);

 

 

OpenGL之貝賽爾(Bezier)曲面

OpenGL貝賽爾(Bezier)曲面

貝賽爾(Bezier)『曲面』與『曲線』相比不同之處在於多咗v空間域.曲面函式s(u,v).曲面演示程式按空格鍵在線框、填充、紋理模式中切換:下載

1.對曲面進行柵格映射

void glMap2f(GLenum target,GLfloat u1,GLfloat u2,GLint ustride,GLint uorder,GLfloat v1,GLfloat v2,GLint vstride,GLint vorder,const GLfloat *points);

函式示例:

glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3,u,  0,1, 3v,3,  (GLfloat)bezier->control_array);

參數 簡介
target 控制點類型
u1,u2 u空間域參數的定義域(u1~u2)
ustride u空間域頂點跨度
uorder u空間域頂點個數
v1, v2 v空間域參數的定義域(u1~u2)
vstride v空間域頂點跨度
vorder v空間域頂點個數
points 定點數組

 

控制點類型(target) 簡介
GL_MAP1_VERTEX_3 頂點座標(x,y,z)
GL_MAP1_VERTEX_4 頂點座標(x,y,z,w)
GL_MAP1_INDEX 顏色索引
GL_MAP1_COLOR_4 顏色值(RGBA)
GL_MAP1_NORMAL 法線座標
GL_MAP1_TEXTURE_COORD_1 紋理座標(s)
GL_MAP1_TEXTURE_COORD_2 紋理座標(s,t)
GL_MAP1_TEXTURE_COORD_3 紋理座標(s,t,r)
GL_MAP1_TEXTURE_COORD_4 紋理座標(s,t,r,g)

2.啟用貝賽爾(Bezier)曲面

glEnable(GL_MAP2_VERTEX_3);

3.定義定義均勻的柵格,並設置間隔個數

void glMapGrid2f(GLint un,GLfloat u1,GLfloat u2,GLint vn,GLfloat v1,GLfloat v2);

函式示例:

glMapGrid2f(10,0, 10, 10, 0, 10);

參數 簡介
un u域[u1~u2]間隔個數,必須為正
u1 u1=0
u2 u2=un
vn v域[v1~v2]間隔個數,必須為正
v1 v1=0
v2 v2=vn
  1. 根據柵格設置繪製曲面

void glEvalMesh2(GLenum mode,GLint i1,GLint i2,GLint j1,GLint j2);

函式示例:

glEvalMesh2(GL_FILL, 0, 10, 0, 10);

參數 簡介
mode 繪畫模式

GL_POINT:點

GL_LINE:直線

GL_FILL:填充

i1,i2 u空間域的範圍
j1,j2 v空間域的範圍

5.為曲面生成法線用於光照

glEnable(GL_AUTO_NORMAL);

6.對曲面進行紋理映射

glMap2f(GL_MAP2_TEXTURE_COORD_2, 0,1,2, 2,0,1, 4,2,(GLfloat*)bezier->texture_coord);

對曲面進行紋理映射glMap2f() 簡介
target 紋理座標(s,t):GL_MAP2_TEXTURE_COORD_2(睇上表)
u1,u2 s空間域:[0~1]
ustride s紋理座標頂點跨度:2
uorder s空間域頂點個數:2
v1,v2 t空間域:[0~1]
vstride t紋理座標頂點跨度:4
vorder t空間域頂點個數:2
points 紋理座標:{0.0, 0.0}, {0.0, 1.0}, {1.0, 0.0}, {1.0, 1.0}

4.啟用曲面紋理座標

glEnable(GL_MAP2_TEXTURE_COORD_2);

OpenGL之貝賽爾(Bezier)曲線

OpenGL貝賽爾(Bezier)曲線

OpenGL支持貝賽爾(Bezier)曲線繪畫,貝賽爾曲線由起點,終點和控制點組成,並且具有平滑的運動軌跡.控制點用於定義曲線的形狀,像磁石將曲線向控制點的位置吸引.上圖的演示程式通過滑鼠點選屏幕放置『控制點』繪畫貝賽爾(Bezier)曲線.ESC鍵清空『控制點』:下載

1.設置貝賽爾(Bezier)曲線

void glMap1f(GLenum target,GLfloat u1,GLfloat u2,GLint stride,GLint order,const GLfloat *points);

參數 簡介
target 控制點類型
u1, u2 u參數的定義域(0~1)
stride 頂點跨度
order 曲線頂點個數(由起點,終點和控制點組成)
points 指向曲線頂點數組由(起點,終點和控制點組成)

 

控制點類型(target) 簡介
GL_MAP1_VERTEX_3 頂點座標(x,y,z)
GL_MAP1_VERTEX_4 頂點座標(x,y,z,w)
GL_MAP1_INDEX 顏色索引
GL_MAP1_COLOR_4 顏色值(RGBA)
GL_MAP1_NORMAL 法線座標
GL_MAP1_TEXTURE_COORD_1 紋理座標(s)
GL_MAP1_TEXTURE_COORD_2 紋理座標(s,t)
GL_MAP1_TEXTURE_COORD_3 紋理座標(s,t,r)
GL_MAP1_TEXTURE_COORD_4 紋理座標(s,t,r,g)

2.啟用控制點

glEnable(GL_MAP1_VERTEX_3);

3.現在可以繪畫曲線,100條直線繪畫貝賽爾(Bezier)曲線,以均等間隔繪畫.

glBegin(GL_LINE_STRIP);

for (i = 0; i <= 100; ++i)

glEvalCoord1f((float)i / 100.0f);

glEnd();

上面的代碼可以簡化為

glMapGrid1d(100, 0.0f, 1.0f);// 定義定義均勻的間格

glEvalMesh1(GL_LINE,0, 100);// 繪製曲線

4.完成繪畫後需要繪畫控制點

glPointSize(5);

glBegin(GL_POINTS);

for (i = 0; i < bezier->countol_count; ++i)

           glVertex3fv(bezier->control_array[i].M);

glEnd();

 

OpenGL之二次曲面

OpenGL之二次曲面

如果要繪製『球體』,『圓柱』,『圓錐』,『圓盤』除了可以自已編寫圖形庫.還可以時用OpenGL Utility Library(OpenGL實用庫)提供的高級圖形支持.簡稱為GLUT.要下載可到官網下載: https://www.opengl.org/resources/libraries/glut/glutdlls37beta.zip 但windows版只支持x86.演示程式:下載

1.創建二次曲面對象,成功返回指向二次曲面的指針,否則返回NULL

GLUquadric*gluNewQuadric(void);

2.設置繪製風格

void gluQuadricDrawStyle(GLUquadric *quadObject,GLenum drawStyle);

drawStyle(繪畫樣式) 簡介
GLU_FILL 實體填充渲染
GLU_LINE 以線框模式渲染
GLU_SILHOUETTE 以輪框模式渲染,排除多邊形的共線.
GLU_POINT 以頂點模式渲染

3.設置法線生成

法線狀態用於設置二次曲面的表面法線的生成方式.

void WINAPI gluQuadricNormals(GLUquadric *quadObject,GLenum normals);

Normals(法線狀態) 簡介
GLU_NONE 不生成法線
GLU_FLAT 每個多邊形生成一個法線(單調明暗處理)
GLU_SMOOTH 每個頂點生成一個法線(平滑明暗處理) (默認值)

3.設置法線方向

設置生成法線的方向,內部還是外部.

Void gluQuadricOrientation(GLUquadric *quadObject,GLenum orientation);

Orientation(方向) 簡介
GLU_OUTSIDE 法線指向外側(默認值)
GLU_INSIDE 法線指向內側

4.設置紋理座標

紋理狀態是否為圖形生成紋理座標

void gluQuadricTexture(GLUquadric *quadObject,GLboolean textureCoords);

textureCoords 簡介
GL_TRUE 生成紋理座標
GL_FALSE 不生成紋理座標(默認值)

5.當不使用二次曲面後需要釋放資源

void gluDeleteQuadric(GLUquadricObj *state);

 

二次曲面繪畫

1.繪畫偏平的圓盤,中間可以有個圓孔

void gluDisk(GLUquadric *qobj,GLdouble innerRadius,GLdouble outerRadius,GLint slices,GLint loops);

繪畫偏平的扇形圓盤

void gluPartialDisk(GLUquadric *qobj,GLdouble innerRadius,GLdouble outerRadius,GLint slices,GLint loops,GLdouble startAngle,GLdouble sweepAngle);

參數 簡介
qobj 二次曲面對象
innerRadius 內圓直徑,若為0則是閉孔,大於零則中心有個孔
outerRadius 外圓直徑
Slices 繞z軸的細分數.此值越大圓盤越圓
Loops 同軸圓盤的邊形細分數
startAngle 扇形起此角度
sweepAngle 扇形圓弧角度

2.繪畫圓錐和圓柱

void gluCylinder(GLUquadric *qobj,GLdouble baseRadius,GLdouble topRadius,GLdouble height,GLint slices,GLint stacks);

分別有底座半徑與柱頂半徑若其一為0為圓錐,若兩值不相等則為錐形圓柱,若兩值相等則為圓柱. 底座圓面與柱頂圓面是繪畫的,你需要使用gluDisk()另外繪畫

圓柱沿Z軸繪畫,底座為於Z=0, 柱頂位於Z=height(高度)

參數 簡介
qobj 二次曲面對象
baseRadius 底座半徑 z=0
topRadius 柱頂半徑z=height
height 圓柱的高度
slices 繞Z軸的細分數.此值越大圓柱越圓
stacks 延Z軸的細分數.此值越高返射光的效果越好.

3.繪畫球體,按給定半徑以原點為中心進行繪製

void gluSphere(GLUquadric *qobj,GLdouble radius,Glint slices,GLint stacks);

參數 簡介
qobj 二次曲面對象
radius 球體的半徑
slices 繞z軸的細分數(相當於經線)
stacks 沿z軸的細分數(相當於緯線)

 

OpenGL之模板緩存

OpenGL之模板緩存

使用『模板緩存』可以將屏幕中的某些部分從視野中屏蔽.其中的特效是『鏡像』效果.鏡像(倒影)演示程式:下載

1.設置像素格式時對PIXELFORMATDESCRIPTOR對cStencilBits設置為 16bit『模板緩存』.

PIXELFORMATDESCRIPTOR pdf;

pfd.cStencilBits = 16;

2.要啟用『模板緩存』

glEnable(GL_STENCIL_TEST);

3.設置模板操作

void glStencilOp(GLenum fail,GLenum zfail,GLenum zpass);

fail:模板測試失敗

zfail:模板測試通過,但深度測試失敗

zpass:模板測試與深度測試均通過

操作 簡介
GL_KEEP 保持當前值
GL_ZERO 將模板緩存的值設定為0
GL_REPLACE 將模板緩存的值設定為glStencilFunc()的參考值ref.
GL_INCR 增加當前模板的緩存值
GL_DECR 減小當前模板的緩存值
GL_INVERT 將模板緩存的值反轉

4.設置模板比較程序

void glStencilFunc(GLenum func,GLint ref,GLuint mask);

func:比較程式

ref:參考值

mask:模板掩碼

比較程式 簡介
GL_NEVER 總是失敗
GL_LESS 條件比較:if (ref & mask) < (stencil & mask).
GL_LEQUAL 條件比較:if (ref & mask) ≤ (stencil & mask).
GL_GREATER 條件比較:if (ref & mask) > (stencil & mask).
GL_GEQUAL 條件比較: if (ref & mask) ≥ (stencil & mask).
GL_EQUAL 條件比較: if (ref & mask) = (stencil & mask).
GL_NOTEQUAL 條件比較: if (ref & mask) ≠ (stencil & mask).
GL_ALWAYS 總是允許

繪畫鏡像函式代碼演示:

1.禁用深度測試

glDisable(GL_DEPTH_TEST);

2.禁用對所有的然色分量的修改

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

3.啟用模板測試

glEnable(GL_STENCIL_TEST);

4.將地板繪製到模板緩存中,引用參考值ref

glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

glStencilFunc(GL_ALWAYS, 1, 1);

5.繪畫地板後雖然還未睇到地板,但已在模板緩存中生成孔,在孔中的多邊形將不被繪製

Draw_Floor();

6.啟用所有顏色分量的修改

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

7.啟用深度測試

glEnable(GL_DEPTH_TEST);

8.設置模板比較程序,只能在模板緩存中值為1的相應去區域繪製

glStencilFunc(GL_EQUAL, 1, 1);

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

9.壓入模型矩陣.準備繪製”鏡像”

glPushMatrix();

10.返轉境像

glScalef(1.0f, -1.0f, 1.0f);

11.繪畫鏡像(倒影)

Draw_Mirror();

11.彈出模型矩陣結束鏡像繪製

PopMatrix();

12.禁用模板測試

glDisable(GL_STENCIL_TEST);

13.以50%繪製透明地板, 啟用混合

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

glColor4f(1.0f,1.0f,1.0f,0.5f);

14.繪畫地板

Draw_Floor();

15.禁用混合

glDisable(GL_BLEND);

 

OpenGL之文本渲染

OpenGL之文本渲染

在OpenGL進行文本渲染.有以下幾種通用方法:

1.點陣字體

2.位圖字體,利用透面的背景色進行渲染.此方法更通用

4.2D位圖字體(OpenGL自動生成位圖,只支持Windows)

5.3D輪廓字體(OpenGL自動3D字體模型,只支持Windows)

6.3D輪廓字體支持紋理映射(OpenGL自動紋理座標,只支持Windows)

 

OpenGL之紋理映射字體

OpenGL之紋理映射字體

因為『3D輪廓字體』只是填充純色,但可以對『3D輪廓字體』應用紋理映射,以增強其外觀效果.紋理座標可以讓OpenGL自動生成.演示程式下載:

1.讓OpenGL為文本生成紋理座標,並固定於3D模型

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

glEnable(GL_TEXTURE_GEN_S);

glEnable(GL_TEXTURE_GEN_T);

2.載入紋理

Load_File_Texture(&font3D->texture, hInstance, filename);

3.綁定紋理

glBindTexture(GL_TEXTURE_2D, texture->texID);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

OpenGL之3D輪廓字體

OpenGL之3D輪廓字體

之前一直使用Windows自帶字體輸出文本,但只限2D的『位圖字體』.在Windows下有獨有輪廓字體函式.讓文本具有一定的厚度.讓所有的Windows字體轉換成3D字體.演示程式可輸入英文字符,下載程式:

1.存儲輪廓字體位置和方向,生成256個GLYPHMETRICSFLOAT變量

GLYPHMETRICSFLOAT gmf[256];

2.創建大小為256個字符的顯示列表,對所有256個ASCII碼支持.

UINT base;// 顯示列表的BASE ID

base = glGenLists(256);

3.創建字體句柄

HFONT font;

font=CreateFont(1,0,0,0,FW_BOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,NTIALIASED_QUALITY,FF_DONTCARE|DEFAULT_PITCH,”Arial”);

4.為字體選擇設備環境

SelectObject(hDC,font);

5.創建3D輪廓字體,每個ASCII對應一個輪廓字體

wglUseFontOutlines(hDC,0,256,base,0.0f,FONT3D_DEPTH,WGL_FONT_POLYGONS,gmf);

6.複製當前矩陣並壓棧

glPushMatrix();

7.將文本定位於場景空間的中央.計算文本的長度

length = (int)strlen(text);

for(int i=0;i<length; ++i)

{

char  c = text[i];

width = width + gmf[c].gmfCellIncX;

if(gmf[c].gmfCellIncX > height )

height = gmf[c].gmfCellIncX;

}

8.設定文本座標

glTranslatef(posX – (width0.1f)/2.0f,posY – (height0.1f)/2.0f ,posZ + (FONT3D_DEPTH*0.1f)/2.0f);

9.顯示列表屬性壓棧

glPushAttrib(GL_LIST_BIT);

10.載入顯示列表ID

glListBase(base);

11.渲染文本

glCallLists((int)strlen(text),GL_UNSIGNED_BYTE,text);

12.顯示列表屬性出棧

glPopAttrib();

OpenGL之位圖字體

OpenGL之位圖字體

要在屏幕上顯示文本可以使用Windows自帶字體進行渲染.比起之前使用『點陣字體』方便簡潔很多,但此方法只式用於Windows.演示程式可輸入英文字符,下載程式:

1.創建96個顯示列表IDs,存儲生成的字符位圖

UINT base;// 顯示列表的BASE ID

base = glGenLists(96);//創建大小為96BYTE的顯示列表

2.創建顯示列表後,使用CreateFont()創建字體

HFONT font;// 字體句柄

font=CreateFont(12,0,0,0,FW_BOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,FF_DONTCARE | DEFAULT_PITCH,”Courier”);

3.為字體選擇一個設備環境

SelectObject(hDC,font);

4.從系統載入字體文檔並生成位圖.

wglUseFontBitmaps(hDC,32,96,base);

5.設定屏幕位置

glRasterPos2i(xPos,yPos);

7.顯示列表屬性壓棧

glPushAttrib(GL_LIST_BIT);

6.載入顯示列表ID

glListBase(base-32);

7.渲染文本

glCallLists((int)strlen(text),GL_UNSIGNED_BYTE,text);

8.顯示列表屬性出棧

glPopAttrib();

OpenGL之視錐體裁剪

OpenGL之視錐體裁剪

圖形引擎繪製任何睇不見(視錐截體之外)的任何物體,都會浪費保貴GPU資源.相機的視錐體定義你所能夠梯到的3D世界空間.視錐體由相機的位置和視野組成.視錐體由六個平面組成.只要進入視錐體內的3D模型都應被渲染.(被其它3D模型遮檔除外).位於視錐體之外則不必渲染.

OpenGL會自動丟棄在視錐體之外的三角形.但OpenGL是逐個三角形進行視錐體裁剪.當你有大量(幾萬個)的三角形進入OpenGL裁剪隊列時.會大大地降低程式性能.

最簡單的優化方式是定義3D球形容器,用於包裹你的3D模型.然後使用視錐體與3D球形進行測試.如果3D球形完全在視錐體之外.即可放棄渲染3D模型.如果3D模型部分或全部進入視錐體內,則交由OpenGL繼序進行三角形裁剪

演示程式中有幾百個隨機移動球體,按空格鍵啟用或禁用『視錐體裁剪』.未啟用之前不到200幀.一但啟動『視錐體裁剪』則超過1000幀.性能大幅提高五倍(視渲染球體的個數而定).程式下載:

 

定義3D球體

typedef struct SPHERE3D{

float    x, y, z;// 中心點

float    radius;// 球體半徑

}SPHERE3D,* SPHERE3D_PTR;

 

定義3D平面

typedef struct PLANE3D_TYP{

float A, B, C;// 平面法線向量

float D;//平面到原點最近距離

}PLANE3D,*PLANE3D_PTR;

 

定義視錐體

typedef struct FRUSTUM_TYP {

PLANE3D planes[6];//6個平面

}FRUSTUM, *FRUSTUM_PTR;

 

1.平面方程式定義:

Ax + By + Cz + D = 0

A,B,C:平面的法線向量

D:從平面到原點的距離

x,y,z:平面上的任意點

結果為零:該點落在平面上

結果為正:該點為于平面的前方

結果為負:該點為於平面的後方

 

2.確定視錐體尺寸,獲取投影矩陣和模型視口矩陣

glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&projection);

glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelview);

 

3.投影矩陣乘以模型視口矩陣

glPushMatrix();

glLoadMatrixf((GLfloat*)&projection);

glMultMatrixf((GLfloat*)&modelview);

glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelview);

glPopMatrix();

 

4.提取視錐體的六個平面

int scale = (row < 0) ? -1 : 1;

row = abs(row) – 1;

plane->A = mat->M[0][3] + scale * mat->M[0][row];

plane->B = mat->M[1][3] + scale * mat->M[1][row];

plane->C = mat->M[2][3] + scale * mat->M[2][row];

plane->D = mat->M[3][3] + scale * mat->M[3][row];

平面(plane) 行(row)
左則 1
右則 -1
底部 2
頂部 -2
近端 3
遠端 -3

 

5.平面歸一化

float length = sqrtf(plane->A * plane->A + plane->B * plane->B + plane->C * plane->C);

plane->A /= length;

plane->B /= length;

plane->C /= length;

plane->D /= length;

 

6.檢查3D球體是否為於視錐體內

frustum:視錐體

sphere:球體

返回值:若在視錐體內返回TRUE,否則反回FALSE

bool Compute_Sphere_In_Frustum(FRUSTUM_PTR frustum, SPHERE3D_PTR sphere)

{

float dist;

for (int i=0;i<6;++i)

{// 判斷球體是否為與六個平面之前

PLANE3D_PTR plane = &frustum->planes[i];

// 計算點與平面的距離,若為正在視錐體內,若為負在視錐體外

dist = plane->A * sphere->x + plane->B * sphere->y + plane->C * sphere->z + plane->D;

if (dist <= -sphere->radius)

return false;// 在視錐體外

}

return true; // 在視錐體內

}

 

OpenGL之數組鎖定

OpenGL之數組鎖定

OpenGL允許鎖定(lock)與解鎖(unlock)數組.當鎖定(lock)數組後,數據便不能更改.當存在大量共用的頂點,並且進行多次操作.能大大地提高運行速度

 

數據初此化後鎖定數組

void glLockArraysEXT(GLint first, GLsizei count);

first:頂點索引

count:頂點個數

 

程序退出時對數組解鎖

void glUnlockArraysEXT(void);

 

演示程式中,因為地形數據不會改變,對其進行鎖定.按空格鍵可切換鎖定(lock)與解鎖(unlock).幀數大約提升10%.只是兩行代碼就有不小的性能改進.下載:

OpenGL之頂點數組與多紋理映射

OpenGL之頂點數組與多紋理映射
將頂點數組『VertexArray』應用於多紋理『Multitexture』映射,由於每個紋理單元都有其自已的狀態.要將每個紋理單元都單度地啟用頂點數組,並且為其每個紋理單元設定紋理座標的頂點數組.通過常試可以將多個2D紋理(GL_TEXTURE_2D)進行映射,但2D紋理與1D紋理(GL_TEXTURE_1D)多次償試都無法將『頂點數組』應用『紋理數組』映射暈.演示程式按空格鍵切換『多紋理映射』草地紋理與高度紋理.兩者幀數相約.下載:

 

要讓紋理單元使用頂點數組,必須先激活它.

void glClientActiveTexture(GLenum texture);

texture:紋理單元索引GL_TEXTURE0_ARB~GL_TEXTURE31_ARB

 

啟用紋理單元頂點座標數組

void glEnableClientState(GLenum array);

禁用紋理單元頂點座標數組

void glDisableClientState(GLenum array);

array:GL_TEXTURE_COORD_ARRAY 紋理座標數組

 

為紋理單元設定指定數組

void WINAPI glTexCoordPointer(Glint size,GLenum type,GLsizei stride,const GLvoid  *pointer);

 

設置兩個紋理單元設置頂點數組

glClientActiveTexture(GL_TEXTURE0_ARB);// 激活紋理0 

glEnableClientState(GL_VERTEX_ARRAY); // 啟用頂點數組

glEnableClientState(GL_TEXTURE_COORD_ARRAY); // 啟用紋理座標數組

glVertexPointer(3, GL_FLOAT, 0, terrain->vertex_array);// 頂點數組

glTexCoordPointer(3, GL_FLOAT, 0, terrain->grass_texcoord);// 紋理數組

glClientActiveTexture(GL_TEXTURE1_ARB);// 激活紋理1

glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glTexCoordPointer(3, GL_FLOAT,0, terrain->height_texcoord);// 紋理數組

glDrawArrays(GL_TRIANGLES, 0, polygon_num);// 繪畫所有當前以啟用的頂點數組

glDisable(GL_TEXTURE_1D);// 禁用紋理單元1

glClientActiveTexture(GL_TEXTURE0_ARB);// 重新激活紋理0,這個很重要否則會影響其它紋理貼圖

glDisableClientState(GL_VERTEX_ARRAY); // 禁用頂點數組

glDisableClientState(GL_TEXTURE_COORD_ARRAY); // 禁用紋理座標數組

glDisableClientState(GL_NORMAL_ARRAY);// 禁用法線數組

OpenGL頂點數組

OpenGL頂點數組

遊戲的實際的開發中將會大量頻繁地處理頂點,在簡單多邊形可以使用代碼直接生成模型頂點.而真正的遊戲隨便一個模型就可能有幾百或幾千個多邊形.不可能使用代碼直接生成.解決的方法是使用『頂點數組』(Vertex Array).通過建模軟件進形3D建模,然後輸出特定的文本文檔或二進制文檔.可分為以下幾個部驟.

  1. 從磁盤上的模型文檔中載入模型的頂點數據.
  2. 將頂點數據儲存在數組(array),如將頂點座標存入獨立數組,法線數組,顏色數組.
  3. 當OpenGL需要頂點數據時,載入相應的數據.

 

啟用『頂點數組』

void glEnableClientState(GLenum array);

禁用『頂點數組』

void glDisableClientState(GLenum array);

參數 簡介
GL_COLOR_ARRAY 啟用頂點顏色數組
GL_EDGE_FLAG_ARRAY 啟用頂點的邊EdgeFlag數組
GL_INDEX_ARRAY 啟用頂點的調色板索引數組
GL_NORMAL_ARRAY 啟用法線數組
GL_TEXTURE_COORD_ARRAY 啟用紋理座標數組
GL_VERTEX_ARRAY 啟用定點座標數組

 

載入頂點的顏色數組:

void glVertexPointer(GLint size,GLenum type,GLsizei stride,const GLvoid  *pointer);

參數 簡介
size 頂點顏色分量其值只能為3(rgb)或4(rgba)
type 數組的數據類型

GL_BYTE

GL_UNSIGNED_BYTE

GL_SHORT

GL_UNSIGNED_SHORT

GL_INT

GL_UNSIGNED_INT

GL_FLOAT

GL_DOUBLE

stride 跨度,兩個顏色之間的字節數,如果顏色數據是緊湊則填為0
pointer 頂點的顏色數組

 

載入邊(Edge)數組:

void glEdgeFlagPointer(GLsizei stride,const GLvoid *pointer);

參數 簡介
stride 跨度
pointer 多邊形邊(Edge)數組bool類型數值

 

載入調色板顏色索引數組

void glIndexPointer(GLenum type,GLsizei stride,const GLvoid *pointer);

參數 簡介
type 數組的數據類型

GL_SHORT

GL_INT

GL_FLOAT

GL_DOUBLE

stride 跨度相鄰索引之間的字節數
pointer 調色板顏色索引數組

 

載入頂點的法線向量,每三個元素組成一個法線向量

void glNormalPointer(GLenum type,GLsizei stride,const GLvoid *pointer);

參數 簡介
type 數組的數據類型

GL_BYTE

GL_SHORT

GL_INT

GL_FLOAT

GL_DOUBLE

stride 跨度相鄰法線之間的字節數
pointer 頂點的法線向量數組

載入頂點的紋理座標數組void glTexCoordPointer(Glint size,GLenum type,GLsizei stride,const GLvoid  *pointer);

參數 簡介
size 頂點的座標數,其值只能為1,2,3,4

1為1維紋理(s)

2為2維紋理(s,t)

3與4較小使用

type 數組的數據類型

GL_SHORT

GL_INT

GL_FLOAT

GL_DOUBLE

stride 跨度,兩個紋理之間的字節數
pointer 頂點紋理座標數組

 

載入頂點座標數組:

void glVertexPointer(Glint size,GLenum type,GLsizei stride,const GLvoid *pointer);

參數 簡介
size 頂點的座標分量:2,3,4
type 數組的數據類型

GL_SHORT

GL_INT

GL_FLOAT

GL_DOUBLE

stride 跨度,兩個頂點座標之間的字節數,如果是緊湊方置其值為0
pointer 頂點座標數組

繪畫所有當前以啟用的頂點數組void glDrawArrays(GLenum mode,GLint first,GLsizei count);

參數 簡介
mode GL_POINTS:點

GL_LINE_STRIP:相連的直線

GL_LINE_LOOP:閉合的相連直線

GL_LINES:非相連的直線

GL_TRIANGLE_STRIP:相連的三角形 GL_TRIANGLE_FAN:共用頂點三角形

GL_TRIANGLES:度立的三角形

GL_QUAD_STRIP:相連的四邊形

GL_QUADS:四邊形

GL_POLYGON:任意頂點多邊形

first 數組起此索引
count 繪畫的頂點量

 

以任意順序繪畫以啟用的頂點數組void glDrawElements(GLenum mode,GLsizei count,GLenum type,const GLvoid *indices);

參數 簡介
mode 與glDrawArrays()的一致
count 索引數組的長度
type 索引的數據類型,只能是無符號整數GL_UNSIGNED_BYTE GL_UNSIGNED_SHORT GL_UNSIGNED_INT
indices: 索引數組

按值定的範圍繪畫以啟用的頂點數組

void glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices);

參數 簡介
mode 與glDrawArrays()的一致
start 索引數組開此索引
end 索引數組結束索引
count 索引數組的長度
type 索引的數據類型,只能是無符號整數與glDrawElements()的一致
indices: 索引數組

按數組索引繪畫單一個頂點void glArrayElement(GLint index);

參數 簡介
index 頂點的索引

演示程式中繪畫幾百個球體不斷移動,若不使用頂點數組每秒只有約66幀,而若使用頂點數組性能大幅度提升到每秒有200幀.性能有驚人的提升(不同的計算機性能有所差別).按空格鍵啟用或禁用頂點數組.演示程式下載:

 

OpenGL顯示列表

OpenGL顯示列表

OpenGL支持稱為『顯示列表』(Display List)性能優化,它相當於把OpenGL代碼進行預編譯,並載入顯卡記憶體,從而降低系統開銷,但它對於程式的性能改善並不總是特別明顯,而且不同的圖形卡廠商的實現也有方式各異,最終效果視情況而定,不過最差的情況下也要比不使用好.

 

生成顯示列表, 返回列表索引

GLuint glGenLists(GLsizei range);

range:顯示列表數量

 

判斷顯示列表是否有效,有效則返回GL_TRUE

GLboolean glIsList (GLuint list);

list:列表索引

 

向顯示列表填充命令,把代碼寫在glNewList()與glEndList()

void glNewList(GLuint list,GLenum mode);

list:列表索引

mode:編譯模式分別有GL_COMPILE(編譯)和GL_COMPILE_AND_EXECUTE(編譯並執行)

 

結束填充數據

void glEndList(void);

 

當你擁有顯示列表,可以在任何地方調用顯示列表

void glCallList(GLuint list);

 

如果要一次調用多個顯示列表,依次調用.

void glCallLists(GLsizei num,GLenum type,const GLvoid *lists);

num:顯示列表個數

type:索引的類型

lists:顯示列表索引數組

 

如果想其它索引值開次執行,從offset開此到num-1結束

void glListName(GLuint offset);

offset:索引數組的偏移

 

顯示列表的燒毀,當創建顯示列表需要為其分配記憶體存儲OpenGL代碼,當程式結束時要將記憶體釋放,防止記憶體洩漏

void glDeleteLists(GLuint list,GLsizei range);

list:列表索引

range:顯示列表數量

 

執行顯示列表演示代碼:

if(glIsList(list->ID) == GL_TRUE)

{

glCallList(list->ID);// 調用顯示列表

}

else

{

list->ID = glGenLists(1);// 生成顯示列表

glNewList(list->ID, GL_COMPILE_AND_EXECUTE);

// 填入執行代碼

glEndList();

}

 

演示程式中繪畫幾百個球體不斷移動,若不使用顯示列表每秒大約200幀,而若使用顯示列表性能大幅度提升,每秒去到670幀.真是出乎意料.按空格鍵啟用或禁用顯示列表.下載演示程式:

OpenGL之圖像合成器

OpenGL之圖像合成器

對圖像(紋理)合成你可以得到圖像變換的動畫效果,如『燈火』.通過讀取兩張圖像,然後對其進行插值運算,最後生成平滑過渡的效果.演示程式下載:

對兩個或者多個紋理進行組合:

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_COMBINE);

紋理平滑過渡

GLfloat texEnvColor[] = {0.0f,0.0f,0.0f,combiner->interpol };

glActiveTexture(GL_TEXTURE1);

glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR, texEnvColor);

載入紋理單元0

Load_File_Texture(&combiner->texture0, hInstance, filename0);

激活紋理單元0

glActiveTexture(GL_TEXTURE0);

綁定紋理0

Bind_Image_Texture(&combiner->texture0);

glEnable(GL_TEXTURE_2D);

將紋理傳遞到下一個單元

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

載入紋理1

Load_File_Texture(&combiner->texture1, hInstance, filename1);

激活紋理單元1

glActiveTexture(GL_TEXTURE1);

綁定紋理1

Bind_Image_Texture(&combiner->texture1);

glEnable(GL_TEXTURE_2D);

設置紋理組合模式

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_COMBINE);

使用插值組合函式

glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);

設置成紋理單元0的輸出

glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);

設置成當前紋理圖像

glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE);

為紋理單元設置ARG2

glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);

使用alpha常數修改RGB分量

glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);

OpenGL之環境映射

OpenGL之環境映射

環境映射即在表面『映射』其它物體,如湖面上『映射』出『樹、雲、天、人』,而OpenGL並不是真正地對環境進行反射獲得的效果.演示函式下載:

  1. 創建物體表面的紋理圖
  2. 使用OpenGL自動生成相應的紋理座標,可以使用GL_SPHERE_MAP(球體映射)和GL_REFLECTION_MAP(反射光映射)
  3. 移動鏡頭到物體並指向視點
  4. 隨著物體的移動而生成新的紋理座標,使得環境反射效果隨著物體移動而變化,使用glCopyTexImage2D()和glCopyTexSubImage2D()從屏幕上拷貝並生成紋理
  5. 如果要填充多邊形顏色,你要禁用紋理glDisable(GL_TEXTURE_2D);才能正確渲染.

 

紋理座標生成函式:

void glTexGenf(GLenum coord,GLenum pname, GLenum param)

glTexGenf()函式coord: 簡介
GL_S s紋理坐標
GL_T t紋理坐標
GL_R r紋理坐標
GL_Q q紋理坐標

 

glTexGenf()函式pname: param簡介
GL_TEXTURE_GEN_MODE GL_SPHERE_MAP

GL_REFLECTION_MAP

GL_OBJECT_LINEAR

GL_EYE_LINEAR

GL_NORMAL_MAP

GL_OBJECT_PLANE Param指向生成紋理坐標所需的4個元素,配合GL_OBJECT_LINEAR使用
GL_EYE_PLANE Param指向生成紋理坐標所需的4個元素,配合GL_EYE_LINEAR使用

 

生成環境映射紋理代碼

glGenTextures(1, &texture.ID);

glBindTexture(GL_TEXTURE_CUBE_MAP, texture.ID);

設定紋理參數代碼

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

將紋理坐標生成模式設置為環境映射(反射光)函式

glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);

glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);

glTexGenf(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);

 

移動鏡頭到物體並指向視點,設置屏幕以匹配環境映射的大小

glViewport(0, 0, 256,256);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

設置90度視口

gluPerspective(90, 1, 0.1, 500);

glMatrixMode(GL_MODELVIEW);

glClear(GL_DEPTH_BUFFER_BIT);

載入單位矩陣

glLoadIdentity();

設定相機

gluLookAt(0.0, 0.0, 0.0,posx,posy,posz,neggx,negy,negz);

通過glCopyTexImage2D()和glCopyTexSubImage2D()從屏幕上拷貝並生成環鏡映射紋理

從緩衝區中拷貝像素創建紋理函式

typedef void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);

從緩衝區中拷貝部分像素創建紋理函式

typedef void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);

 

渲染環鏡映射代碼

glEnable(GL_TEXTURE_GEN_S);

glEnable(GL_TEXTURE_GEN_T);

glEnable(GL_TEXTURE_GEN_R);

使用立方體映射

glEnable(GL_TEXTURE_CUBE_MAP);

glBindTexture(GL_TEXTURE_CUBE_MAP, texture.ID);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

gluSphere(quadric, 1.0, 64, 64);// 繪畫大球球體

glDisable(GL_TEXTURE_CUBE_MAP);

glDisable(GL_TEXTURE_GEN_S);

glDisable(GL_TEXTURE_GEN_T);

glDisable(GL_TEXTURE_GEN_R);

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: 紋理坐標

 

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

}

 

OpenGL之紋理映射使用mipmap

OpenGL之紋理映射使用mipmap

紋理繪畫多邊形當遠離視點,會出現視覺失真或炫目問題,原因是OpenGL對兩個相鄰的像素進行采樣時,從紋理圖中的一些差別較大的部分進行采樣所產生的,在靜止場景問題並不明顯,若運動時紋理紋理采樣發生改變時炫目問題將會更特出,使用mipmap『紋理鏈』能消除部分失真,因為低解釋度的紋理被應用到遠距離的多邊形上時采樣更加精准.因為紋理一級級縮小時紋理可以保持平滑,使用mipmap『紋理鏈』的另一個好處是較小的紋理更容易保存在顯卡『記憶體』中提高命中率.

OpenGL的gluBuild2DMipmaps()可以全自動的生成mipmap『紋理鏈』.但不仿自已動手生成.

mipmap『紋理鏈』演示程式下載:

  1. 按M鍵可切換『紋理鏈』啟用與禁用,當啟用時炫目失真將大副減小.
  2. 按ESC鍵相機返回原點.
  3. 按方向鍵移動相機

紋理結構定義可梯『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]);    //載入紋理

}

 

OpenGL之紋理映射

OpenGL之紋理映射

將紋理映射到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);// 綁定紋理

}

 

OpenGL之讀取Targa圖檔-RLE解碼

OpenGL之讀取Targa圖檔-RLE解碼

『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索引

}

}

}

OpenGL之讀取Targa圖檔

OpenGL之讀取Targa圖檔

『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;//返回

}

OpenGL之讀取Bitmap圖檔-RLE解碼

OpenGL之讀取Bitmap圖檔-RLE解碼

『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;// 重複的像素

}

}

}

OpenGL之讀取Bitmap圖檔

OpenGL之讀取Bitmap圖檔

『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;//返回

}

OpenGL之繪畫圖像文檔

OpenGL之繪畫圖像文檔

在屏幕上渲染圖檔與模型貼上紋理總會令人興奮,幸好在OpengGL繪畫圖像文檔也並不困難,並且實現左示例程式.下載:

  1. 載入並解釋圖像文檔如『.bmp』『.tga』『.pcx
  2. 設置圖像左下角繪畫在屏幕位置glRasterPos2f(x, y);
  3. 設置圖像的縮放比glPixelZoom(xfactor,yfactor);縮放因子=屏幕尺寸/圖像尺寸
  4. 繪畫圖像glDrawPixels(width,height,format,type,pixels);與『DirectDraw』相比稍複雜點, 『DirectDraw』支持32Bit、16Bit、256色.而OpenGL卻直接支持32Bit、24Bit、256色、但要將像素『Pixel』倒轉排成RGB/RGBA順序
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代碼演示:

  1. glPixelStorei(GL_UNPACK_ALIGNMENT, 4);// 4字節對齊
  2. glRasterPos2f(x, y);// 設定圖像左下角在屏幕的位置
  3. glPixelZoom((float)screen_width/(float)image_width, (float)screen_height / (float)image_height); // 設置全屏縮放
  4. glDrawPixels(image_width,image_height,format,type,image); // 繪畫

OpenGL之點陣字體

OpenGL之點陣字體

在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的函式為例:

  1. const unsigned char BITMAPFONT_B[] //16Bit * 16Bit 點陣字體={ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0x00,0x66,0x00,0x66,0x00,0x66,0x00, 0x66,0x00,0x7C,0x00,0x66,0x00,0x66,0x00,0x66,0x00,0xFC,0x00,0x00,0x00,0x00,0x00};
  2. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);// 對齊像素字節
  3. glColor3f(1.0f, 1.0f, 1.0f);// 顏色
  4. glRasterPos2i(x, y);// 設定位圖的位置
  5. glBitmap(16, 16, 0, 0, 16, 0, BITMAPFONT_B);// 渲染字符B

點陣字體的程序示例:下載

  1. 把輸入法設為英文
  2. 通過WM_CHAR接收ASCII碼並在屏幕繪畫
  3. 在遊戲設計中此方法也適用漢字渲染,只要把漢字轉換為點陣字體

OpenGL之載入地形文檔

OpenGL之載入地形文檔

地形文檔就是256色位圖文檔,因為位圖即點陣圖.點的數值越大地形越高,數值越低形成低窪地帶.最終生成高山、湖水.

地形演示程式:下載

  1. 按+/-鍵移動煙霧距離
  2. 按{}鍵升高或降低湖水高度
  3. OPEN/FILE載入地形文檔,支持拖放.
  4. 只支持RAW格式
  5. 按ESC鍵重置聚地形文檔
  6. 壓縮包內的terrain目錄附有三個地形文檔

繪畫地形思路如下:

  1. 設地形的最大高度為10
  2. 湖水平面設為1
  3. 因為像素8Bit最大256色, 所以縮放因子等於256除以地形的最大高度scale=256/10
  4. 地形由三角形所組成, 遍曆所有像素生成三角形.
  5. 地形的顏色由綠色分量組成,Y座標越高顏色越明亮.
  6. 通過像素的索引生成三角形XZ座標
  7. 通過像素的顏色值生成三角形Y座標,Y=像素顏色/縮放因子
  8. 繪畫藍色湖水平面覆蓋低窪地帶

成生地形RAW文檔

  1. 打開Photoshop並載入高空拍攝圖
  2. 大小轉換為64*64像素
  3. 圖像/模式/灰度
  4. 圖像/模式/8位通道
  5. 儲存為.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();

}

OpenGL之煙霧

OpenGL之煙霧

煙霧(Fog)使遠距離的物體變暗,近距離的物體變得清晰.是遊戲中常用特效.OpenGL煙霧將每個像素煙霧色進行混合處理.

煙霧演示程式:下載

  1. 按+/-鍵移動煙霧距離
  2. 按ESC鍵重置聚煙霧

煙霧函式演示:

  1. glEnable(GL_FOG);
  2. GLfloat fogColor[] = { 0f, 0.0f, 0.0f };
  3. glFogfv(GL_FOG_COLOR, fogColor);
  4. glFogi(GL_FOG_MODE, GL_LINEAR);
  5. glFogi(GL_FOG_COORD_SRC,GL_FRAGMENT_DEPTH);
  6. glFogf(GL_FOG_START, start);
  7. glFogf(GL_FOG_END, end);
  8. glFogf(GL_FOG_DENSITY, 2f);
  9. Draw_Terrain();
  10. glDisable(GL_FOG);

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)

 

OpenGL之顏色混合

OpenGL之顏色混合

OpenGL之顏色混合alpha通過glBlendFunc()設定的混合因子生成透明效果,但通過其它的混合因子可產生更多不同的混合效果.令外OpenGL的顏色混合是無需啟用光照的glEnable(GL_LIGHTING); 但需要啟動多邊形隱面裁剪::glEnable(GL_CULL_FACE);否則會有一面會繪畫錯誤.

混合因子演示程式.下載

  1. 設定Sour Factor選擇”來源混合因子”
  2. 設定Dest Factor選擇”目標混合因子”
  3. 設定Blend Equation選擇”混合公式”
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)作為混合權重係數

 

OpenGL之顏色混合alpha

OpenGL之顏色混合alpha

顏色混合(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:完全不透明

函式示例

  1. glEnable(GL_BLEND);// 啟用混合
  2. glDepthMask(GL_FALSE);// 將深度環存設置為只讀
  3. glBlendFunc(GL_SRC_ALPHA,GL_ONE); // 透明效果
  4. glColor4f(1.0f, 1.0f, 1.0f,alpha);//月球模型顏色
  5. auxSolidSphere(1.0f);// 繪畫
  6. glDepthMask(GL_TRUE);// 將深度環存設置為可讀可寫
  7. glDisable(GL_BLEND);// 禁用混合
混合函式 簡介
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演示程式示例,分別有太陽,地球和月球.下載程式

  1. 按+/-鍵增加或減小月球透明度
  2. 按SPACE(空格)鍵啟用或禁用聚光燈
  3. 按UP/DOWN鍵相機前後移動
  4. 按LEFT/RIGHT鍵相機左右旋轉
  5. 按F1鍵打開幫助
  6. 按ESC鍵重置聚光燈參數

OpenGL之鏡面反射光

OpenGL之鏡面反射光

鏡面反射產生閃耀的光輝,此光照效果很有趣,需要分別設置光源材質.

光源和材質 簡介
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:高度聚焦

函式示例

  1. float Light_Specular[] = {0f,1.0f,1.0f,1.0f};// 鏡面反射光分量
  2. float Light_Position[] = {0f,0.0f,1.0f,1.0f};// 燈光位置
  3. glLightfv(light,GL_SPECULAR, Light_Specular);// 鏡面反射光分量
  4. glLightfv(light,GL_POSITION, Light_Position);// 光源位置
  5. Material_Specular[] = {0f,1.0f,1.0f,1.0f};// 鏡面反射光材質
  6. glMaterialfv(GL_FRONT,GL_SPECULAR, Material_Specular);// 鏡面反射光材質
  7. glMaterialf (GL_FRONT,GL_SHININESS,10);// 鏡面反射光聚焦度

鏡面反射光程式示例,行星發出閃耀的光輝,而衛星繞行星旋轉並且發光,分別使用聚光燈和定點光.但無使用環境光.下載程式

  1. 按(/)鍵增加或減小聚光燈焦點指數
  2. 按</>鍵增加或減小聚光燈張角
  3. 按+/-鍵增加或減小多邊形表面的鏡面反射光聚焦度
  4. 按123鍵切換衛星的顏色與光線(紅綠藍)
  5. 按SPACE(空格)鍵啟用或禁用聚光燈
  6. 按UP/DOWN鍵衛星上下移動
  7. 按LEFT/RIGHT鍵衛星左右移動
  8. 按F1鍵打開幫助
  9. 按ESC鍵重置聚光燈參數

 

OpenGL之材質

OpenGL之材質

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} 材質的反射光顏色

設置多邊形材質的環境光和散射光,函式示例:

  1. glEnable(GL_LIGHTING); // 啟用光照
  2. float white[] = {0f,1.0f,1.0f,1.0f};// 白色
  3. glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, white);// 設置材質

OpenGL之聚光燈

OpenGL之聚光燈

聚光燈:在定點光源的基礎加上光線輻射方向,因此光線呈圓錐.

聚光燈參數 範圍 簡介
GL_SPOT_DIRECTION (x,y,z) 聚光燈指向/方向3D矢量

默認值:(0,0,-1)

GL_SPOT_EXPONENT 0-128 焦點/指數:光線從焦點到圓錐的邊界,光的強度不斷衰減,直到邊界消失.

指數越大焦點越小

指數越小焦點越大

GL_SPOT_CUTOFF 0.1-90 圓錐面與指向軸的角度(圓錐角),所以聚光燈的張角為圓錐角的2倍.

張角=圓錐角*2

聚光燈函式使用示例

  1. float Spot_Direction[] = {0f,0.0f,-1,0.0f}; // 聚光燈的指向
  2. float Light_Ambient[] = {0f,1.0f,1.0f,1.0f};// 環境光
  3. float Light_Diffuse[] = {0f,1.0f,1.0f,1.0f};// 散射光
  4. float Light_Position[] = {0f,0.0f,1.0f,1.0f};// 光源位置
  5. glEnable(GL_LIGHTING); // 啟用光照
  6. glLightfv(light,GL_AMBIENT, Light_Ambient);// 環境光分量
  7. glLightfv(light,GL_DIFFUSE, Light_Diffuse);// 散射光分量
  8. glLightfv(light,GL_POSITION,Light_Position);// 光源位置
  9. glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 0f); // 設置聚光燈的角度
  10. glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION, Spot_Direction); // 設置聚光燈的
  11. glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 0f); // 設置聚光燈的指數
  12. glEnable(GL_LIGHT0); // 啟用燈光

聚光燈程式示例,可分別設定張角與焦點.下載

  1. 按(/)鍵增加或減小聚光燈焦點指數
  2. 按[/]鍵增加或減小聚光燈張角
  3. 按+/-鍵增加或減小聚光燈亮度
  4. 按SPACE(空格)鍵啟用或禁用聚光燈
  5. 按UP/DOWN鍵正方體繞X軸旋轉
  6. 按LEFT/RIGHT鍵正方體繞Y軸旋轉
  7. 按F1鍵打開幫助
  8. 按ESC鍵重置聚光燈參數

 

OpenGL之法線

OpenGL之法線

法線:即垂直於其表面的單位矢量

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;

}

法線演示程式,旋轉正方體觀看光照的變化如上圖:下載

  1. 按+/-鍵增加或減小光照亮度
  2. 按SPACE(空格)鍵啟用或禁用光照
  3. 按UP/DOWN鍵正方體繞X軸旋轉
  4. 按LEFT/RIGHT鍵正方體繞Y軸旋轉
  5. 按Z+LEFT/Z+RIGHT鍵正方體繞Z軸旋轉
  6. 按F1鍵打開幫助
  7. 按ESC鍵重置模型旋轉角度

 

OpenGL之光照

OpenGL之光照

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 二次衰減值係數

光照亮度演示程式,定點光源並且全方位輻射.如上圖:下載

  1. 按+/-鍵增加或減小光照亮度
  2. 按SPACE(空格)鍵啟用或禁用光照
  3. 按UP/DOWN鍵正方形繞X軸旋轉
  4. 按LEFT/RIGHT鍵正方形繞Y軸旋轉
  5. 按Z+LEFT/Z+RIGHT鍵繞Z軸旋轉
  6. 按F1鍵打開幫助
  7. 按ESC鍵重置模型旋轉角度

 

OpenGL之漸變

OpenGL之漸變

OpenGL可對每個頂點設定不同的顏色,令模型產生漸變效果.

漸變函式示例:

  1. glShadeModel(GL_SMOOTH);啟用漸變
  2. glBegin(GL_QUADS);繪畫四邊形
  3. glColor3f(0f, 0.0f, 0.0f);glVertex3f(0.0f, 0.0f, 0.0f);黑色
  4. glColor3f(0f, 0.0f, 1.0f);glVertex3f(0.0f, 0.0f, 1.0f);藍色
  5. glColor3f(0f, 0.0f, 1.0f);glVertex3f(1.0f, 0.0f, 1.0f);紫紅色
  6. glColor3f(0f, 0.0f, 0.0f);glVertex3f(1.0f, 0.0f, 0.0f);紅色
  7. glEnd();
漸變 簡介
void glShadeModel (GLenum mode); 設定模型漸變模式(mode)

獲取當前使用的漸變模式

  1. GLenum mode;
  2. glGetIntegerv( GL_SHADE_MODEL,&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 應用第一個頂點的顏色

漸變演示程式:下載

  1. 按UP/DOWN鍵繞X軸旋轉
  2. 按LEFT/RIGHT鍵繞Y軸旋轉
  3. 按Z+LEFT/Z+RIGHT鍵繞Z軸旋轉
  4. 按F1鍵打開幫助
  5. 按ESC鍵模型旋轉角度重置
  6. 按SPACE鍵切換漸變模式

 

OpenGL之屬性

OpenGL之屬性

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 紋理狀態變量

 

OpenGL之變換

OpenGL之變換

渲染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

 

OpenGL之相機-旋轉和平移

OpenGL之相機-旋轉和平移

相機CAMERA固定在原點,通過旋轉和平移在世界坐標中移動所有模型

相機設定示例:

  1. glMatrixMode(GL_MODELVIEW);設定模型視圖矩陣
  2. glLoadIdentity()將觀察矩陣載入單位矩陣,相機位於原點(0,0,0), 朝向為負Z軸 (0,-90,0)
  3. glRotatef(AngleZ,0f,0.0f,1.0f); 橫搖/側滾角繞Z軸旋轉
  4. glRotatef(AngleY+90,0f,1.0f,0.0f); 偏航/航向角繞Y軸旋,補償90度
  5. glRotatef(AngleX,0f,0.0f,0.0f); 傾斜/俯仰角繞X軸旋轉
  6. glTranslatef(-posX,-posY,-posZ);移動模型坐標
視圖變換(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); 反方向移動模型坐標

相機移動程式演示:下載

  1. 按UP/DOWN鍵前後移動
  2. 按LEFT/RIGHT鍵左右旋轉,繞Y軸旋轉
  3. 小鍵盤4/6繞Z軸旋轉
  4. 小鍵盤8/2繞X軸旋轉
  5. 按F1鍵打開幫助
  6. 按ESC鍵相機返回原點

OpenGL之相機 gluLookAt()

OpenGL之相機 gluLookAt()

函式gluLookAt()允許你設定相機的位置、方向、朝上向量

相機設定示例:

  1. glMatrixMode(GL_MODELVIEW);設定模型視圖矩陣
  2. glLoadIdentity()將觀察矩陣載入單位矩陣,相機為於原點,朝向為負Z軸,上方向量為正Y軸.
  3. 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:相機的朝向

相機移動程式演示:下載

  1. 按UP/DOWN鍵前後移動
  2. 按LEFT/RIGHT鍵左右旋轉
  3. 按F1鍵打開幫助
  4. 按ESC鍵相機返回原點
  5. 按+鍵加大直線寬度
  6. 按-鍵減小直線寬度
  7. 按S鍵切換抗鋸齒功能
  8. 按.鍵切換四邊形點畫模式
  9. 按1鍵切換為黃色
  10. 按2鍵切換為綠色
  11. 按3鍵切換為紅色
  12. 按4鍵填充模式
  13. 按5鍵線框模式
  14. 按6鍵頂點模式

OpenGL之視口變換

OpenGL之視口變換

視口(Viewport)即渲染窗口大小,每當窗口大小發生改變都要使用glViewport()進行設定.視口變換在投影變換之後進行

視口設定 簡介
void glViewport(

GLint x, GLint y, 視口的左下角坐標

GLsizei width, 視口的寬度

GLsizei height); 視口的高度

視口的左下角坐標設為0,0

視口的寬高設為窗口大寬高

 

重設窗口大小,在WM_SIZE(窗口大小發生改變)消息下調用

  1. glViewport(0,0,width,height);將視口重置為新的尺寸
  2. glViewport(0,0,OpenGL_Width,OpenGL_Height);
  3. glMatrixMode(GL_PROJECTION); 設定投影矩陣
  4. glLoadIdentity();載入單位矩陣
  5. gluPerspective(0f, Width/Height,1.0f,1000.0f);
  6. glMatrixMode(GL_MODELVIEW);設定模型視圖矩陣
  7. glLoadIdentity();載入單位矩陣

 

OpenGL之矩陣

OpenGL之矩陣

OpenGL的變換運算均使用4×4矩陣進行.OpenGL使用堆棧保存矩陣.各種變換運算均針對棧頂進行操作.

矩陣 簡介
void glMatrixMode (GLenum mode); 設定當前矩陣堆棧
GL_MODELVIEW 模型視圖矩陣
GL_PROJECTION 投影矩陣
GL_COLOR 顏色矩陣
GL_TEXTURE 紋理矩陣

示例

  1. glMatrixMode(GL_MODELVIEW); 設定當前矩陣為模型視圖矩陣
  2. glLoadIdentity();載入單位矩陣,並且朝向負Z軸
  3. 進行其它變換
矩陣堆棧 簡介
void glPushMatrix(void); 複製當前矩陣(棧頂)並壓棧.
void glPopMatrix(void); 當前矩陣堆棧出棧並丟棄.

將當前坐標系統切換為新坐標系統,渲染完成後恢復原始坐標系統.示例:

  1. glMatrixMode(GL_MODELVIEW);設定模型視圖矩陣
  2. glPushMatrix();複製當前矩陣並壓棧.
  3. glLoadIdentity();載入單位矩陣
  4. glTranslatef(x,y,x);移動坐標系
  5. glPopMatrix();出棧,恢復源始坐標系統

不同矩陣堆棧有不同的深度,可通過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 屬性矩陣堆棧的深度

獲取模型視圖矩陣深度示例:

  1. GLint params[1];
  2. glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH,params);

OpenGL之旋轉

OpenGL之旋轉

旋轉glRotatef()使模形圍繞軸向量進行旋轉.先設定旋轉矩陣後繪畫模型.

旋轉 簡介
void glRotatef (

GLfloat angle,

GLfloat x,

GLfloat y,

GLfloat z);

單精度版本

angle:旋轉角度

逆時針旋轉:角度為正

順時針旋轉:角度為負

xyz:旋轉軸向量

void glRotated (

GLdouble angle,

GLdouble x,

GLdouble y,

GLdouble z);

雙精度版本

函式示例分別對XYZ進行旋轉:

  1. 設定模型視圖矩陣glMatrixMode(GL_MODELVIEW);
  2. 載入單位矩陣glLoadIdentity();
  3. 繞X軸旋轉glRotatef(angleX,1,0,0);
  4. 繞Y軸旋轉glRotatef(angleY,0,1,0);
  5. 繞Z軸旋轉glRotatef(angleZ,0,0,1);
  6. 繪畫立方體Draw();
旋轉示例 簡介
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度

旋轉演示程式如上圖:下載

  1. 按UP/DOWN鍵繞X軸旋轉
  2. 按LEFT/RIGHT鍵繞Y軸旋轉
  3. 按Z+LEFT/Z+RIGHT鍵繞Z軸旋轉
  4. 按F1鍵打開幫助
  5. 按ESC鍵模型旋轉角度重置

OpenGL之縮放

OpenGL之縮放

縮放glScalef()可在XYZ三軸指定不同縮放系數,放大或縮小模型或坐標系統的大小.

放大: 縮放系數大於(>1),若設為2放大一倍

縮小: 縮放系數為(1.0~0.0),若設為0.5側縮小一倍

函式示例:

  1. 設定模型視圖矩陣glMatrixMode(GL_MODELVIEW);
  2. 載入單位矩陣glLoadIdentity();
  3. 縮放glScalef(x,y,z);參數xyz為縮放系數,若縮放系數为1侧不缩放.縮放操作就是乘以縮放系數.
  4. 繪畫立方體Draw();
縮放 簡介
void glScalef(

GLfloat x,

GLfloat y,

GLfloat z);

單精度版本

對模型放大一倍:

1.    縮放glScalef(2,2,2)

2.    後再繪畫Draw()

void glScaled(

GLdouble x,

GLdouble y,

GLdouble z);

雙精度版本

縮放演示程式如上圖:下載

  1. 按UP/DOWN鍵對XYZ三軸進行模型縮放
  2. 按LEFT/RIGHT鍵繞Y軸旋轉
  3. 按F1鍵打開幫助
  4. 按ESC鍵模型縮放重置

OpenGL之平移

OpenGL之平移

平移Translate可將模型在3D世界中移動,先設定平移矩陣後繪畫模型:

函式示例:

  1. 設定模型視圖矩陣glMatrixMode(GL_MODELVIEW);
  2. 載入單位矩陣glLoadIdentity();
  3. 移動3D坐標glTranslatef(x,y,z);參數xyz為3D世界坐標的偏移量
  4. 繪畫立方體Draw();
平移 簡介
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);

雙精度版本

平移演示程式如上圖:下載

  1. 按LEFT/RIGHT鍵模型在X軸中移動
  2. 按UP/DOWN鍵模型在Z軸中移動
  3. 按F1鍵打開幫助
  4. 按ESC鍵模型歸位

OpenGL之投影變換

OpenGL之投影變換

投影變換是指設定視口面積和剪切平面,它在模型變換與視圖變換之後執行,用於確定那些多邊型模型位於視口之內. 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 剪切面的範圍

投影示例

  1. 選擇投影矩陣堆棧glMatrixMode(GL_PROJECTION);
  2. 載入單位矩陣glLoadIdentity()
  3. 設定透視投影或正交投影

投影函式

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();// 載入單位矩陣

}

投影演示程式如上圖:下載

  1. 按P鍵在正交投影與透視投影之間切換
  2. 按S鍵切換抗鋸齒功能
  3. 按1鍵切換為黃色
  4. 按2鍵切換為綠色
  5. 按3鍵切換為紅色
  6. 按F1鍵打開幫助
  7. 按ESC鍵旋轉角度清零
  8. 按+鍵加大直線寬度
  9. 按-鍵減小直線寬度