??大家好怎静,今天來學習OpenGL中的倆個重要概念,頂點緩沖對象(VBO)和幀緩沖對象(FBO)与柑。
??VBO
??定義頂點數(shù)據(jù)以后伦乔,我們會把它作為輸入發(fā)送給圖形渲染管線的第一個處理階段:頂點著色器。它會在GPU上創(chuàng)建內(nèi)存用于儲存我們的頂點數(shù)據(jù)垄提,還要配置OpenGL如何解釋這些內(nèi)存榔袋,并且指定其如何發(fā)送給顯卡。頂點著色器接著會處理我們在內(nèi)存中指定數(shù)量的頂點铡俐。
??我們通過頂點緩沖對象(Vertex Buffer Objects, VBO)管理這個內(nèi)存凰兑,它會在GPU內(nèi)存(通常被稱為顯存)中儲存大量頂點。使用這些緩沖對象的好處是我們可以一次性的發(fā)送一大批數(shù)據(jù)到顯卡上审丘,而不是每個頂點發(fā)送一次吏够。從CPU把數(shù)據(jù)發(fā)送到顯卡相對較慢,所以只要可能我們都要嘗試盡量一次性發(fā)送盡可能多的數(shù)據(jù)滩报。當數(shù)據(jù)發(fā)送至顯卡的內(nèi)存中后岛杀,頂點著色器幾乎能立即訪問頂點脯丝,這是個非成芴睿快的過程乖阵。
??頂點緩沖對象是我們在OpenGL教程中第一個出現(xiàn)的OpenGL對象。就像OpenGL中的其它對象一樣惭笑,這個緩沖有一個獨一無二的ID侣姆,所以我們可以使用glGenBuffers函數(shù)和一個緩沖ID生成一個VBO對象生真,演示如下:
private int vboId;
int [] vbos = new int[1];
GLES20.glGenBuffers(1, vbos, 0);
vboId = vbos[0];
??開辟空間后我們用一個int表示我們的頂點緩沖id,方便后面的使用捺宗。
??從這一刻起柱蟀,我們使用的任何(在GL_ARRAY_BUFFER目標上的)緩沖調(diào)用都會用來配置當前綁定的緩沖(VBO)。然后我們可以調(diào)用glBufferData函數(shù)蚜厉,它會把之前定義的頂點數(shù)據(jù)復制到緩沖的內(nèi)存中:
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
??glBufferData是一個專門用來把用戶定義的數(shù)據(jù)復制到當前綁定緩沖的函數(shù)长已。
??它的第一個參數(shù)是目標緩沖的類型:頂點緩沖對象當前綁定到GL_ARRAY_BUFFER目標上。
??第二個參數(shù)指定傳輸數(shù)據(jù)的大小(以字節(jié)為單位)昼牛;用一個簡單的sizeof計算出頂點數(shù)據(jù)大小就行术瓮。
??第三個參數(shù)是我們希望發(fā)送的實際數(shù)據(jù)。
??第四個參數(shù)指定了我們希望顯卡如何管理給定的數(shù)據(jù)贰健。它有三種形式:
- GL_STATIC_DRAW :數(shù)據(jù)不會或幾乎不會改變胞四。
- GL_DYNAMIC_DRAW:數(shù)據(jù)會被改變很多。
- GL_STREAM_DRAW :數(shù)據(jù)每次繪制時都會改變伶椿。
??三角形的位置數(shù)據(jù)不會改變辜伟,每次渲染調(diào)用時都保持原樣,所以它的使用類型最好是GL_STATIC_DRAW脊另。如果导狡,比如說一個緩沖中的數(shù)據(jù)將頻繁被改變,那么使用的類型就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW偎痛,這樣就能確保顯卡把數(shù)據(jù)放在能夠高速寫入的內(nèi)存部分旱捧。
??假如我們定義了頂點坐標和片段坐標,如下:
private float[] vertexData = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f
};
private FloatBuffer vertexBuffer;
private float[] fragmentData = {
0f, 1f,
1f, 1f,
0f, 0f,
1f, 0f
};
private FloatBuffer fragmentBuffer;
??就可以將其綁定到我們的vbo上踩麦,如下:
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20. GL_STATIC_DRAW);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
??上面我們說了glBufferData可以綁定并賦值廊佩,這里我們分成了倆步,一是使用glBufferData開辟空間靖榕,二是調(diào)用glBufferSubData進行賦值,可以一步寫顽铸,也可以分開寫茁计。
??最終的調(diào)用,我們?nèi)匀换谏弦恍」?jié)的TextureRender類谓松,調(diào)用的地方如下:
@Override
public void onDrawFrame() {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1f,0f, 0f, 1f);
GLES20.glUseProgram(program);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureid);
//通過vboid綁定頂點數(shù)據(jù)
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glEnableVertexAttribArray(vPosition);
//注意最后一個參數(shù)星压,不需要傳入頂點數(shù)據(jù)了
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
0);
GLES20.glEnableVertexAttribArray(fPosition);
//同上,最后一個參數(shù)不需要傳入頂點數(shù)據(jù)
GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,
vertexData.length * 4);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
//解綁vboid
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}
??對比上一小節(jié)的內(nèi)容鬼譬,我們新增了倆行代碼和修改了倆行代碼(有注釋部分代碼)娜膘,目的就是使用頂點緩沖對象來替換之前的頂點數(shù)據(jù),提高渲染效率优质。
??FBO
??我們使用幾種不同類型的屏幕緩沖:用于寫入顏色值的顏色緩沖竣贪,用于寫入深度信息的深度緩沖军洼,以及允許我們基于一些條件丟棄指定片段的模板緩沖。把這幾種緩沖結(jié)合起來叫做幀緩沖(Framebuffer)演怎,它被儲存于內(nèi)存中匕争。OpenGL給了我們自己定義幀緩沖的自由,我們可以選擇性的定義自己的顏色緩沖爷耀、深度和模板緩沖甘桑。
??我們目前所做的渲染操作都是是在默認的幀緩沖之上進行的。當你創(chuàng)建了你的窗口的時候默認幀緩沖就被創(chuàng)建和配置好了(GLFW為我們做了這件事)歹叮。通過創(chuàng)建我們自己的幀緩沖我們能夠獲得一種額外的渲染方式,渲染分倆種跑杭,渲染到緩沖區(qū)和渲染到紋理,我們用FBO渲染到紋理咆耿,這個過程我們可以稱之為離屏渲染德谅。
??你也許不能立刻理解應用程序的幀緩沖的含義,通過幀緩沖可以將你的場景渲染到一個不同的幀緩沖中票灰,可以使我們能夠在場景中創(chuàng)建鏡子這樣的效果女阀,或者做出一些炫酷的特效。首先我們會討論它們是如何工作的屑迂,然后我們將利用幀緩沖來實現(xiàn)一些炫酷的效果浸策。
??就像OpenGL中其他對象一樣,我們可以使用一個叫做glGenBuffers的函數(shù)來創(chuàng)建一個幀緩沖對象(簡稱FBO),我們梳理一下使用FBO的創(chuàng)建和解綁流程惹盼。
1庸汗、創(chuàng)建FBO
GLES20.glGenBuffers(1, fbos, 0);
2、綁定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbos[0]);
3手报、設置FBO分配內(nèi)存大小,第一個參數(shù)表示二級紋理蚯舱,第三個參數(shù)表示顏色模式,四五倆個參數(shù)代表屏幕寬高掩蛤,可以與實際不一致枉昏,其他自行查閱
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 720, 1280, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
4、把紋理綁定到FBO
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureid, 0);
5揍鸟、檢查FBO綁定是否成功
GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE)
6兄裂、解綁FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
??創(chuàng)建好之后我們看下使用流程:
1、綁定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbos[0]);
2阳藻、獲取需要繪制的圖片紋理晰奖,然后繪制渲染
3、解綁FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
4腥泥、再把綁定到FBO的紋理繪制渲染出來?
??通過這倆步就能完成離屏渲染匾南,基于上篇文章:音視頻開發(fā)-渲染圖片紋理(3)中的TextureRender,我們進行添加FBO蛔外,看下結(jié)果:
public class TextureRender implements WLEGLSurfaceView.WlGLRender {
private Context context;
private float[] vertexData = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f
};
private FloatBuffer vertexBuffer;
private float[] fragmentData = {
0f, 0f,
1f, 0f,
0f, 1f,
1f, 1f
};
private FloatBuffer fragmentBuffer;
private int program;
private int vPosition;
private int fPosition;
private int textureid;
private int sampler;
private int vboId;
private int fboId;
private int imgTextureId;
private FboRender fboRender;
public TextureRender(Context context) {
this.context = context;
fboRender = new FboRender();
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(fragmentData);
fragmentBuffer.position(0);
}
@Override
public void onSurfaceCreated() {
String vertexSource = WlShaderUtil.getRawResource(context, R.raw.vertex_shader);
String fragmentSource = WlShaderUtil.getRawResource(context, R.raw.fragment_shader);
program = WlShaderUtil.createProgram(vertexSource, fragmentSource);
vPosition = GLES20.glGetAttribLocation(program, "v_Position");
fPosition = GLES20.glGetAttribLocation(program, "f_Position");
sampler = GLES20.glGetUniformLocation(program, "sTexture");
int[] vbos = new int[1];
GLES20.glGenBuffers(1, vbos, 0);
vboId = vbos[0];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20.GL_STATIC_DRAW);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
int[] fbos = new int[1];
GLES20.glGenBuffers(1, fbos, 0);
fboId = fbos[0];
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
textureid = textureIds[0];
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureid);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glUniform1i(sampler, 0);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 720, 1280, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureid, 0);
if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) {
Log.e("create", "fbo wrong");
} else {
Log.e("create", "fbo success");
}
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
imgTextureId = loadTexture(R.drawable.androids);
fboRender.setParams(vertexData, program, vPosition, fPosition, vboId);
}
@Override
public void onSurfaceChanged(int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame() {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1f, 0f, 0f, 1f);
GLES20.glUseProgram(program);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imgTextureId);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glEnableVertexAttribArray(vPosition);
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
0);
GLES20.glEnableVertexAttribArray(fPosition);
GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,
vertexData.length * 4);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
fboRender.onDraw(textureid);
}
/**
* 通過資源文件創(chuàng)建紋理
**/
private int loadTexture(int src) {
int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), src);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureIds[0];
}
}
??新增了一個FboRender蛆楞,主要作用就是上面使用FBO中的第四步溯乒,我們看下代碼:
public class FboRender {
private float[] vertexData;
private int program;
private int vPosition;
private int fPosition;
private int vboId;
public void setParams(float[] vertexData, int program, int vPosition, int fPosition, int vboId) {
this.vertexData = vertexData;
this.program = program;
this.vPosition = vPosition;
this.fPosition = fPosition;
this.vboId = vboId;
}
/**
* 在已經(jīng)解綁FBO后重新繪制視圖
* **/
public void onDraw(int textureId) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1f, 0f, 0f, 1f);
GLES20.glUseProgram(program);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glEnableVertexAttribArray(vPosition);
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
0);
GLES20.glEnableVertexAttribArray(fPosition);
GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,
vertexData.length * 4);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}
}
??運行上面的效果我們發(fā)現(xiàn)最終展示的圖片被橫向的從中間被反轉(zhuǎn)了,這是因為坐標系的緣故臊岸,看下面一張圖
??紋理坐標系和FBO紋理坐標系是不一樣的橙数,F(xiàn)BO的坐標系也是OpenGL的標準坐標系,那么當我們使用FBO的時候帅戒,我們的坐標就不能用普通紋理坐標系的標準來決定坐標點灯帮,那么上面的TextureRender和FboRender的代碼我們就需要進行修改,保證普通紋理坐標系和FBO紋理坐標系的使用互不干擾逻住,如下:
public class TextureRender implements WLEGLSurfaceView.WlGLRender{
private Context context;
private float[] vertexData = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f
};
private FloatBuffer vertexBuffer;
private float[] fragmentData = {
0f, 0f,
1f, 0f,
0f, 1f,
1f, 1f
};
private FloatBuffer fragmentBuffer;
private int program;
private int vPosition;
private int fPosition;
private int textureid;
private int sampler;
private int vboId;
private int fboId;
private int imgTextureId;
private FboRender fboRender;
public TextureRender(Context context) {
this.context = context;
fboRender = new FboRender(context);
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(fragmentData);
fragmentBuffer.position(0);
}
@Override
public void onSurfaceCreated() {
fboRender.onCreate();
String vertexSource = WlShaderUtil.getRawResource(context, R.raw.vertex_shader);
String fragmentSource = WlShaderUtil.getRawResource(context, R.raw.fragment_shader);
program = WlShaderUtil.createProgram(vertexSource, fragmentSource);
vPosition = GLES20.glGetAttribLocation(program, "v_Position");
fPosition = GLES20.glGetAttribLocation(program, "f_Position");
sampler = GLES20.glGetUniformLocation(program, "sTexture");
int [] vbos = new int[1];
GLES20.glGenBuffers(1, vbos, 0);
vboId = vbos[0];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20. GL_STATIC_DRAW);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
int[] fbos = new int[1];
GLES20.glGenBuffers(1, fbos, 0);
fboId = fbos[0];
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
int []textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
textureid = textureIds[0];
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureid);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glUniform1i(sampler, 0);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 720, 1280, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureid, 0);
if(GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE)
{
Log.e("create", "fbo wrong");
}
else
{
Log.e("create", "fbo success");
}
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
imgTextureId = loadTexture(R.drawable.androids);
}
@Override
public void onSurfaceChanged(int width, int height) {
GLES20.glViewport(0, 0, width, height);
fboRender.onChange(width, height);
}
@Override
public void onDrawFrame() {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1f,0f, 0f, 1f);
GLES20.glUseProgram(program);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imgTextureId);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glEnableVertexAttribArray(vPosition);
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
0);
GLES20.glEnableVertexAttribArray(fPosition);
GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,
vertexData.length * 4);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
fboRender.onDraw(textureid);
}
private int loadTexture(int src)
{
int []textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), src);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureIds[0];
}
}
??以及
public class FboRender {
private Context context;
private float[] vertexData = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f
};
private FloatBuffer vertexBuffer;
private float[] fragmentData = {
0f, 1f,
1f, 1f,
0f, 0f,
1f, 0f
};
private FloatBuffer fragmentBuffer;
private int program;
private int vPosition;
private int fPosition;
private int vboId;
public FboRender(Context context) {
this.context = context;
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(fragmentData);
fragmentBuffer.position(0);
}
public void onCreate()
{
String vertexSource = WlShaderUtil.getRawResource(context, R.raw.vertex_shader);
String fragmentSource = WlShaderUtil.getRawResource(context, R.raw.fragment_shader);
program = WlShaderUtil.createProgram(vertexSource, fragmentSource);
vPosition = GLES20.glGetAttribLocation(program, "v_Position");
fPosition = GLES20.glGetAttribLocation(program, "f_Position");
int [] vbos = new int[1];
GLES20.glGenBuffers(1, vbos, 0);
vboId = vbos[0];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20. GL_STATIC_DRAW);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}
public void onChange(int width, int height)
{
GLES20.glViewport(0, 0, width, height);
}
public void onDraw(int textureId)
{
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1f,0f, 0f, 1f);
GLES20.glUseProgram(program);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
GLES20.glEnableVertexAttribArray(vPosition);
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
0);
GLES20.glEnableVertexAttribArray(fPosition);
GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,
vertexData.length * 4);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}
}
??這樣就完成了倆種坐標系的數(shù)據(jù)的互不干擾钟哥,我們可以自行觀察前后的坐標點發(fā)生了什么變化。有的同學可能發(fā)現(xiàn)圖片展示后展示不全瞎访,這是因為我們分配的大小和我們屏幕的大小不一致
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 720, 1280, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
??可以通過更改720的寬和1280的高度來適應我們的屏幕腻贰,有的同學可能仍然改了發(fā)現(xiàn)有問題,我們可以更改當前Ativity的style為全屏和無標題看看效果扒秸。
??重要的是理解整個繪制流程播演,綁定FBO后,可以進行一系列復雜的紋理繪制伴奥,并且這些繪制是不可見的(故此稱作離屏渲染)写烤,解綁后再在之前和FBO綁定的紋理上進行繪制,即可展示最終的渲染效果拾徙。