WebGL中圖片多級處理(FrameBuffer)

在webgl的使用過程當中,咱們一般會想對texture進行多級處理並對其貼在表面顯示html

如對較精準的邊緣檢測,要前後使用灰度shader模糊shader邊緣shader來進行處理,而每次的處理對象則是上一次處理後的texture,這就要對處理後的結果進行覆蓋保存。git

這是我在作Polyer使用到的:http://zhiyishou.github.io/Polyergithub

在衆多webgl庫中,直接有選項rederToTarget來實現將shader處理後的texture渲染並覆蓋原texture,其是怎麼完成這個步驟的呢?web

這就要引出本文的主角——FrameBuffer緩存

 

FrameBuffer是什麼


 

FBO(Frame Buffer Object)是被推薦用於將數據渲染到紋理對象的擴展。函數

FrameBuffer就像是一個webgl顯示容器同樣,平時咱們使用gl.drawArrays或者gl.drawElements都是將對象繪製在了默認的窗口中,而當咱們指定一個FrameBuffer爲當前窗口時,則用這兩個方法去繪製,則會將對象繪製於指定的FrameBuffer中。webgl

 

FrameBuffer的使用


 

internalformat, int x, int y, sizei width,
sizei height, int border);
target: TEXTURE_2D, TEXTURE_ui

FBO的建立:spa

//建立一個Framebuffer
var fb = gl.createFramebuffer();
//將fb綁定爲目前的窗口   
gl.bindFramebuffer(gl.FRAMEBUFFER,fb);

 

這樣,咱們則建立了一個新的能夠繪製的buffer了,且其並不會被顯示出來code

可是,這樣就能夠了嗎?咱們想到的是將通過shader渲染後的texture渲染出來並交給下一個shader,這時則引入方法framebufferTexture2D

Reference from《OpenGL ES Reference Pages about FramebufferTexture2D》:

 

To render directly into a texture image, a specified image from a texture object can be attached as one of the logical buffers of the currently bound framebuffer object by calling the command

爲了直接渲染至紋理圖片中,一個紋理對象中指定的圖片可用下面的方法綁定在當前使用的FBO上一個邏輯緩存中

void FramebufferTexture2D( enum target, enum attachment, enum textarget, uint texture, int level );

 

target:

• FRAMEBUFFER

attachment:

• If attachment is COLOR_ATTACHMENT0, then image must have a colorrenderable internal format.(色彩)

• If attachment is DEPTH_ATTACHMENT, then image must have a depthrenderable internal format.(深度)

• If attachment is STENCIL_ATTACHMENT, then image must have a stencilrenderable internal format.(模板)

textarget:

• TEXTURE_2D    (two-dimensional texture)

• TEXTURE_CUBE_MAP_POSITIVE_X  (three-dimensional +x texture)

• TEXTURE_CUBE_MAP_POSITIVE_Y  (three-dimensional +y texture)

• TEXTURE_CUBE_MAP_POSITIVE_Z  (three-dimensional +z texture)

• TEXTURE_CUBE_MAP_NEGATIVE_X  (three-dimensional -x texture)

• TEXTURE_CUBE_MAP_NEGATIVE_Y  (three-dimensional -y texture)

• TEXTURE_CUBE_MAP_NEGATIVE_Z  (three-dimensional -z texture)

texture:

  texture object

level:

  specifies the mipmap level of the texture image to be attached to the framebuffer and must be 0.

 

咱們使用這個方法來進行綁定(本文只介紹色彩的綁定,嘗試和模板相似,可是有不一樣之處,不在此討論)

//建立一個紋理對象
var texture = gl.createTexture();
//使用以下的設置來建立texture,這樣對texture的設置可使咱們對任何尺寸的圖片進行處理
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

var fb = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER,fb); //使用該方法將texture的顏色值與FBO進行綁定 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);

 

綁定後,當咱們執行gl.drawArrays或gl.drawElements方法時,則會將直接渲染至目前綁定的FBO上,而FBO又與texture的色彩進行了綁定,因此繪製時則也將色彩渲染至了texture中

這樣,咱們則可用兩個FBO來進行隊列加工:

OriginalImage --> texture1

texture1 --> gray --> texture2

texture2 --> blur --> texture1

texture1 --> edge --> texture2

 

下面是具體實現過程

var FBOs = [],
    textures = [];
    
for(var i = 0; i < 2; i++){
    var texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

    var fb = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER,fb);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
    
    //store corresponding texture and fb
    textures.push(texture);
    FBOs.push(fb);
}

gl.bindTexture(gl.TEXTURE_2D, originalImageTexture);

for(var i = 0; i < 3; i++){
    switch(i){case 0:
            //set gray shader to current shader program
            //handle arguments to vs shader and fs shader
            break;
        case 1:
            //set blur shader to current shader program
            //handle arguments to vs shader and fs shader
            break;
        case 2:
            //set edge shader to current shader program
            //handle arguments to vs shader and fs shader
            break;
    }
    
    gl.bindFramebuffer(gl.FRAMEBUFFER, FBOs[i%2]);
    //set the viewport fits the images size
    gl.viewport(0, 0, imgWidth, imgHeight);
    gl.drawArrays(....); //or gl.drawElements(....);
    
    //set the rendered texture to current texture for next frambuffer using
    gl.bindTexture(gl.TEXTURE_2D, texture[i%2]);
}

 

