參考git
https://blog.csdn.net/u011371324/article/details/78011211github
書寫本文的初衷是爲了自我學習緩存
出現背景ide
明確兩個概念學習
窗口系統默認幀緩衝測試
應用程序幀緩衝優化
FBO(frame buffer object),幀緩衝區對象,在Android中,繪製三角形通常都是直接重寫GLSurfaceView,由於Android已經集成好了OpenGLES的環境,渲染操做都是在默認的幀緩衝去作的,這個幀緩衝是咱們在建立一個Surface的時候自動建立的(Surface,第二節講一下)。但這僅限於demo級別,實際應用中,若是咱們須要渲染到紋理,每每不使用窗口系統默認提供的幀緩衝區域,須要本身建立了。this
何爲幀緩衝?spa
顯示到屏幕上的每一幀數據其實對應的就是內存中的數據,在內存中對應分配着存儲幀數據的緩衝區,包括寫入顏色的顏色緩衝,寫入深度值的深度緩衝,以及基於一些條件丟棄片元的模板緩衝,這幾種緩衝一塊兒稱之爲幀緩衝。.net
爲何要使用幀緩衝?
以前繪製使用的紋理都是使用圖片加載獲得的紋理,若是咱們要對紋理在着色器中作一些處理,模糊、虛化、雙屏、鏡子等特效,那麼使用幀緩衝能夠很好的實現。此外,幀緩衝提供了一個很高效的機制,它可以快速的分離和附着紋理或渲染緩衝對象,這比在幀緩衝之間切換要快得多。
兩種附件
紋理附件
紋理附件和經過圖片加載的紋理相似,只不過這個紋理附加是經過渲染命令寫入到紋理當中的,不是經過圖片紋理獲得的。
注意:除了附加顏色附件以外,還能夠附件深度和模板紋理附件。例如,當咱們開啓了深度測試時,就須要附着一個深度附件,來表示深度信息,以便進行深度測試。爲了附加一個深度緩衝,可用GL_DEPTH_ATTACHMENT做爲附件類型。
渲染緩衝對象(RBO)
渲染緩衝對象(RenderBuffer Object,簡稱RBO)是一個OpenGL格式的緩衝,即以OpenG原生(native)格式存儲它的數據,所以它至關因而優化過的內部數據。當它綁定到FrameBuffer上時,渲染的像素信息就寫到RBO中。
渲染緩衝對象將渲染數據存儲到緩衝中,而且以原生格式存儲,因此它成爲一種快速可寫入的介質。可是,只能寫入,不能修改。RBO經常用來存儲深度和模板信息,用於深度測試和模板測試,並且比用紋理存儲的深度和模板方式要快得多。RBO能夠用來實現雙緩衝(double buffer)。
一樣,渲染緩衝對象也能夠寫入顏色信息,而後將圖像信息顯示在屏幕上。
貌似RBO比紋理有點多,但也不是萬能的,紋理自有紋理的優勢,紋理可以在shader中進行操做或者須要讀取像素時,作一些處理,此時RBO就無能爲力了。
案例演示
網上的不少教程都只是說明了流程,而後演示的demo都混雜着各類其餘的東西,初學者要費很大的力氣才能理解。下面說明一個單純只應用了FBO的案例:
環境是使用Android集成好的,GLSurfaceView ,egl環境也是。(關於這一點,能夠看下一個文檔,介紹EGL)。
撇開OpenGLES的API,單單說離屏渲染的流程以下:
建立一個幀緩衝的buffer,取名爲FrameBuffer(一塊顯存)。
將要繪製的紋理數據寫入FrameBuffer(即綁定)。
在渲染的任意時刻將幀緩衝FrameBuffer中的數據取出使用(在你想用的任意時刻)。
對比直接繪製到屏幕的渲染流程以下:
將要繪製的紋理數據寫入默認的幀緩衝,取名defaultFrameBuffer。
在渲染的過程當中連續的將defaultFrameBuffer中的數據取出使用。
咱們只是使用OpenGLES的API去完成上面的流程。
首先是工程結構,FBO的實現應用了紅線的幾個文件(其餘文件繪製了一個普通的三角形,):
MainActivity指定了View。
public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGLView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new MySurfaceView(this);
setContentView(mGLView);
}
}
MySurfaceView指定了渲染器,渲染模式。onDrawFrame開始離屏渲染。
public class MySurfaceView extends GLSurfaceView {
private MyRenderer mRenderer;
private FBORenderer fboRenderer;
public MySurfaceView (Context context){
super(context);
this.setEGLContextClientVersion(2);
// 繪製普通三角形的渲染器
// mRenderer=new MyRenderer(context);
// this.setRenderer(mRenderer);
fboRenderer = new FBORenderer(context);
this.setRenderer(fboRenderer);
this.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}
FBORenderer重寫了GLSurfaceView的渲染器
public class FBORenderer implements GLSurfaceView.Renderer{
public static int sScreenWidth;
public static int sScreenHeight;
private Shape_FBO mRectangle;
float yAngle;
float xAngle;
private Context mContext;
public FBORenderer(Context context) {
super();
mContext = context;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1);
mRectangle = new Shape_FBO(mContext);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
sScreenWidth = width;
sScreenHeight = height;
GLES20.glViewport(0, 0, width, height);
Matrix.perspectiveM(mProjectionMatrix, 0, 45, (float)width/height, 2, 5);
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0);
}
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mModuleMatrix = new float[16];
private final float[] mViewProjectionMatrix = new float[16];
private final float[] mMVPMatrix = new float[16];
@Override
public void onDrawFrame(GL10 gl) {
Matrix.setIdentityM(mModuleMatrix, 0);
Matrix.rotateM(mModuleMatrix, 0, xAngle, 1, 0, 0);
Matrix.rotateM(mModuleMatrix, 0, yAngle, 0, 1, 0);
Matrix.multiplyMM(mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mViewProjectionMatrix, 0, mModuleMatrix, 0);
// GLES20.glViewport(0, 0, 1024, 1024);
mRectangle.draw(mMVPMatrix, mModuleMatrix);
mRectangle.draw(mMVPMatrix, mModuleMatrix);
}
}
本類就是核心的繪製類了。
1.colorTextureId這個紋理緩存指定到FrameBuffer,與FrameBuffer中的數據進行關聯,也就是說,如今colorTextureId就是FrameBuffer中數據所生成的圖片。
2.在FrameBuffer中繪製讀入的圖片mLoadedTextureId。
3.在默認的窗口defaultFrameBuffer中繪製colorTextureId。
對照代碼理解這張圖,更有體會。
通常狀況咱們都是直接走紅線進行繪製,FBO離屏渲染走綠線
能夠在項目中crtl+f( GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, colorTxtureId);
將其中的colorTxtureId,替換爲mLoadedTextureId,
並註釋draw方法中/*================================================================*/以上的代碼,你也會看到圖片顯示了出來。
首先是生成一張紋理mLoadedTextureId,而後綁定到FrameBuffer中,而後
public class Shape_FBO {
private static String TAG = "ShapeFBO";
private FloatBuffer mSqureBuffer;
private FloatBuffer mSqureBufferfbo;
private int mFrameBufferProgram;
private int mWindowProgram;
private int mLoadedTextureId;
private Context mContext;
public Shape_FBO(Context context) {
this.mContext = context;
this.initVetexData();
}
public void initVetexData() {
//生成紋理
mLoadedTextureId=initTexture(R.drawable.texture1);
//準備繪製數據
float [] bgVertex = new float[] {
-1f,-1f, 0,1,
-1f,1f, 0,0,
1f,-1f, 1,1,
1f,1f, 1,0
};
ByteBuffer vbb0 = ByteBuffer.allocateDirect(bgVertex.length * 4);
vbb0.order(ByteOrder.nativeOrder());
mSqureBuffer = vbb0.asFloatBuffer();
mSqureBuffer.put(bgVertex);
mSqureBuffer.position(0);
float [] fboVertex = new float[] {
-1f,-1f, 0,1,
-1f,1f, 0,0,
1f,-1f, 1,1,
1f,1f, 1,0
};
ByteBuffer vbb1 = ByteBuffer.allocateDirect(fboVertex.length * 4);
vbb1.order(ByteOrder.nativeOrder());
mSqureBufferfbo = vbb1.asFloatBuffer();
mSqureBufferfbo.put(fboVertex);
mSqureBufferfbo.position(0);
}
public int initTexture(int res) {
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), res);
int [] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
//綁定紋理緩存到紋理單元
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[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);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_MIRRORED_REPEAT);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_MIRRORED_REPEAT);
//指定紋理圖片生成2D紋理
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
//釋放bitmap
bitmap.recycle();
//解除綁定
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textures[0];
}
public void draw(float[] mvpMatrix, float[] mMatrix) {
// 生成FrameBuffer
int [] framebuffers = new int[1];
GLES20.glGenFramebuffers(1, framebuffers, 0);
// 生成Texture
int [] textures = new int[2];
GLES20.glGenTextures(2, textures, 0);
int colorTxtureId = textures[0];
//綁定紋理緩存到紋理單元
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, colorTxtureId);
//設置採樣,拉伸方式
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_MIRRORED_REPEAT);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_MIRRORED_REPEAT);
//生成2D紋理
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, FBORenderer.sScreenWidth, FBORenderer.sScreenHeight,0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_SHORT_5_6_5, null);
//綁定framebuffer
int framebufferId = framebuffers[0];
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferId);
//掛載textureID到framebufferId
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, colorTxtureId, 0);
if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)== GLES20.GL_FRAMEBUFFER_COMPLETE) {
Log.e("shapefbo", "glFramebufferTexture2D error");
}
int frameBufferVertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int frameBufferFagmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mFrameBufferProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mFrameBufferProgram, frameBufferVertexShader);
GLES20.glAttachShader(mFrameBufferProgram, frameBufferFagmentShader);
GLES20.glLinkProgram(mFrameBufferProgram);
int positionHandle1 = GLES20.glGetAttribLocation(mFrameBufferProgram, "aPosition");
int textureCoordHandle1 = GLES20.glGetAttribLocation(mFrameBufferProgram, "aTextureCoord");
int textureHandle1 = GLES20.glGetUniformLocation(mFrameBufferProgram, "uTexture");
mSqureBufferfbo.position(0);
GLES20.glVertexAttribPointer(positionHandle1, 2, GLES20.GL_FLOAT, false, (2+2) * 4, mSqureBufferfbo);
mSqureBufferfbo.position(2);
GLES20.glVertexAttribPointer(textureCoordHandle1, 2, GLES20.GL_FLOAT, false, (2+2) * 4, mSqureBufferfbo);
GLES20.glEnableVertexAttribArray(positionHandle1);
GLES20.glEnableVertexAttribArray(textureCoordHandle1);
// GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mLoadedTextureId);
GLES20.glUniform1i(textureHandle1, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);
/*================================================================*/
// 切換到窗口系統的緩衝區
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
int vertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, windowVertexShaderCode);
int fragmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, windowFragmentShaderCode);
mWindowProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mWindowProgram, vertexShader);
GLES20.glAttachShader(mWindowProgram, fragmentShader);
GLES20.glLinkProgram(mWindowProgram);
GLES20.glUseProgram(mWindowProgram);
int positionHandle = GLES20.glGetAttribLocation(mWindowProgram, "aPosition");
int textureCoordHandle = GLES20.glGetAttribLocation(mWindowProgram, "aTextureCoord");
int textureHandle = GLES20.glGetUniformLocation(mWindowProgram, "uTexture");
mSqureBuffer.position(0);
GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, (2+2) * 4, mSqureBuffer);
mSqureBuffer.position(2);
GLES20.glVertexAttribPointer(textureCoordHandle, 2, GLES20.GL_FLOAT, false, (2+2) * 4, mSqureBuffer);
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glEnableVertexAttribArray(textureCoordHandle);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, colorTxtureId);
GLES20.glUniform1i(textureHandle, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDeleteTextures(2, textures, 0);
GLES20.glDeleteFramebuffers(1, framebuffers, 0);
}
private int loaderShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
private String windowVertexShaderCode = ""
+ "attribute vec2 aPosition;"
+ "attribute vec2 aTextureCoord;"
+ "varying vec2 vTextureCoord;"
+ "void main(){"
+ "gl_Position = vec4(aPosition,0,1);"
+ "vTextureCoord = aTextureCoord;"
+ "}";
private String windowFragmentShaderCode = "precision mediump float;"
+ "uniform sampler2D uTexture;"
+ "varying vec2 vTextureCoord;"
+ "void main(){"
+ "gl_FragColor = texture2D(uTexture, vTextureCoord);"
+ "}";
private String vertexShaderCode = ""
+ "attribute vec2 aPosition;"
+ "attribute vec2 aTextureCoord;"
+ "varying vec2 vTextureCoord;"
+ "void main(){"
+ "gl_Position = vec4(aPosition,0,1);"
+ "vTextureCoord = aTextureCoord;"
+ "}";
private String fragmentShaderCode = "precision mediump float;"
+ "uniform sampler2D uTexture;"
+ "varying vec2 vTextureCoord;"
+ "void main(){"
+ "gl_FragColor = texture2D(uTexture, vTextureCoord);"
+ "}";
GitHub地址歡迎點星星