JavaScript-WebGL2学习笔记四-蒙板

stencil test(蒙板) demo的显示效果


这个例子由四个源文件构成

webgl.html

<html>
<head>
    <!--
        Title: JavaScript-WebGL2学习笔记四-Stencil
        Date: 2018-3-22
        Author: kagula
        Prologue:
        为了让自己在WebGl中如何使用stencil test有个概念,画出三个Shape做测试!

        Description:
        第一个红色正方形用来设置stencil buffer.
        第二个绿色四边形用来演示,只显示同stencil buffer相交的区域.
        第三个蓝色矩形用来演示,只显示同stencil buffer不相交的区域.

        Reference:
        [1]https://www.cnblogs.com/aokman/archive/2010/12/13/1904723.html
        [2]https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/stencilFunc

        Run environment
        [1]Chrome 65.0.3325.162
        [2]nginx  1.12.2

        Remark
        [1]顺便测试了在一次drawScene中使用两个shader程序。
    -->
    <title>JavaScript-WebGL2学习笔记四-Stencil蒙板</title>

    <meta charset="utf-8">
    <!-- gl-matrix version 2.4.0 from http://glmatrix.net/ -->
    <script type="text/javascript" src="/gl-matrix-min.js"></script>

    <script type="text/javascript" src="webgl_helper.js"></script>
    <script type="text/javascript" src="shader.js"></script>
    <script type="text/javascript" src="shape.js"></script>
</head>

<body>
    <canvas id="glCanvas" width="320" height="200"></canvas>
</body>

</html>

<script>
    main();
    
    function main() {
        //选择器的使用
        //http://www.runoob.com/jsref/met-document-queryselector.html
        const canvas = document.querySelector("#glCanvas");

        // Initialize the GL context
        //设置stencil=true, 在webgl上下文中启用stencil buffer.
        const gl = canvas.getContext("webgl2", {stencil: true});    

        // Only continue if WebGL is available and working
        if (!gl) {
            alert("Unable to initialize WebGL. Your browser or machine may not support it.");
            return;
        }        

        var contextAttributes = gl.getContextAttributes();
        var haveStencilBuffer = contextAttributes.stencil;
        if (!haveStencilBuffer) {
            alert("Unable to support stencil.");
            return;
        }      

        initShader(gl);

        //initBuffers(gl)返回要render的vertex.
        drawScene(gl);
    }//main
</script>

webgl_helper.js

