天天看點

HGE 系列教材(5) --- 輸入、聲音和渲染

建議讀者對應 hge 的官方的例子:tutorial 02 - using input, sound and rendering 來閱讀本文

渲染:

在 hge 中,四邊形是一種圖元,對應了結構體 hgequad,另外還有三角形圖元,對應 hgetriple,為了渲染,我們現在需要使用 hgequad 結構體,這個結構體如下:

struct hgequad

{

hgevertex v[4];   // 頂點描述了這個四邊形

htexture   tex;   // 紋理的句柄或者為0

int        blend;   // 混合模式(blending mode)

};

hge 中圖元對應的結構體總含有這3個部分:頂點,紋理句柄,混合模式

struct hgevertex

    float x, y;   // 螢幕的 x,y 坐标

    float z;   // z-order,範圍 [0, 1]

    dword col;   // 頂點的顔色

    float tx, ty;   // 紋理的 x,y 坐标(指派前需要規格化坐标間隔,使得 tx,ty 取值範圍在[0,1])

規格化坐标間隔在後面的例子中會談到

1. 顔色的表示:

顔色使用32位表示,從左開始,8位為 alpha 通道,8位紅色,8位綠色,8位藍色

對于後24位,如果全部為0,表示黑色,如果全部為1,表示白色

2. 定義顔色的運算:

我們把顔色看成一個四維向量,即 alpha 通道,紅色,綠色,藍色這四個分量

    <1> 顔色是可以相乘的

    顔色的相乘是對應的四個分量分别相乘的結果,即:alpha 通道的值與 alpha 通道的值相乘,紅色的值與紅色的值相乘,綠色的值與綠色的值相乘,藍色的值與藍色的值相乘。

    <2> 顔色是可以相加的

    同上,對應分量相加。

顔色的每個分量使用浮點數表示,範圍是[0-1],相加操作可能導緻溢出,一種處理的方式就是,如果溢出,則設定值為1。

3. 混合模式:

1)blend_coloradd

表示頂點的顔色與紋理的紋元(texel)顔色相加,這使得紋理變亮,可見頂點顔色為 0x00000000 将不造成任何影響。

2)blend_colormul

表示頂點的顔色與紋理的紋元顔色相乘,這使得紋理變暗,可見頂點顔色為 0xffffffff 将不造成任何影響。

注意:必須在1),2)中做一個選擇,且隻能選擇1),2)中的一個。處理的對象是紋理顔色和頂點顔色。

這裡有一個技巧:

如果我們需要在程式中顯示一個氣球,這個氣球的顔色不斷變化,這時候我們并不需要準備多張不同顔色的氣球紋理,而隻需要一張白色的氣球紋理,設定 blend 為 blend_colormul,白色的r,g,b值被表示成 1.0,也就是說,紋理顔色和頂點顔色相乘的結果是頂點的顔色,那麼就可以通過修改頂點顔色,得到任意顔色的氣球了。

3)blend_alphablend

渲染時,将對象的像素顔色(而非頂點的顔色)與目前螢幕的對應像素顔色進行 alpha 混合。alpha 混合使用到 alpha 通道,對于兩個像素顔色進行如下操作,得到一個顔色:

r(c)=alpha*r(b)+(1-alpha)*r(a)

g(c)=alpha*g(b)+(1-alpha)*g(a)

b(c)=alpha*b(b)+(1-alpha)*b(a)

這裡的blend_alphablend使用的是對象像素的顔色的 alpha 通道。可見如果對象像素顔色 alpha 通道為 0,那麼結果就是隻有目前螢幕的像素顔色,也就是常常說的 100% 透明,是以,我們可以了解 alpha 混合就是一個是圖像透明的操作,0 表示完全透明,255 表示完全不透明。

4)blend_alphaadd

渲染時,将對象的像素顔色與目前螢幕的對應像素顔色相加,結果是有了變亮的效果。

注意:這裡的3),4)必選其一,且隻能選其一。處理的對象是對象像素顔色和螢幕像素顔色。

5)blend_zwrite

渲染時,寫像素的 z-order 到 z-buffer

