這個系列教程 是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就會用到混合。另外一種方法是按下面的流程去作:函數
全屏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();