function drawScene(gl) {
    gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
    gl.clearDepth(1.0);                 // Clear everything
    gl.clearStencil(0);                 // 用0填充 stencil buffer
    gl.enable(gl.DEPTH_TEST);           // Enable depth testing
    gl.depthFunc(gl.LEQUAL);            // Near things obscure far things

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);

    const fieldOfView = 45 * Math.PI / 180;   // in radians
    const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    const zNear = 0.1;
    const zFar = -100.0;

    const projectionMatrix = mat4.create();
    mat4.perspective(projectionMatrix,
                     fieldOfView,
                     aspect,
                     zNear,
                     zFar);

    const modelViewMatrix = mat4.create();
    mat4.translate(modelViewMatrix,     // destination matrix
                   modelViewMatrix,     // matrix to translate
                   [-0.0, 0.0, -3.0]);  // amount to translate

    //第一个shape, 当中的红色正方形, 用来修改stencil buffer.
    {
        let redShape =createRedShape(gl);
        setShaderProgramArrayArg(gl,
            programInfo.attribLocations.vertexPosition, 
            redShape.position, 2);
            
        setShaderProgramArrayArg(gl,
            programInfo.attribLocations.vertexColor, 
            redShape.color, 4);
    
        // Tell WebGL to use our program when drawing
        gl.useProgram(programInfo.program);
    
        gl.uniformMatrix4fv(
            programInfo.uniformLocations.projectionMatrix,
            false,
            projectionMatrix);
    
        gl.uniformMatrix4fv(
            programInfo.uniformLocations.modelViewMatrix,
            false,
            modelViewMatrix);

        {
            // gl.stencilFunc(func, ref, mask);  
            /*
            gl.stencilFunc第一个参数可能的取值
            gl.NEVER:      Never pass.
            gl.LESS:       Pass if (ref & mask) <  (stencil & mask).
            gl.EQUAL:      Pass if (ref & mask) =  (stencil & mask).
            gl.LEQUAL:     Pass if (ref & mask) <= (stencil & mask).
            gl.GREATER:    Pass if (ref & mask) >  (stencil & mask).
            gl.NOTEQUAL:   Pass if (ref & mask) != (stencil & mask).
            gl.GEQUAL:     Pass if (ref & mask) >= (stencil & mask).
            gl.ALWAYS:     Always pass.
            */
            // gl.stencilOp(fail,zfail,zpass);
            /*
            gl.KEEP
                Keeps the current value.
            gl.ZERO
                Sets the stencil buffer value to 0.
            gl.REPLACE
                Sets the stencil buffer value to the reference value as specified by WebGLRenderingContext.stencilFunc().
            gl.INCR
                Increments the current stencil buffer value. Clamps to the maximum representable unsigned value.
            gl.INCR_WRAP
                Increments the current stencil buffer value. Wraps stencil buffer value to zero when incrementing the maximum representable unsigned value.
            gl.DECR
                Decrements the current stencil buffer value. Clamps to 0.
            gl.DECR_WRAP
                Decrements the current stencil buffer value. Wraps stencil buffer value to the maximum representable unsigned value when decrementing a stencil buffer value of 0.
            gl.INVERT
                Inverts the current stencil buffer value bitwise.
            */
        }
    
        {            
            gl.enable(gl.STENCIL_TEST);
            //gl.colorMask(false, false, false, false);//不render要被用作修改stencil buffer的pixel.
            gl.stencilFunc(gl.ALWAYS, 1, 0xff);
            //对每个要render的pixel做检查, 这里是canvas当中,蓝色的一块区域
            //stencil[willRenderPixelLocation]+=1,stencil buffer的其它地方保持原来的值!
            gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);

            const offset = 0;
            const vertexCount = 4;
    
            gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
            //现在要render pixel的地方,stencil buffer中那个位置的值为1, 其它位置还是0.

            //release buffer
            //gl.bindBuffer(gl.ARRAY_BUFFER, null);
            //gl.deleteBuffer(buffer);

        }
    }

    //演示只render stencilBuffer[aPostion]=1的pixel.
    {
        gl.useProgram(programInfo2.program);
        let greenShape = createGreenShape(gl);
        setShaderProgramArrayArg(gl,
            programInfo2.attribLocations.vertexPosition, 
            greenShape.position, 2);
            
        setShaderProgramArrayArg(gl,
            programInfo2.attribLocations.vertexColor, 
            greenShape.color, 4);
    
        {
            //stencilFunc表达式为true的位置, write pixel.
            gl.stencilFunc(gl.EQUAL, 1, 0x0f);

            //我们只是使用stencil不是修改stencil,所以三个参数都设成gl.KEEP就可以了.
            gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);


            gl.depthMask(true);//enable write depth
            gl.colorMask(true,true,true,true);//enable write pixel


            const offset = 0;
            const vertexCount = 4;
    
            gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);

            gl.bindBuffer(gl.ARRAY_BUFFER, null);
        }
    }

    //演示只render stencilBuffer[aPostion]!=1的pixel.
    {
        //stencilFunc表达式为true的位置, write pixel.
        //所以这里蓝色矩形同红色重叠的部分,就not write pixel了.
        gl.stencilFunc( gl.NOTEQUAL, 0x1, 0xf);

        //我们只是使用stencil不是修改stencil,所以三个参数都设成gl.KEEP就可以了.
        gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP );
        let blueShape = createBlueShape(gl);
        setShaderProgramArrayArg(gl,
            programInfo2.attribLocations.vertexPosition, 
            blueShape.position, 2);
            
        setShaderProgramArrayArg(gl,
            programInfo2.attribLocations.vertexColor, 
            blueShape.color, 4);
    
        {
            gl.depthMask(true);
            gl.colorMask(true,true,true,true);
            const offset = 0;
            const vertexCount = 4;
    
            gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
            gl.bindBuffer(gl.ARRAY_BUFFER, null);
        }
    }
    
    {
        gl.disable(gl.STENCIL_TEST);
    }

    //关闭stencil test后, 再gl.draw, 
    //就不管stencil buffer中的值, 要write的pixel都能write了.
}

shape.js

function createRedShape(gl) {
    const positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    const positions = [
        1.0, 1.0,
        -1.0, 1.0,
        1.0, -1.0,
        -1.0, -1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER,
                    new Float32Array(positions),
                    gl.STATIC_DRAW);


    const colors = [
    1.0, 0.0, 0.0, 1.0,    // 
    1.0, 0.0, 0.0, 1.0,    // red
    1.0, 0.0, 0.0, 1.0,    // 
    1.0, 0.0, 0.0, 1.0,    // 
    ];
    const colorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

    return {
        position: positionBuffer,
        color: colorBuffer,
    };
}

function createGreenShape(gl) {
    const positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    const positions = [
        0.0,0.0,
        1.0,0.0,
        1.0,1.0,
        2.0,1.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER,
                    new Float32Array(positions),
                    gl.STATIC_DRAW);


    const colors = [
    0.0, 1.0, 0.0, 1.0,    // 
    0.0, 1.0, 0.0, 1.0,    // 
    0.0, 1.0, 0.0, 1.0,    // 
    0.0, 1.0, 0.0, 1.0,    // 
    ];
    const colorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

    return {
        position: positionBuffer,
        color: colorBuffer,
    };
}