6)blend_nozwrite

渲染時,不寫像素的 z-order 到 z-buffer

這裡一樣是二者選一

設定舉例:

quad.blend=blend_alphaadd | blend_colormul | blend_zwrite;   // quad 為 hgequad 變量

4. hge 渲染

1)定義和初始化 hgequad 結構體:

hgequad quad;   // 定義四邊形

2)初始化 hgequad 變量:

// 設定混合模式

quad.blend=blend_alphaadd | blend_colormul | blend_zwrite;

// 加載紋理

quad.tex = phge->texture_load("particles.png");

注意,讀取硬碟上資源的時候,可能會失敗,是以通常都需要檢查,例如:

if (!quad.tex)

    messagebox(null, "load particles.png", "error", 0);

}

// 初始化頂點

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

    // 設定頂點的 z 坐标

    quad.v[i].z=0.5f;

    // 設定頂點的顔色,顔色的格式為 0xaarrggbb

    quad.v[i].col=0xffffa000;

// 這裡假定載入的紋理大小為 128*128,現在截取由點(96,64),(128,64),(128,96),(96,96)這四個點圍成的圖形。

quad.v[0].tx=96.0/128.0; quad.v[0].ty=64.0/128.0;   // 規格化坐标間隔

quad.v[1].tx=128.0/128.0; quad.v[1].ty=64.0/128.0;

quad.v[2].tx=128.0/128.0; quad.v[2].ty=96.0/128.0;

quad.v[3].tx=96.0/128.0; quad.v[3].ty=96.0/128.0;

注意,對于 hgequad 結構體,頂點 quad.v[0] 表示左上那個點,quad.v[1] 表示右上的點,quad.v[2] 表示右下的點,quad.v[3] 表示左下的點。

// 設定 hgequad 在螢幕中的位置

float x=100.0f, y=100.0f;

quad.v[0].x=x-16; quad.v[0].y=y-16;

quad.v[1].x=x+16; quad.v[1].y=y-16;

quad.v[2].x=x+16; quad.v[2].y=y+16;

quad.v[3].x=x-16; quad.v[3].y=y+16;

3)設定渲染函數(render function):

system_setstate(hge_renderfunc,renderfunc);

renderfunc 原型和幀函數一樣:

bool renderfunc();

4)編寫 renderfunc 函數:

bool renderfunc()

   phge->gfx_beginscene();   // 在如何渲染之前,必須調用這個函數

   phge->gfx_clear(0);   // 清屏,使用黑色,即顔色為 0

   phge->gfx_renderquad(&quad);   // 渲染

   phge->gfx_endscene();   // 結束渲染,并且更新視窗

   return false;   // 必須傳回 false

補充:load 函數是和 free 函數成對出現的,即在硬碟上加載了資源之後,需要 free 它們,例如:

quad.tex = phge->texture_load("particles");

// ...

phge->texture_free(quad.tex);

音效:

使用音效是很簡單的

1. 載入音效:

heffect heffect = phge->effect_load("sound.mp3");

2. 播放:

phge->effect_playex(heffect);

或者 phge->effect_play(heffect);

1)effect_play 函數隻接受一個參數就是音效的句柄 heffect xx;

2)effect_playex 函數較為強大,一共有四個參數:

hchannel effect_playex(

                       heffect effect,   // 音效的句柄

                       int volume = 100,   // 音量,100為最大,範圍是[0, 100]

                       int pan = 0,   // 範圍是[-100, 100],-100表示隻使用左聲道,100表示隻使用右聲道

                       float pitch = 1.0,   // 播放速度,1.0 表示正常速度,值越大播放速度越快,值越小播放越慢。這個值要大于0才有效(不可以等于0)

                       bool loop = false   // 是否循環播放,false表示不循環

                       );

輸入:

僅僅需要調用函數 phge->input_getkeystate(hgek_xxx); 來判斷輸入,應該在幀函數中調用它,例如:

bool framefunc()

    if (phge->input_getkeystate(hgek_lbuttom))

       // ...

    if (phge->input_getkeystate(hgek_up))