OpenGL & GLSL 教程(四)

這個系列教程 是https://github.com/mattdesl/lwjgl-basics/wiki的系列翻譯。git

2d和3d的編程的一個基本特性就是能夠渲染多個sprite成爲一個紋理( render-to-texture)。例如,咱們有一個由兩個sprite組成的滑動UI,咱們使它的不透明度漸變。當以50%的不透明度渲染它們,重合的部分會出現一些咱們不想要的混合現象:
請輸入圖片描述github

左邊的圖形是100%的不透明度,中間圖像的問題是每一個精靈都是50%的不透明度重合部分咱們能看到下面的sprite。而右邊是咱們想要的結果,先把全部sprite處理爲一個texture( render-to-texture),而後再以50%的透明度渲染這個texture。編程

請輸入圖片描述
另外一種利用 render-to-texture的做用是處理特效。咱們先把全部的sprite處理爲一個爲屏幕大小的texture,而後在用shader去處理它,渲染到屏幕。
Note
在咱們繼續以前,咱們必須指出,利用FBOs技術,特別是在每個幀都用到的時候。可能致使性能的降低,由於它會進行屢次轉換狀態,批量刷新, 緩存清理, 和 着色器的更新。因此在使用以前咱們仔細考慮。segmentfault

Frame Buffer Objects(FBO)
在OpenGL中咱們爲了render-to-texture,咱們必須創建一個FBO。咱們將利用Framebuffer類 使過程更簡單些。
首先咱們建立一個frame buffer 對象:緩存

try {
    fbo = new FrameBuffer(width, height, Texture.NEAREST);
} catch (LWJGLException e) {
    ... if the FBO could not be created ...
}

爲了最大的兼容性,咱們應該把長和寬設置成2的冪,由於咱們會把FrameBuffer轉換爲Texture。這樣能夠避免一些咱們以前教程裏說的硬件限制。
你能夠利用Framebuffer.isSupported()去查看硬件是否支持幀緩存。若是返回是false,建立Framebuffer的時候會報錯。只有特別老的版本纔會出現這種狀況,並且多數這時候都不支持shader。這裏給一個統計大約93%的驅動支持GL_EXT_framebuffer_object。當用戶不支持的時候你最好建議他們去更新顯卡或者驅動。
下面是一段利用FBO的僞代碼:app

//make the FBO the current buffer
fbo.begin()

//... clear the FBO color with transparent black ...
glClearColor(0f, 0f, 0f, 0f); //transparent black
glClear(GL_COLOR_BUFFER_BIT); //clear the color buffer

//since the FBO may not be the same size as the display, 
//we need to give the SpriteBatch our new screen dimensions
batch.resize(fbo.getWidth(), fbo.getHeight());

//render some sprites 
batch.begin();

//draw our track and thumb button
batch.draw(track, ...);
batch.draw(slider, ...);

batch.end(); //flushes data to GL

//now we can unbind the FBO, returning rendering back to the default back buffer (the Display)
fbo.end();

//reset the batch back to the Display width/height
batch.resize(Display.getWidth(), Display.getHeight());

//now we are rendering to the back buffer (Display) again
batch.begin();

//draw our offscreen FBO texture to the screen with the given alpha
batch.setColor(1f, 1f, 1f, alpha);
batch.draw(fbo, 0, 0);

batch.end();

若是如今你用下面的sprite sheet去測試:
請輸入圖片描述
當50%不透明度的時候會以下圖:
請輸入圖片描述ide

混合的苦惱
不透明的背景有些難看,可是隻要咱們以去掉它,咱們在alpha通道就會丟掉一些信息:
請輸入圖片描述
這是由於咱們進行了兩次混合,當咱們把sprite放入FBO時咱們和背景進行了一次混合,當咱們把sprite渲染到屏幕的時候咱們又進行了一些混合,第二次混合使sprite比實際看起來更加透明瞭。
一種解決方案是使用FBO以前調用glDisable(GL_BLEND),或者使用一個不和背景混合的函數。但不幸的是,咱們在一個sprite之上渲染另外一個半透明的sprite就會用到混合。另外一種方法是按下面的流程去作:函數

  1. 清理 FBO 爲 black (0, 0, 0, 0).
  2. 設置 RGB 混合混合函數爲 GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA and
    設置 Alpha 混合函數爲GLONE, GL_ONE.
  3. 以100%的不透明度渲染sprites到FBO.
  4. 結束批處理,解除FBO的綁定.
  5. 使"pre-multipled alpha" blending, GL_ONE,
    GL_ONE_MINUS_SRC_ALPHA.
  6. 用batch.setColor(alpha, alpha, alpha, alpha) to 調整不透明度.
  7. 渲染FBO.
    結果以下圖:
    請輸入圖片描述

全屏FBOs 和後期處理:post

像咱們以前提到的,一些老的驅動不知道支持非2次冪的長和寬。當咱們處理全屏FBO時,這是很是頭疼的。由於不多有真正的屏幕的的長寬是2的次冪。
解決方法是利用TextureRegion 去渲染2的次冪的Texture的一部分。因爲GL和TextureRegion 的座標系不一樣,咱們要作一下轉換,代碼以下:性能

TextureRegion fboRegion; //the region of our POT frame buffer
FrameBuffer fbo; //our POT frame buffer with a color texture attached

...
if (Texture.isNPOTSupported()) {
fbo = new FrameBuffer(width, height);
fboRegion = new TextureRegion(fbo.getTexture());
} else {
int texWidth = Texture.toPowerOfTwo(width);
int texHeight = Texture.toPowerOfTwo(height);
fbo = new FrameBuffer(texWidth, texHeight);
fboRegion = new TextureRegion(fbo.getTexture(), 0, texHeight-height, width, height);
}
fboRegion.flip(false, true);
...
後期處理的僞代碼,以下:

//make offscreen texture active
fbo.begin(); 

//standard shader
batch.setShader(DEFAULT_SHADER);

//if FBO size == Display size, we can omit this
batch.resize(fbo.getWidth(), fbo.getHeight());

batch.begin();
... render all sprites here ...
batch.end();

//unbind FBO
fbo.end();

//now apply post-processing
batch.setShader(POST_PROCESS_SCENE);

//resize to display since we are no longer rendering to a FBO texture
batch.resize(Display.getWidth(), Display.getHeight());

//draw screen, will be affected by shader
batch.begin();
batch.draw(fboRegion, 0, 0);
batch.end();
相關文章
相關標籤/搜索