Android遊戲開發:遊戲架構的搭建(3)

來源:互聯網
上載者:User

5. 映像模組(Graphics)

  最後一個模組是映像操作模組,用來繪製映像到螢幕上。不過要想高效能的繪製映像,就不得不瞭解一些基本的映像編程知識。讓我們從繪製2D映像開始,首先要瞭解的一個問題是:映像究竟是如何繪製到螢幕的?答案相當複雜,我們不需要知道所有的細節。

光柵、像素和幀緩衝(Framebuffers)

  現在的顯示器都是基於光柵的,光柵是一個兩維度格子組成,也就是像素格。光柵格子的長寬,我們一般用像素來表示。如果仔細觀察顯示器(或者用放大鏡),我們就可以發現顯示器上面有一個一個的格子,這就是像素格或者光柵格。每個像素的位置可以用座標表示,於是引入了二維座標系統,這也意味著座標值是整數。顯示器源源不斷地收到從圖形處理器傳過來的映像流,解碼每個像素的顏色(程式或者作業系統設定),然後繪製到螢幕上。每秒鐘顯示器會進行多次重新整理,重新整理頻率單位是Hz,比如LCD顯示器主流重新整理率是85Hz。

  圖形處理器需要從一個特殊的儲存地區擷取像素資訊以便顯示在顯示器上,這個地區就叫做視頻記憶體區,或者叫VRAM。這個地區一般稱作框架緩衝區(framebuffer)。因此一個完整的螢幕圖形叫做一個幀。對於每個顯示器柵格中的像素,在框架緩衝區都有一個對應的記憶體位址。當我們需要改變螢幕顯示內容時,我們只需要簡單地改變框架緩衝區中的內容即可。

  是顯示器柵格和框架緩衝區的簡單:

垂直同步和雙緩衝

  普通的繪圖方法,當要繪製的對象太複雜,尤其是含有位元影像時,這時的畫面會顯示的很慢,對於運動的畫面,會給人“卡”住了的感覺,有時候還會導致畫面閃爍。於是我們採用雙緩衝技術(採用兩個framebuffer)。 雙緩衝的原理可以這樣形象的理解:把電腦螢幕看作一塊黑板。首先我們在記憶體環境中建立一個“虛擬“的黑板,然後在這塊黑板上繪製複雜的圖形,等圖形全部繪製完畢的時候,再一次性的把記憶體中繪製好的圖形“拷貝”到另一塊黑板(螢幕)上。採取這種方法可以提高繪圖速度,極大的改善繪圖效果。下面是原理圖:

  要知道什麼是垂直同步,必須要先明白顯示器的工作原理。顯示器上的所有映像都是一線一線的掃描上去的,無論是隔行掃描還是漸進式掃描,顯示器,都有2種同步參數——水平同步和垂直同步。水平同步訊號決定了CRT畫出一條橫越螢幕線的時間,垂直同步訊號決定了CRT從螢幕頂部畫到底部,再返回原始位置的時間,而恰恰是垂直同步代表著CRT顯示器的重新整理率水平!

  關閉垂直同步:我們平時運行作業系統一般螢幕重新整理率一般都是在85Hz上下,此時顯卡就會每按照85Hz的頻率時間來發送一個垂直同步訊號,訊號和訊號的時間間隔是85的解析度所寫一屏映像時間。

  開啟垂直同步:在遊戲中,或許強勁的顯卡迅速的繪製完一屏的映像,但是沒有垂直同步訊號的到達,顯卡無法繪製下一屏,只有等85單位的訊號到達,才可以繪製。這樣fps自然要受到作業系統重新整理率運行值的制約。也就是說,當然開啟後如果你的遊戲畫面FPS數能達到或超過你顯示器的重新整理率,這時你的遊戲畫面FPS數被限制為你顯示器的重新整理率。如果達不到會出現不同程度的跳幀現象,FPS與重新整理率差距越大跳幀越嚴重。一般對於高效能的顯卡建議打卡,遊戲畫面會更好!開啟後能防止遊戲畫面高速移動時畫面撕裂現象,比如實況足球等。

  關閉垂直同步,那麼遊戲中作完一屏畫面,顯卡和顯示器無需等待垂直同步訊號,就可以開始下一屏映像的繪製,自然可以完全發揮顯卡的實力。

  但是,不要忘記,正是因為垂直同步的存在,才能使得遊戲進程和顯示器重新整理率同步,使得畫面平滑,使得畫面穩定。取消了垂直同步訊號,固然可以換來更快的速度,但是在映像的連續性上,效能勢必打折扣。這也是關閉垂直同步後發現畫面不連續的理論原因!

映像格式

  比較流行的兩個圖形格式是JPEG和 PNG。JPEG是有損壓縮格式,PNG是無損壓縮格式,因此PNG格式可以百分百重現原始的映像。有損壓縮格式通常佔用少的磁碟空間。我們採用何總壓縮格式取決於我們的磁碟空間。和音頻類似,當我們載入到記憶體中時,我們需要完全地解壓一個映像。因此,即使你的壓縮映像在磁碟上只有20K,在RAM中你依然需要width×height ×color depth的儲存空間。

