1. 程式人生 > >JavaScript-WebGL2學習筆記五-離屏渲染

JavaScript-WebGL2學習筆記五-離屏渲染

Author: kagula

Date: 2018-03-30

Description:

          這是我《WebGL Lesson 16 – rendering to textures》的學習筆記。

源文地址:http://learningwebgl.com/blog/?p=1786

Content:

WebGL中離屏渲染有三個步驟組成:

[第一步]新建frame buffer同frame buffer關聯的texture

[第二步]render到frame buffer, 這樣同frame buffer關聯的texture被繪製。

[最後一步]像使用普通texture一樣,使用剛才繪製好的texture.

第一步:先要建frame buffer,並同texture物件和render buffer做關聯,如下

    var rttFramebuffer;
    var rttTexture;

    function initTextureFramebuffer() {
        //create frame buffer
        rttFramebuffer = gl.createFramebuffer();
        gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
        rttFramebuffer.width = 512;
        rttFramebuffer.height = 512;

        //create texture
        rttTexture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, rttTexture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
        gl.generateMipmap(gl.TEXTURE_2D);

        //把texture的圖片資料指標登出(交給frame buffer管理)
        gl.texImage2D(gl.TEXTURE_2D, //指定目標紋理,這個值必須是gl.TEXTURE_2D
        0, // 執行細節級別。0是最基本的影象級別,n表示第N級貼圖細化級別
        gl.RGBA, //internalFormat, 指定紋理中的顏色元件。可選的值有GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE, GL_LUMINANCE_ALPHA 等幾種。
        rttFramebuffer.width, rttFramebuffer.height, //紋理影象的寬、高度,必須是2的n次方。紋理圖片至少要支援64個材質元素的寬、高度
        0, //邊框的寬度。必須為0。
        gl.RGBA, //源資料的顏色格式, 不需要和internalFormat取值必須相同。
        gl.UNSIGNED_BYTE, //源資料分量的資料型別。
        null);//記憶體中指向影象資料的指標

        //create render buffer
        var renderbuffer = gl.createRenderbuffer();
        gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
        //設定當前工作的渲染緩衝的儲存大小
        gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, //這兩個引數是固定的
        rttFramebuffer.width, rttFramebuffer.height);

        //texture繫結到frame buffer中
        //https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/framebufferTexture2D
        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 
        gl.TEXTURE_2D, //target
        rttTexture, //source, A WebGLTexture object whose image to attach.
        0);//A GLint specifying the mipmap level of the texture image to be attached. Must be 0.


        //把render buffer繫結到frame buffer上
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, 
        gl.RENDERBUFFER, //renderBufferTarget, A GLenum specifying the binding point (target) for the render buffer.
        renderbuffer);//A WebGLRenderbuffer object to attach.

        //unbind
        gl.bindTexture(gl.TEXTURE_2D, null);
        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    }

上面的程式碼新建了rttFrameBuffer物件。

第二步:render到紋理,並讓這個紋理生成MipMap

我們drawElement的時候,可以render到這個物件中。

        gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
        drawSceneOnLaptopScreen();

注意上面的第一行程式碼,我們讓當前render, render到rttFramebuffer中。

drawSceneOnLaptopScreen函式中的程式碼片段如下(就跟原來的普通render一樣寫程式碼,只不過這次是render到rttFrameBuffer

中)

        gl.viewport(0, 0, rttFramebuffer.width, rttFramebuffer.height);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        mat4.perspective(45, laptopScreenAspectRatio, 0.1, 100.0, pMatrix);

。。。

gl.drawElements(gl.TRIANGLES, moonVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

。。。

gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

由於rttFrameBuffer同rttTexture關聯,上面的程式碼把scene render到了rttTexture上去。

再用下面的程式碼為rttTexture生成MipMap

        gl.bindTexture(gl.TEXTURE_2D, rttTexture);
        gl.generateMipmap(gl.TEXTURE_2D);
        gl.bindTexture(gl.TEXTURE_2D, null);

最後一步:

用下面的程式碼解除同frame buffer的繫結,這樣我們才能render到使用者可見的螢幕上。

        gl.bindFramebuffer(gl.FRAMEBUFFER, null);

最後可以像使用普通紋理物件一樣使用rttTexture。

如下

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, rttTexture);
        gl.uniform1i(shaderProgram.samplerUniform, 0);

        setMatrixUniforms();
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, laptopScreenVertexPositionBuffer.numItems);

Lesson16包含了太多不必要的程式碼,現在通過整理,可以看到實現離屏渲染不是件很難的事。