完整的過程爲:

originalTexture --> gray program --> set FBO1 --> draw --> FBO1 --> set texture1

texture1 --> blur program --> set FBO2 --> draw --> FBO2 --> set texture2

texture2 --> edge program --> set FBO1 --> draw --> FBO1 --> set texture1

 

該過程當中,FBO1與texture1是進行色彩渲染綁定的,因此set FBO1後進行渲染則會直接渲染至texture1

當咱們完成了整個繪製的時候,要正常顯示處理後的圖片,則要從FBO中跳出來

//set FBO to null to use default framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, null);

 

FrameBuffer的其它用處


 

gl.readPixels

從FrameBuffer中讀取像素顏色數據

Reference from 《webgl_2.0_reference_card》/OpenGL ES Reference Pages about readPixels:

 

Pixels in the current framebuffercan be read back into an ArrayBufferView object.

void readPixels(int x, int y, long width, long height,enum format, enum type, Object pixels)

 

x,y

• Specify the window coordinates of the first pixel that is read from the frame buffer. This location is the lower left corner of a rectangular block of pixels.

width,height

 

• Specify the dimensions of the pixel rectangle. width and height of one correspond to a single pixel.

format

• Specifies the format of the pixel data. The following symbolic values are accepted  RGBA in WebGL

type

• Specifies the data type of the pixel data. Must be UNSIGNED_BYTEin WebGL

pixels

  • Returns the pixel data.

 

 

在使用過程當中,咱們要先建立pixels對象來儲存數據

//using ArrayBufferView to store pixels data only, Unit8Array is the best because each color data is a byte
var pixels = new Uint8Array(ImageWidth * ImageHeight * 4);

gl.readPixels(0, 0, ImageWidth, ImageHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

 

這樣,咱們則能夠獲得整個FBO中的色彩數據

 

gl.CopyTexImage2D

gl.CopyTexSubImage2D

這兩個函數都是用來從FBO中將數據複製至當前綁定的texture中的

CopyTexImage2D方法:

Reference from OpenGL ES Reference Pages about CopyTexImage2D:

 

copy pixels into a 2D texture image

void CopyTexImage2D(enum target, int level,enum internalformat, int x, int y, sizei width,sizei height, int border);

 

target:

• TEXTURE_2D

• TEXTURE_CUBE_MAP_POSITIVE_{X, Y, Z},

• TEXTURE_CUBE_MAP_NEGATIVE_{X, Y, Z}

internalformat:

• ALPHA

• LUMINANCE

• LUMINANCE_ALPHA

• RGB

• RGBA

x,y

Specify the window coordinates of the lower left corner of the rectangular region of pixels to be copied.

width

Specifies the width of the texture image. Must be 0 or 2 n + 2 ⁡ border for some integer n.

height

Specifies the height of the texture image. Must be 0 or 2 m + 2 ⁡ border for some integer m.

border

Specifies the width of the border. Must be either 0 or 1.

 CopyTexSubImage2D方法:

Reference from 《OpenGL ES Reference Pages about CopyTexSubImage2D:

 

copy a two-dimensional texture subimage

void CopyTexSubImage2D(enum target, int level, int xoffset,int yoffset, int x, int y, sizei width, sizei height);

 

target:

• TEXTURE_2D

• TEXTURE_CUBE_MAP_POSITIVE_{X, Y, Z},

• TEXTURE_CUBE_MAP_NEGATIVE_{X, Y, Z}

level:

Specifies the level-of-detail number. Level 0 is the base image level. Level n is the nth mipmap reduction image.

xoffset:

Specifies a texel offset in the x direction within the texture array.

yoffset:

Specifies a texel offset in the y direction within the texture array.

x,y:

Specify the window coordinates of the lower left corner of the rectangular region of pixels to be copied.

width:

Specifies the width of the texture subimage.

height:

Specifies the height of the texture subimage.

這兩個方法的不一樣之處相信你們已經看得出來了

 

CopyTexSubImage2D相對CopyTexImage2D增長了offset來改變複製區域

其最終複製區域爲:[x, xoffset + width - 1]與[y, yoffset + height -1]。

 

而CopyTexImage2D則是比CopyTexSubImage2D多了internelformat參數來控制對像素數據複製的種類。

 

結語:

有了對texture靈活的操做,則咱們才能作出更有趣的東西出來,而framebuffer在裏面也是至關重要的一個角色。

 

附:

WebGL-1.0參考卡片:http://files.cnblogs.com/files/zhiyishou/webgl-reference-card-1_0.pdf

OpenGL-ES-2.0參考卡片:http://files.cnblogs.com/files/zhiyishou/OpenGL-ES-2_0-Reference-card.pdf

OpenGL-ES-2.0參考手冊:https://www.khronos.org/opengles/sdk/docs/man/

 

The end.

相關文章
相關標籤/搜索