映像疊加

  假定有一個我們可以渲染的框架緩衝區(framebuffer),同時有幾個載入到RAM中的圖片,我們笑需要把RAM中的圖片逐次放入到框架緩衝區,比如一個背景圖片和一個前景圖片:

  這個過程就叫做映像的合成和疊加,我們需要把不同的圖片合成一個最終顯示的圖片。繪製圖片的此項很重要,因為上面的圖片總會覆蓋下面的圖片。

  上面映像合成出現了問題:第二張圖片的白色背景覆蓋了第一張背景圖片。我們怎樣把第二張圖的白色背景消去呢?這就需要alpha混合(alpha blending)。alpha混合是一種把源點的顏色值和目標點的顏色值按照一定的演算法進行運算,得到一種透明的效果。

  下面是最終合成映像的RGB值,公式如下

  red = src.red * src.alpha + dst.red * (1 – src.alpha)  blue = src.green * src.alpha + dst.green * (1 – src.alpha)  green = src.blue * src.alpha + dst.blue * (1 – src.alpha)  

  src和dst分別是我們需要混合的源映像和靶心圖表像(源映像相當於人物,靶心圖表像相當於背景)。下面是一個例子。

  src = (1, 0.5, 0.5), src.alpha = 0.5, dst = (0, 1, 0)  red = 1 * 0.5 + 0 * (1 – 0.5) = 0.5  blue = 0.5 * 0.5 + 1 * (1 – 0.5) = 0.75  red = 0.5 * 0.5 + 0 * (1 – 0.5) = 0.25

  效果如所示

  上述公式用了兩次乘法,乘法消耗的時間多,為了提高運算速度,可以進行最佳化。如

    red = (src.red- dst.red) * src.alpha + dst.red

  Alpha是一個浮點數,我們可以轉換成整數運算,因為一種顏色最多佔8Bit,所以Alpha值最多是256,於是我們把Alpha的值乘以256,然後運算的時候再除以256,就得到下面的公式:

    red = (src.red- dst.red) * src.alpha /256+ dst.red

  這裡,Alpha是一個0到256的數值。

  具體到這個例子,我們只需要把源檔案的白色像素的alpha值設為0即可。最終效果如:


映像模組的介面代碼

  通過以上介紹,我們可以開始設計我們的映像模組的介面。問下需要實現如下功能:

  • 從磁碟載入圖片到記憶體中,為以後繪製到螢幕做準備。
  • 用特定顏色清除framebuffer
  • 用指定顏色在framebuffer指定位置繪製像素。
  • 在framebuffer上繪製線條和矩形。
  • 繪製上面記憶體中的圖片到framebuffer,能夠整個繪製和部分繪製,alpha混合繪製。
  • 得到framebuffer的長寬。

  這裡用兩個介面來實現:Graphics和 Pixmap,下面是Graphics介面:

package com.badlogic.androidgames.framework;

public interface Graphics {

  public static enum PixmapFormat {

    ARGB8888, ARGB4444, RGB565

  }

  public Pixmap newPixmap(String fileName, PixmapFormat format);

  public void clear(int color);

  public void drawPixel(int x, int y, int color);

  public void drawLine(int x, int y, int x2, int y2, int color);

  public void drawRect(int x, int y, int width, int height, int color);

  public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, int srcWidth, int srcHeight);

  public void drawPixmap(Pixmap pixmap, int x, int y);

  public int getWidth();

  public int getHeight();

}

  枚舉 PixmapFormat儲存了該遊戲支援的像素的顏色值(包括透明度)。比如ARGB8888,A表示透明度,R表示紅色,G表示綠色,B表示藍色,他們非別用8位來表示,就是各有256種狀態。            接下來看下介面的方法:

  • Graphics.newPixmap()方法載入指定格式的圖片。
  • Graphics.clear()方法用特定顏色清除framebuffer。
  • Graphics.drawPixel()方法在framebuffer中指定位置繪製給定顏色的像素。
  • Graphics.drawLine()和 Graphics.drawRect()方法在framebuffer繪製線條和矩形。
  • Graphics.drawPixmap()方法繪製映像的到framebuffer。(x, y) 座標指定了framebuffer繪製的起始位置,參數 srcX和srcY指定了圖片被繪製部分的起始位置。srcWidth和srcHeight制定了繪製的寬度和高度。
  • Graphics.getWidth()和Graphics.getHeight()方法返回framebuffer的寬度和高度。
   接下來我們看一下Pixmap介面:
//Pixmap介面

package com.badlogic.androidgames.framework;

import com.badlogic.androidgames.framework.Graphics.PixmapFormat;

public interface Pixmap {

  public int getWidth();

  public int getHeight();

  public PixmapFormat getFormat();

  public void dispose();

}
  • Pixmap.getWidth()和Pixmap.getHeight()方法返回映像的寬度和高度。
  • Pixmap.getFormat()返回圖片的格式。
  • Pixmap.dispose()方法。Pixmap執行個體使用記憶體資源和其他潛在的系統資源,如果我們不在需要它,我們需要回收資源,這也該方法的作用。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.