一、 FBO 相关
1.1 什么是 FBO
FBO(Frame Buffer Object)即帧缓冲区对象,用于离屏渲染缓冲, 实际上是一个可添加缓冲区的容器,
相对于其它同类技术,如数据拷贝或交换缓冲区等,使用FBO技术会更高效并且更容易实现。而且FBO不受窗口大小限制。FBO可以包含许多颜色缓冲区,可以同时从一个片元着色器写入
重要:
FBO是一个容器,自身不能用于渲染,需要与一些可渲染的缓冲区绑定在一起,像纹理或者渲染缓冲区。
所以可以给他添加添加纹理(Texture )或渲染缓冲区对象(RBO)
它仅且提供了 3 个附着(Attachment):颜色附着、深度附着、模板附着。
渲染方式
渲染到缓冲区(Render)- 深度测试和模板测试(3D)
渲染到纹理(Texture)- 图像渲染
1.2 为什么要使用 FBO
默认情况下:OpenGL ES 通过绘制到窗口系统提供的帧缓冲区,然后将帧缓冲区的对应区域复制到纹理来实现渲染到纹理(但是此方法只有在纹理尺寸小于或等于帧缓冲区尺寸才有效)
另一种方式是通过使用连接到纹理的 pbuffer 来实现渲染到纹理,但是与上下文和窗口系统提供的可绘制表面切换开销也很大。因此,引入了帧缓冲区对象 FBO 来解决这个问题。
OpenGL ES 开发中,经常使用 GLSurfaceView 将绘制结果显示到屏幕上(或者自己创建GL环境,绘制在 SurfaceView或者 TextureView上)。
1.提高渲染效率(后台绘制没有展示到窗口上)
2.避免闪屏
3.可以很方便的实现纹理共享等。
但是,也有许多场景不需要渲染到屏幕上,如利用 GPU 在后台完成一些图像转换、缩放等耗时操作,这个时候利用 FBO 可以方便实现类似需求。
1.3 使用FBO 的场景
使用 FBO 可以让渲染操作不用再渲染到屏幕上,而是渲染到离屏 Buffer 中,然后使用 glReadPixels 或者 HardwareBuffer 将渲染后的图像数据读出来,从而实现在后台利用 GPU 完成对图像的处理。
Camera 美颜、滤镜、贴纸等功能。
二、API了解
2.1 生成纹理 ,第一个参数为需要的纹理数,第二个参数为存储获得的纹理ID的数组,第三个参数为数组的起始位置。
//创建一个 2D 纹理用于连接 FBO 的颜色附着
GLES20.glGenTextures(int size,int[] textures,int start)
其C为void glGenTextures(GLsizei n,GLuint * textures);
2.2 生成 FrameBuffer
GLES20.glGenFrameBuffers
2.3 生成RenderBuffers
GLES20.glGenRenderBuffers
2.4 设置FBO分配内存大小
//2d图形,图层层级0,rgba,宽,高,0,rgba,无符号字节类型,null只分配大小
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 720, 1280, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
2.5 绑定、设置纹理参数 (2种纹理类型)
(1)----- 设置纹理参数 扩展纹理 --------
---------- 必须首先绑定纹理, 让GPU知道给谁设置 ------------
GLES20.glGenTextures(1, texture, 0);
//
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture[0]);
//设置 纹理参数
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_LINEAR);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
(2)---- 设置纹理参数(2D 纹理的设置) ----
---------- 必须首先绑定纹理, 让GPU知道给谁设置 ------------
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
//设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
//设置放大过滤为使用纹理中坐标最接近的若干个颜色,通过加权平均算法得到需要绘制的像素颜色
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
//设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
//设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
2.6 使用纹理
GLES20.glActiveTexture
选择当前活跃的纹理单元。默认情况下当前活跃的纹理单元为GLES20.GL_TEXTURE0
选择当前活跃的纹理单元。默认情况下当前活跃的纹理单元为GLES20.GL_TEXTURE0
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//绑定2D纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mtextureID);
//将纹理设置给Shader
GLES20.glUniform1i(mHTexture,1);
三、 FBO的创建 并绑定 纹理
Render Buffer Object(RBO): 即为渲染缓冲对象,分为color buffer(颜色)、depth buffer(深度)、stencil buffer(模板)。
在使用FBO做离屏渲染时,可以只绑定纹理,也可以只绑定Render Buffer,也可以都绑定或者绑定多个,视使用场景而定。如只是对一个图像做变色处理等,只绑定纹理即可。如果需要往一个图像上增加3D的模型和贴纸,则一定还要绑定depth Render Buffer。
同Texture使用一样,FrameBuffer使用也需要调用GLES20.glGenFrameBuffers
生成FrameBuffer,然后在需要使用的时候调用GLES20.glBindFrameBuffer
。
3.1 FrameBuffer的创建,Texture的创建及参数设置,代码如下:
int[] fFrame= new int[1];
int[] textures= new int[1];
//---- 创建 FBO --------------------------------
GLES20.glGenFramebuffers(1, fFrame, 0);
//---- 创建一个 2D 纹理用于连接 FBO 的颜色附着 -----
GLES20.glGenTextures(1, textures, start);
//---- 绑定 纹理 -----------
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0,gl_format, width, height,
0, gl_format, GLES20.GL_UNSIGNED_BYTE, null);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);
//-------- 绑定FrameBuffer -----------------
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fFrame[0]);
//---------- 将纹理连接到 FBO 附着 -------
// --------- 为FrameBuffer 绑定 Texture 来存储颜色,-----------
// --------- 绑定FrameBuffer后的绘制会绘制到textures 上了 -------
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, textures[1], 0);
//--------- 分配内存大小 ---------------------------
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width, m_RenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
//---------- 检查 FBO 的完整性状态 ------------------
if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE) {
LOGCATE("FBOSample::CreateFrameBufferObj glCheckFramebufferStatus status !=
GL_FRAMEBUFFER_COMPLETE");
return false;
}
//----- 操作完成后,解绑纹理 和 FBO----------
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, GL_NONE);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);
3.2 Frame Buffer与 Render Buffer绑定
当绑定纹理并不能满足我们的要求,当我们需要深度的时候,还需要绑定Render Buffer。在绑定纹理的基础上再绑定RenderBuffer 。首先使用前还是先生成RenderBuffer,并为RenderBuffer建立数据存储的格式和渲染对象的尺寸:
//生成Render Buffer
GLES20.glGenRenderbuffers(1,fRender,0);
//绑定Render Buffer
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER,fRender[0]);
//设置为深度的Render Buffer,并传入大小
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,GLES20.GL_DEPTH_COMPONENT16,
width, height);
//为FrameBuffer挂载fRender[0]来存储深度
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT,
GLES20.GL_RENDERBUFFER, fRender[0]);
//解绑Render Buffer
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER,0);
使用渲染时,增加深度绑定和解绑:
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, textureId, 0);
//为FrameBuffer挂载fRender[0]来存储深度
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT,
GLES20.GL_RENDERBUFFER, fRender[0]);
GLES20.glViewport(0,0,width,height);
mFilter.draw();
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);
//解绑Render Buffer
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER,0);
释放
//删除Render Buffer
GLES20.glDeleteRenderbuffers(1, fRender, 0);
//删除Frame Buffer
GLES20.glDeleteFramebuffers(1, fFrame, 0);
//删除纹理
GLES20.glDeleteTextures(1, fTexture, 0);
三、 FBO 的使用流程 和 普通渲染区别
// 这里我们编译连接了 2 个 program ,一个用作离屏渲染的 m_FboProgramObj,一个用于普通渲染的
// 绑定 FBO
glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
// 选定离屏渲染的 Program,绑定 VAO 和图像纹理,进行绘制(离屏渲染)
glUseProgram(m_FboProgramObj);
glBindVertexArray(m_VaoIds[1]);
glActiveTexture(GL_TEXTURE0);
// 绑定图像纹理
glBindTexture(GL_TEXTURE_2D, m_FboTextureId );
glUniform1i(m_FboSamplerLoc, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
// 解绑 FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 完成离屏渲染后,结果图数据便保存在我们之前连接到 FBO 的纹理 m_FboTextureId 。
// 我们再拿 FBO 纹理 m_FboTextureId 做一次普通渲染便可将之前离屏渲染的结果绘制到屏幕上。
m_ProgramObj
//选定另外一个着色器程序,以 m_FboTextureId 纹理作为输入进行"普通渲染",
//就直接渲染到屏幕上了
glUseProgram(m_ProgramObj);
glBindVertexArray(m_VaoIds[0]);
glActiveTexture(GL_TEXTURE0);
//绑定 FBO 纹理
glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
glUniform1i(m_SamplerLoc, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
glBindVertexArray(GL_NONE);
3 Demo
demo流程
FBO还需相关对象来辅佐,Shader;原纹理texture;输出纹理texture(fbo纹理)
2.1 编写Shader
2.2 GL环境, 使用 GLSurfaceView 的或者 自己创建
2.3 初始化 GL环境成功后,创建GL Program。获取可用Texture,
在 onSurfaceCreated 设置渲染参数
在 onSurfaceChanged 中设置ViewPor
2.4 在绘制方法 onDraw 中
清屏
启用必要的属性,useProgram,绑定纹理,传入参数(顶点坐标、纹理坐标、变换矩阵等)
2.5 继续下一帧数据
requestRender
FBO 如何与纹理结合使用
在GL线程创建成功后,在GL线程中生成纹理,并设置纹理参数,然后在渲染时启用纹理,绑定纹理,并将纹理传入Shader(告诉Shader,采样器是哪个
3.1 如何创建 FBO
private int createFbo(int width, int height) {
int[] texture = new int[1];
int[] fbo = new int[1];
//创建FBO
GLES20.glGenFramebuffers(1, fbo, 0);
//创建 texture
GLES20.glGenTextures(1, texture, 0);
mFbo = fbo[0];
mDstTexture = texture[0];
//绑定 2D纹理 mDstTexture
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mDstTexture);
//申请内存
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
//设置 texture param
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
//绑定 FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFbo);
// FBO - texture
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mDstTexture, 0);
//检测 fbo 状态
int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
Log.e(LOG_TAG, "Failed to create framebuffer!!!");
}
return 0;
}
渲染器
public class TextureRenderer {
private static final String LOG_TAG = "TextureRenderer";
private final String mVertexShader =
"attribute vec4 position;\n" +
"attribute vec2 inputTextureCoordinate;\n" +
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
"varying vec2 textureCoordinate;\n" +
"void main()\n" +
"{\n" +
"gl_Position = uMVPMatrix * position;\n" +
"vec4 tex4 = vec4(inputTextureCoordinate.xy, 1.0, 1.0);\n" +
"textureCoordinate = (uSTMatrix * tex4).xy;\n" +
"}";
private final String mFragmentShaderOes =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" +
"varying vec2 textureCoordinate;\n" +
"uniform samplerExternalOES s_texture;\n" +
"void main() {\n" +
"gl_FragColor = texture2D(s_texture, textureCoordinate);\n" +
"}";
private final String mFragmentShaderRgba =
"precision mediump float;\n" +
"varying vec2 textureCoordinate;\n" +
"uniform sampler2D s_texture;\n" +
"void main() {\n" +
"gl_FragColor = texture2D(s_texture, textureCoordinate);\n" +
"}";
private FloatBuffer vertexBuffer, textureVerticesBuffer;
private ShortBuffer drawListBuffer;
private final int mProgram;
private boolean mOesTexture = true;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 2;
static float squareVertices[] = {-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
// in counterclockwise order:
float textureVertices[] = {0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,};
private short drawOrder[] = {0, 1, 2, 0, 2, 3}; // order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
// uniforms
private int muMVPMatrixHandle;
private int muSTMatrixHandle;
private float[] mMVPMatrix = new float[16];
private float[] mSTMatrix = new float[16];
public TextureRenderer(boolean isOesTexture) {
mOesTexture = isOesTexture;
ByteBuffer bb = ByteBuffer.allocateDirect(squareVertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareVertices);
vertexBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
ByteBuffer bb2 = ByteBuffer.allocateDirect(textureVertices.length * 4);
bb2.order(ByteOrder.nativeOrder());
textureVerticesBuffer = bb2.asFloatBuffer();
textureVerticesBuffer.put(textureVertices);
textureVerticesBuffer.position(0);
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, mVertexShader);
int fragmentShader;
if (mOesTexture) {
fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, mFragmentShaderOes);
} else {
fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, mFragmentShaderRgba);
}
mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram);
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
Matrix.setIdentityM(mMVPMatrix, 0);
Matrix.setIdentityM(mSTMatrix, 0);
}
public void rotate(int degrees) {
double theta = (double) degrees / 180 * Math.PI;
mMVPMatrix[0] = (float) Math.cos(theta);
mMVPMatrix[1] = -(float) Math.sin(theta);
mMVPMatrix[4] = (float) Math.sin(theta);
mMVPMatrix[5] = (float) Math.cos(theta);
}
public void draw(int texture) {
//绘制前要设置 program
GLES20.glUseProgram(mProgram);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
if (mOesTexture)
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);
else
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
int positionHandle = GLES20.glGetAttribLocation(mProgram, "position");
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
int textureCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
GLES20.glEnableVertexAttribArray(textureCoordHandle);
GLES20.glVertexAttribPointer(textureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureVerticesBuffer);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(positionHandle);
GLES20.glDisableVertexAttribArray(textureCoordHandle);
GLES20.glUseProgram(0);
}
private void printMatrix(float[] matrix) {
// 4 x 4
for (int i = 0; i < 4; i++) {
Log.d(LOG_TAG, matrix[i * 4 + 0] + " " + matrix[i * 4 + 1] + " " + matrix[i * 4 + 2] + " " + matrix[i * 4 + 3]);
}
}
public void draw(int texture, float[] matrix) {
// Log.i(TAG, "Matrix is");
// printMatrix(matrix);
// mSTMatrix = matrix;
for (int i = 0; i < matrix.length; i++) {
mSTMatrix[i] = matrix[i];
}
// debug
draw(texture);
}
private int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
Sharder
public class Shader {
private int mProgram = 0;
private int mShaderVertex = 0;
private int mShaderFragment = 0;
// HashMap for storing uniform/attribute handles
private final HashMap<String, Integer> mShaderHandleMap = new HashMap<>();
public Shader() {
}
public void setProgram(int vertexShader, int fragmentShader, Context context)
throws Exception {
String vertexSource = loadRawString(vertexShader, context);
String fragmentSource = loadRawString(fragmentShader, context);
mShaderVertex = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
mShaderFragment = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
int program = GLES20.glCreateProgram();
if (program != 0) {
GLES20.glAttachShader(program, mShaderVertex);
GLES20.glAttachShader(program, mShaderFragment);
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
String error = GLES20.glGetProgramInfoLog(program);
deleteProgram();
throw new Exception(error);
}
}
mProgram = program;
mShaderHandleMap.clear();
}
public void useProgram() {
GLES20.glUseProgram(mProgram);
}
public void deleteProgram() {
GLES20.glDeleteShader(mShaderVertex);
GLES20.glDeleteShader(mShaderFragment);
GLES20.glDeleteProgram(mProgram);
mProgram = mShaderVertex = mShaderFragment = 0;
}
public int programHandle() {
return mProgram;
}
public int getHandle(String name) {
if (mShaderHandleMap.containsKey(name)) {
return mShaderHandleMap.get(name);
}
int handle = GLES20.glGetAttribLocation(mProgram, name);
if (handle == -1) {
handle = GLES20.glGetUniformLocation(mProgram, name);
}
if (handle == -1) {
Log.d("GLSL shader", "Could not get attrib location for " + name);
} else {
mShaderHandleMap.put(name, handle);
}
return handle;
}
public int[] getHandles(String... names) {
int[] res = new int[names.length];
for (int i = 0; i < names.length; ++i) {
res[i] = getHandle(names[i]);
}
return res;
}
private int loadShader(int shaderType, String source) throws Exception {
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
String error = GLES20.glGetShaderInfoLog(shader);
GLES20.glDeleteShader(shader);
throw new Exception(error);
}
}
return shader;
}
private String loadRawString(int rawId, Context context) throws Exception {
InputStream is = context.getResources().openRawResource(rawId);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len;
while ((len = is.read(buf)) != -1) {
baos.write(buf, 0, len);
}
return baos.toString();
}}
1.FBO并不受窗口大小的限制。
2.纹理可以连接到FBO,允许直接渲染到纹理,不需要显示glCopyTexImage。
3.FBO可以包含许多颜色缓冲区,可以同时从一个片段着色器写入
一个FBO本身并没有多大用处,要想让他能被更有效的使用,
我们需要把它与一些可被渲染的缓冲区绑定在一起,这样的缓冲区可以是纹理texture,也可以是渲染缓冲区renderbuffer,
其实它就是一个用来支持离屏渲染的缓冲区,通常是帧缓冲区的一部分,一般不具有纹理格式,常见的模板缓冲和深度缓冲就是这样一类对象,我们要为FBO指定一个dephtbuffer:
GLuint dbname;
glGenRenderBuffersEXT(1,&dbname);
#include <gl\glew.h>
#include <gl\glut.h>
#include <gl\glext.h>
void InitFBO(){
//--------- create fbo
glGenFramebuffersEXT(1,&g_framebuffer);
//bind fbo
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,g_framebuffer);
//--------- 分配一块RGBA贴图 (纹理)空间给FBO绘图使用
glGenTextures(1,&g_texture);
//bing texture
glBindTexture(GL_TEXTURE_2D,g_texture);
//设置 TEXTURE_2D
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
//声明贴图大小及格式分配空间
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,256,256,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL);
//framebuffer的RGBA贴图-绑定纹理与FBO
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE_2D,g_texture,0);
//--------- create render buffer
glGenRenderbuffersEXT(1,&g_depthbuffer);
//分配zbuffer给FBO 使用
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,g_depthbuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,GL_DEPTH_COMPONENT24,256,256);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,GL_DEPTH_ATTACHMENT_EXT,GL_RENDERBUFFER_EXT,g_depthbuffer);
//----------- check fbo status
GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
switch( status ){
case GL_FRAMEBUFFER_COMPLETE_EXT:
//MessageBox(NULL,"GL_FRAMEBUFFER_COMPLETE_EXT!","SUCCESS",MB_OK|MB_ICONEXCLAMATION);
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
MessageBox(NULL,"GL_FRAMEBUFFER_UNSUPPORTED_EXT!","ERROR",MB_OK|MB_ICONEXCLAMATION);
exit(0);
break;
}
}
void myDispaly(){
//使用g_framebuffer FBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,g_framebuffer);
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0,0,256,256);
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0.0f, 0.0f, -5.0f );
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,texture[0]);
glRotatef(xrot,0.0,1.0,0.0);
glutSolidTeapot(2.0);
glPopAttrib();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0);
//glViewport( 0, 0, width, height);
glClearColor( 0.2f, 0.2f, 0.2f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glRotated (cam_rot.x, 1.0f, 0.0f, 0.0f);
glRotated (cam_rot.y, 0.0f, 1.0f, 0.0f);
glRotated (cam_rot.z, 0.0f, 0.0f, 1.0f);
glTranslatef(0,0,-5);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,g_texture);
glScalef(1.5,1.5,1.5);
glRotated (60, 0.0f, 1.0f, 0.0f);
glInterleavedArrays( GL_T2F_V3F, 0, g_cubeVertices );
glDrawArrays( GL_QUADS, 0, 24 );
glutSwapBuffers();
}