这个系列教程 是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();