function createBlueShape(gl) {
    const positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    const positions = [
         -1, -1,
        0.5, -1,
         -1, 0.5,
        0.5, 0.5
    ];
    gl.bufferData(gl.ARRAY_BUFFER,
                    new Float32Array(positions),
                    gl.STATIC_DRAW);


    const colors = [
    0.0, 0.0, 1.0, 1.0,    // 
    0.0, 0.0, 1.0, 1.0,    // 
    0.0, 0.0, 1.0, 1.0,    // 
    0.0, 0.0, 1.0, 1.0,    // 
    ];
    const colorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

    return {
        position: positionBuffer,
        color: colorBuffer,
    };
}

shader.js

var programInfo;
var programInfo2;

function initShaderProgram(gl, vsSource, fsSource) {
    const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
    const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

    // Create the shader program
    const shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    // If creating the shader program failed, alert
    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
        return null;
    }

    return shaderProgram;
}

function setShaderProgramArrayArg(gl, destPositionInShader, srcArray, elementSize)
{
    gl.bindBuffer(gl.ARRAY_BUFFER, srcArray);

    gl.vertexAttribPointer(
        destPositionInShader,
        elementSize,// pull out 2 values per iteration //Must be 1, 2, 3, or 4.
        gl.FLOAT,// the data in the buffer is 32bit floats
        false,// don't normalize
        0,//stride, how many bytes to get from one set of values to the next
        0);//how many bytes inside the buffer to start from

    gl.enableVertexAttribArray(destPositionInShader);    
}

function loadShader(gl, type, source) {
    const shader = gl.createShader(type);

    gl.shaderSource(shader, source);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
    }

    return shader;
}

function initProgram(gl)
{
    //Vertex shader program
    //vertex shader负责把点的position转成左下角为(-1,-1)右上角为(1,1)的二维平面上去。
    const vsSource = `
    attribute vec4 aVertexPosition;
    attribute vec4 aVertexColor;//从外部拿到颜色,不做任何处理,丢给vColor.

    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;

    //从aVertexColor拿到的颜色直接给vColor,
    //由vColor传给Fragment Shader.
    varying lowp vec4 vColor;

    void main() {
    gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
    vColor = aVertexColor;//什么都不做,只是为了把从外部得到的color传递给Fragment Shader.
    }
    `;

    // Fragment shader, 相当于pixel shader  
    const fsSource = `
    varying lowp vec4 vColor;//从vertex shader得到的颜色放在这里。
    void main() {
    //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    gl_FragColor = vColor;//直接使用从vertex shader传过来的数据。
    }
    `;

    //装配shader到shaderProgram中去
    const shaderProgram = initShaderProgram(gl, vsSource, fsSource);

    //为了让外部的数据能统一传到shanderProgram中去,新建programInfo对象。
    //vertexPosition => aVertexPosition位置
    //projectionMatrix => uProjectionMatrix位置
    //modelViewMatrix => uModelViewMatrix位置
    //...
    programInfo = {
        program: shaderProgram,
        attribLocations: {
            vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
            vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'),
        },
        uniformLocations: {
            projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
            modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
        },
    };
}

function initProgram2(gl)
{
    // Vertex shader program
    const vsSource = `
    attribute vec2 aVertexPosition;

    attribute vec4 aVertexColor;
    varying lowp vec4 vColor;

    void main() {
    vColor = aVertexColor;
    gl_Position = vec4(aVertexPosition, 0, 1);
    }
    `;

    // Fragment shader, 相当于pixel shader  
    const fsSource = `
    varying lowp vec4 vColor;//从vertex shader得到的颜色放在这里。
    void main() {
    gl_FragColor = vColor;//直接使用从vertex shader传过来的数据。
    }
    `;

    //装配shader到shaderProgram中去
    const shaderProgram = initShaderProgram(gl, vsSource, fsSource);

    //为了让外部的数据能统一传到shanderProgram中去,新建programInfo对象。
    //vertexPosition => aVertexPosition位置
    //projectionMatrix => uProjectionMatrix位置
    //modelViewMatrix => uModelViewMatrix位置
    //...
    programInfo2 = {
        program: shaderProgram,
        attribLocations: {
            vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
            vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'),
        },
        uniformLocations: {
            projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
            modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
        },
    };
}

function initShader(gl)
{
    initProgram(gl);
    initProgram2(gl);
}

猜你喜欢

转载自blog.csdn.net/lee353086/article/details/79669258