webgl - 裁剪

738 阅读4分钟

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战 玩过LOL或者王者的人应该都知道,很多游戏在运行时,屏幕上不仅显示主视角游戏场景,还会显示小地图这类的次视角游戏场景。想要满足这样的需求仅采用前面介绍的技术几乎是不可能的,因此,本章将向读者介绍一些能满足特殊需求的裁剪与测试方面的知识,主要内容包括裁剪测试,模板测试。

裁剪测试

裁剪测试主要在渲染场景时限制绘制区域,用它可以方便地实现同时在屏幕上绘制主视角与次视角场景的功能。

前面已经提过,裁剪测试可以在渲染时限制绘制区域,通过此技术可以在屏幕(帧缓冲)上指定一个矩形区域。启用裁剪测试后,绘制将不会像前面介绍过的案例一样在整个屏幕(帧缓冲)中进行,而是仅在指定的区域中进行。 不在此矩形区域内的片元将被丢弃,只有在此矩形区域内的片元才有机会最终进入帧缓冲。因此,实际的效果就是在屏幕上开辟一个小窗口,在其中进行特定内容的绘制。

使用裁剪测试所需要的核心代码如下所示。

    //启用剪裁测试
    gl.enable(gl.SCISSOR_TEST);
    //设置剪裁区域
    gl.scissor(0,100, 350, 300);
    //禁用剪裁测试
    gl.disable(gl.SCISSOR_TEST);

gl.scissor函数前两个参数是矩形视口的左下角坐标,三四参数是长宽

下面我们通过一个demo看看裁剪测试的实际效果

 //设置屏幕背景色RGBA
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        //清除着色缓冲与深度缓冲
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        //设置投影参数
        ms.setProjectOrtho(-1.5, 1.5, -1, 1, 1, 300);
        //设置摄像机
        ms.setCamera(0, 0, 0, 0, 0, -1, 0, 1, 0);
        //保护现场
        ms.pushMatrix();
        //执行平移
        ms.translate(0, -0.4, -25);
        ms.scale(0.025, 0.025, 0.025);
        //执行绕Y轴旋转
        ms.rotate(currentYAngle, 0, 1, 0);
        //执行绕X轴旋转
        ms.rotate(currentXAngle, 1, 0, 0);
        //绘制物体
        ooTri.drawSelf(ms, texMap["ghxp"]);
        //绘制副视角场景=============================================begin=========================             
        //启用剪裁测试
        gl.enable(gl.SCISSOR_TEST);
        //设置剪裁区域
        gl.scissor(0, 1080 - 300, 350, 300);
        //设置屏幕背景色RGBA
        gl.clearColor(0.7, 0.7, 0.7, 1.0);
        //清除颜色缓存于深度缓存
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        //调用此方法计算产生透视投影矩阵setProjectOrtho
        ms.setProjectOrtho(-0.17 * 1.5, 1.83 * 1.5, -1.7, 0.30, 2, 100); //-0.62f*ratio, 1.38f*ratio, -1.55f, 0.45f,
        //调用此方法产生摄像机9参数位置矩阵
        ms.setCamera(0, 60, -25, 0, -0.4, -25, 0, 0.0, -1.0);
        ms.scale(0.25, 0.25, 0.25);
        //绘制物体
        ooTri.drawSelf(ms, texMap["ghxp"]);
        //禁用剪裁测试
        gl.disable(gl.SCISSOR_TEST);
        //绘制副视角场景=============================================end=========================
        //恢复现场
        ms.popMatrix();

效果如下: 裁切测试.gif

模板测试

通过上面的裁剪测试可以将绘制限制在一个矩形中,但如果想将绘制限定在任意形状区域内,裁剪测试就无能为力了,此时就需要模板测试了。

使用模板测试所需要的核心代码如下所示。

    //清除着色缓冲与深度缓冲
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    //允许模板测试
    gl.enable(gl.STENCIL_TEST);
    //设置模板测试参数
    gl.stencilFunc(gl.ALWAYS, 1, 1);
    //设置模板测试后的操作
    gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
    //禁用模板测试
    gl.disable(gl.STENCIL_TEST);

stencilFunc的gl.ALWAYS代表总是通过模板测试,第二个参数是参考值,第三个是掩码。

gl.stencilOp的gl.KEEP 模板测试不通过的时候要保持不变,第二个参数代表模板测试沟通但深度测试未通过时,片源的效果,第三个是都通过的效果,使用测试条件设置的值来显示。

下面我们通过一个demo看看裁剪测试的实际效果

        //清除着色缓冲与深度缓冲
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        //清除模板缓存
        gl.clear(gl.STENCIL_BUFFER_BIT);
        //关闭深度检测
        gl.disable(gl.DEPTH_TEST);
        //允许模板测试
        gl.enable(gl.STENCIL_TEST);

        /**
         * 首先打开模板测试 绘制反射地板 用于初始化模板缓冲的模板值
         * 利用产生的模板值 在规定区域中绘制镜像物体
         * 关闭模板测试后 绘制正常的地板
         * 最后在绘制实际物体*/

        //设置模板测试参数
        gl.stencilFunc(gl.ALWAYS, 1, 1);
        //设置模板测试后的操作
        gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
        ms.pushMatrix();
        ms.scale(0.3, 0.3, 0.3);
        //绘制反射面地板
        rectdb.drawSelf(ms, texMap["db"]);
        ms.popMatrix();

        //设置模板测试参数
        gl.stencilFunc(gl.EQUAL, 1, 1);
        //设置模板测试后的操作
        gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
        //绘制镜像体
        drawmirror();
        //禁用模板测试
        gl.disable(gl.STENCIL_TEST);
        //开启混合
        gl.enable(gl.BLEND);
        //设置混合因子
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
        ms.pushMatrix();
        ms.scale(0.3, 0.3, 0.3);
        //绘制半透明反射面地板
        rectdb.drawSelf(ms, texMap["tm"]);
        ms.popMatrix();
        //开启深度检测
        gl.enable(gl.DEPTH_TEST);
        //关闭混合
        gl.disable(gl.BLEND);

        //绘制实际物体
        drawball();

效果如下: 模板测试.gif

本章小结 通过上面两个功能,我们可以用来实现鹰眼地图效果和水面倒影的效果,可是场景更加的真实些。