模板测试和深度测试在cocoscreator中的应用

news2024/11/26 18:40:01

模板测试(Stencil Test):

当片段着色器处理完一个片段之后,模板测试(Stencil Test)会开始执行,和深度测试一样,它也可能会丢弃片段。接下来,被保留的片段会进入深度测试,它可能会丢弃更多的片段。模板测试是根据又一个缓冲来进行的,它叫做模板缓冲(Stencil Buffer),我们可以在渲染的时候更新它来实现一些很有意思的效果。

一个模板缓冲中,(通常)每个模板值(Stencil Value)是8位的。所以每个像素/片段一共能有256种不同的模板值。我们可以将这些模板值设置为我们想要的值,然后当某一个片段有某一个模板值的时候,我们就可以选择丢弃或是保留这个片段了。

模板缓冲的一个简单的例子如下:

模板缓冲首先会被清除为0,之后在模板缓冲中使用1填充了一个空心矩形。场景中的片段将会只在片段的模板值为1的时候会被渲染(其它的都被丢弃了)。 

每个窗口库都需要为你配置一个模板缓冲。GLFW自动做了这件事,所以我们不需要告诉GLFW来创建一个,但其它的窗口库可能不会默认给你创建一个模板库,所以记得要查看库的文档。

测试和混合发生在渲染流程中的最后一个阶段,在这个阶段里,GPU主要的工作是逐片元操作,将片元的颜色以某种形式合并,得到最终在屏幕上显示的像素颜色。

在webgl中的测试有裁剪测试、透明的测试、模板测试以及深度测试。这几个测试都是高度可配置的,测试流程如下图:

模板缓冲: 

通常用户在启用模板缓冲的时候,会将整个模板缓冲中的所有片段模板值设置为0,从而丢弃所有的片段,然后再设置特定区域的模板值以及比较函数。GPU会读取用户设置的模板值,然后将该值和模板缓冲中该位置的模板值,按比较函数进行比较,最终决定是保留还是舍弃该片段,形成遮罩效果。在模板测试中有两个很重要的方法是stencilFunc和stencilOp,stencilFunc用来控制stencil的测试方式,得出测试结果。stencilOp根据结果决定要如何处理缓冲中的数据。

glStencilFunc :

glStencilFunc(GLenum func, GLint ref, GLuint mask)一共包含三个参数:

  • func:设置模板测试函数(Stencil Test Function)。这个测试函数将会应用到已储存的模板值上和glStencilFunc函数的ref值上。可用的选项有:GL_NEVER、GL_LESS、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL和GL_ALWAYS。它们的语义和深度缓冲的函数类似。
  • ref:设置了模板测试的参考值(Reference Value)。模板缓冲的内容将会与这个值进行比较。
  • mask:设置一个掩码,它将会与参考值和储存的模板值在测试比较它们之前进行与(AND)运算。初始情况下所有位都为1。

测试的时候,ref会先和mask做与运算,再将模板缓冲中的值与mask做与运算,最后把这两个与运算的值,代入比较函数得出结果。

所有的比较函数如下:

举个例子:

glStencilFunc(gl.GEQUAL, 1, 0xFF)

 先将参考值1与0xff做与运算得到运算结果:1,将运算结果再与"模板缓冲和0xff进行与运算的值"进行比较。判断是否满足前者大于后者,如果是的话模板测试成功,否则失败。

mask值设为0xff的时候,就等于直接拿参考值和模板缓冲值做比较。

想要禁用模板也可以将mask设置为0x00

经历了glStencilFunc之后,我们就知道模板测试是不是通过。接下来就要对模板缓冲进行操作,这就需要 glStencilOp这个函数了。

glStencilOp:

glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)一共包含三个选项,我们能够设定每个选项应该采取的行为:

  • sfail:模板测试失败时采取的行为。
  • dpfail:模板测试通过,但深度测试失败时采取的行为。
  • dppass:模板测试和深度测试都通过时采取的行为。

每个选项都可以选用以下的其中一种行为:

行为描述
gl.KEEP保持当前储存的模板值
gl.ZERO将模板缓冲值设置为0
gl.REPLACE将模板缓冲区值设置为glStencilFunc函数设置的ref
gl.INCR如果模板缓冲值小于最大值则将模板值加1
gl.INCR_WRAP与GL_INCR一样,但如果模板缓冲值超过了最大值则归零
gl.DECR如果模板缓冲值大于最小值则将模板值减1
gl.DECR_WRAP与GL_DECR一样,但如果模板缓冲值小于0则将其设置为最大值
gl.INVERT按位翻转当前的模板缓冲值

默认情况下glStencilOp是设置为(gl.KEEP, gl.KEEP, gl.KEEP)的,所以不论测试的结果是什么,模板缓冲都会保留它的值。默认的行为不会更新模板缓冲,所以如果你想写入模板缓冲的话你需要至少对其中一个选项设置不同的值。

通常我们这样设置:glStencilOp(gl..KEEP, gl.KEEP,gl.REPLACE),测试失败时保持原有值(KEEP),测试通过的参考值替换模板缓冲值(REPLACE

在webgl中模板测试默认是处于禁用状态,使用时需要手动开启,我们采用gl.enable来开启:

gl.enable(gl.STENCIL_TEST);

注意,和颜色和深度缓冲一样,我们也需要在每帧绘制之前清除模板缓冲。

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

如果想自己在webgl上写模板测试,需要在获取gl上下文对象时传入stencil的请求:

const gl = canvas.getContext("webgl",{stencil:true});

深度测试(Depth Test):

深度测试可以帮助实现3D渲染的物体遮挡效果 ,

深度缓冲就像颜色缓冲(Color Buffer)(储存所有的片段颜色)一样,在每个片段中储存了信息,并且(通常)和颜色缓冲有着一样的宽度和高度。深度缓冲是由窗口系统自动创建的,它会以16、24或32位float的形式储存它的深度值。在大部分的系统中,深度缓冲的精度都是24位的。

 当深度测试被启用的时候,OpenGL会将一个片段的深度值与深度缓冲的内容进行对比。OpenGL会执行一个深度测试,如果这个测试通过了的话,深度缓冲将会更新为新的深度值。如果深度测试失败了,片段将会被丢弃。

深度缓冲是在片段着色器运行之后(以及模板测试运行之后)在屏幕空间中运行的。屏幕空间坐标与通过OpenGL的glViewport所定义的视口密切相关,并且可以直接使用GLSL内建变量gl_FragCoord从片段着色器中直接访问。gl_FragCoord的x和y分量代表了片段的屏幕空间坐标(其中(0, 0)位于左下角)。gl_FragCoord中也包含了一个z分量,它包含了片段真正的深度值z值就是需要与深度缓冲内容所对比的那个值。

深度测试默认是禁用的,所以如果要启用深度测试的话,我们需要用GL_DEPTH_TEST选项来启用它:

glEnable(GL_DEPTH_TEST);

当它启用的时候,如果一个片段通过了深度测试的话,OpenGL会在深度缓冲中储存该片段的z值;如果没有通过深度缓冲,则会丢弃该片段。如果你启用了深度缓冲,你还应该在每个渲染迭代之前使用GL_DEPTH_BUFFER_BIT来清除深度缓冲,否则你会仍在使用上一次渲染迭代中的写入的深度值:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

如果在某些情况下你会需要对所有片段都执行深度测试并丢弃相应的片段,但希望更新深度缓冲。也就是你在使用一个只读的(Read-only)深度缓冲。OpenGL允许我们禁用深度缓冲的写入,只需要设置它的深度掩码(Depth Mask)设置为GL_FALSE就可以了:

glDepthMask(GL_FALSE);

注意这只在深度测试被启用的时候才有效果。

深度测试函数

OpenGL允许我们修改深度测试中使用的比较运算符。这允许我们来控制OpenGL什么时候该通过或丢弃一个片段,什么时候去更新深度缓冲。我们可以调用glDepthFunc函数来设置比较运算符(或者说深度函数(Depth Function)):

glDepthFunc(GL_LESS);

这个函数接受下面表格中的比较运算符:

函数描述
GL_ALWAYS永远通过深度测试
GL_NEVER永远不通过深度测试
GL_LESS在片段深度值小于缓冲的深度值时通过测试
GL_EQUAL在片段深度值等于缓冲区的深度值时通过测试
GL_LEQUAL在片段深度值小于等于缓冲区的深度值时通过测试
GL_GREATER在片段深度值大于缓冲区的深度值时通过测试
GL_NOTEQUAL在片段深度值不等于缓冲区的深度值时通过测试
GL_GEQUAL在片段深度值大于等于缓冲区的深度值时通过测试

默认情况下使用的深度函数是GL_LESS,它将会丢弃深度值大于等于当前深度缓冲值的所有片段。

 如果想自己在webgl上写深度测试,需要在获取gl上下文对象时传入depth的请求:

const gl = canvas.getContext("webgl",{stencil:true,depth:true});

Creator中使用深度、模板测试:

打开TS引擎源码文件cocos\gfx\webgl\webgl-swapchain.ts(低版本打开cocos\gfx\webgl\webgl-device.ts)

有个initStates函数,引擎在这里初始化depth、stencil state:


function initStates (gl: WebGLRenderingContext) {
    gl.activeTexture(gl.TEXTURE0);
    gl.pixelStorei(gl.PACK_ALIGNMENT, 1);
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);

    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    // rasterizer state
    gl.enable(gl.SCISSOR_TEST);
    gl.enable(gl.CULL_FACE);
    gl.cullFace(gl.BACK);
    gl.frontFace(gl.CCW);
    gl.disable(gl.POLYGON_OFFSET_FILL);
    gl.polygonOffset(0.0, 0.0);

    // depth stencil state
    gl.enable(gl.DEPTH_TEST);
    gl.depthMask(true);
    gl.depthFunc(gl.LESS);
    gl.depthRange(0.0, 1.0);

    gl.stencilFuncSeparate(gl.FRONT, gl.ALWAYS, 1, 0xffff);
    gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.KEEP);
    gl.stencilMaskSeparate(gl.FRONT, 0xffff);
    gl.stencilFuncSeparate(gl.BACK, gl.ALWAYS, 1, 0xffff);
    gl.stencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.KEEP);
    gl.stencilMaskSeparate(gl.BACK, 0xffff);

    gl.disable(gl.STENCIL_TEST);

    // blend state
    gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
    gl.disable(gl.BLEND);
    gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
    gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO);
    gl.colorMask(true, true, true, true);
    gl.blendColor(0.0, 0.0, 0.0, 0.0);
}

以上初始化的配置,主要针对3D对象,因为2D对象大多数包含透明像素,因此2D管线不需要进行深度测试,比如:对于builtin-sprite.effect这类2d shader中对应深度测试部分,都被默认关闭了:

除了可以在effect文件中初始化深度模板测试 , creator编辑器还可以在material文件中修改:

所有深度、模板测试相关的配置都可以在Pipeline States下修改。

下面我们做个案例:

 如图,结合相机位置和右下角相机拍摄出的画面来看,由于长方体柱子的深度值比其他模型的深度值要小,(由于深度测试函数是GL_LESS,深度值小的通过测试)使得长方体背后的模型的片段被剔除了,因此长方体遮挡住了其他物体(包括地面)。接下来我们对长方体的深度测试做一些配置修改,再看看显示效果:

1.关闭长方体模型材质中的的深度测试:

关闭长方体的深度测试表现的效果: 

 长方体关闭了深度测试后,没有了深度值,在它和其他模型重叠的地方,会被有深度的模型片段填充。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/993088.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React 开发一个移动端项目(1)

技术栈: 项目搭建:React 官方脚手架 create-react-appreact hooks状态管理:redux 、 redux-thunkUI 组件库:antd-mobileajax请求库:axios路由:react-router-dom 以及 historyCSS 预编译器:sass…

Web开发后端总结

Web后端开发现在基本上都是基于标准的三层架构进行开发的,在三层架构当中,Controller控制器层 - 负责接收请求响应数据,Service - 业务层负责具体的业务逻辑处理,而Dao - 数据访问层也叫持久层,就是用来处理数据访问操…

第9节-PhotoShop基础课程-移动抓手缩放工具

文章目录 前言1. 移动工具1.移动工具1.自动选择(图层和组)2.显示变换控件 (Shift 变换/ Ctrl 变换)3.自由变换 Ctrl T (Shift 变换/ Ctrl 变换)4.对齐功能 2.画板工具 V1. 创建画板并作图2.导出画板 2.路…

【strcpy函数和strncpy函数的对比与模拟实现】

strcpy函数和strncpy函数的对比与模拟实现 1.strcpy函数介绍 资源来源于cplusplus网站 大致意思就是: 它的作用为: 将一个字符串复制到另一块空间地址中 的函数,‘\0’是停止拷贝的终止条件,同时也会将 ‘\0’ 也复制到目标空间…

redis集群最少使用三个主节点和使用16384个槽以及主节点数量不超过1000的原因

目录 集群最少三个主节点的原因 为什么是三个? 为什么是奇数? 16384个槽和1000个主节点 集群最少三个主节点的原因 https://redis.io/docs/management/scaling/ 官网建议,搭建 redis 集群最少三主三从。 但是这么做是出于什么考虑呢? https://workt…

02深度学习目标检测方法介绍-传统

一、目标学习的检测方法变迁及对比 “目标检测“是当前计算机视觉和机器学习领域的研究热点。从Viola-Jones Detector、DPM等冷兵器时代的智慧到当今RCNN、YOLO等深度学习土壤孕育下的GPU暴力美学,整个目标检测的发展可谓是计算机视觉领域的一部浓缩史。整个目标检测…

Python实操 PDF自动识别并提取Excel文件

最近几天,paddleOCR开发了新的功能,通过将图片中的表格提取出来,效果还不错,今天,作者按照步骤测试了一波。 首先,讲下这个工具是干什么用的:它的功能主要是针对一张完整的PDF图片,可…

EasyAVFilter代码示例之将摄像机RTSP流转成RTMP推流输出

以下是一套完整的RTSP流转RTMP推流功能的开发源码,就简简单单几行代码,就可以完成原来ffmpeg很复杂的调用流程,而且还可以集成在自己的应用程序中调用,不需要再单独一个ffmpeg的进程来调用,方法很简单: #i…

spring中的@Configuration配置类和@Component

在Spring的开发工作中,基本都会使用配置注解,尤其以Component及Configuration为主,当然在Spring中还可以使用其他的注解来标注一个类为配置类,这是广义上的配置类概念,但是这里我们只讨论Component和Configuration&…

盲盒小程序开发方案

盲盒游戏作为一种富有趣味性和收藏价的虚拟盲盒产品,近年来在游戏市场中备受关注。本文将深入探讨盲盒游戏的开发方案,从市场趋势分析、用户体验设计、商业模式选择等多个维度,为开发者提供业且有深度的思考,以帮助他们在盲盒游戏…

容器编排学习(八)卷的概述与存储卷管理

一 卷 1 容器化带来的问题 容器中的文件在磁盘上是临时存放的,这给容器中运行的重要的应用程序带来一些问题 问题1:当容器崩溃或重启的时候,kubelet 会以干净的状态(镜像的状态)重启容器,容器内的历史数据会丢失 问题2&…

upload-labs1-17思路

1 直接写一个php文件测试一下,发现弹窗不让上传 原理很简单,就是把后缀名拿出来过滤一遍,而白名单就是弹窗的这三个 解决方法: 因为这是在前端防御的一个手段,所以直接在浏览器设置上禁用js就行了: 也可…

数据结构与算法_栈

一、什么是栈 栈是一种特殊的线性表,它只允许在固定的一端进行插入和删除的操作。 对数据进行插入和删除的一端叫做栈顶,另一端是栈底。 对栈的两项操作分别叫做入栈、出栈。 入栈就是对栈进行插入操作,除此之外,入栈也叫做进…

2023挖漏洞给报酬的网站汇总,兼职副业3天收益2000

一、众测平台(国内)二、前沿漏洞研究奖励计划三、行业SRC四、企业应急响应中心-SRC-汇总 1、互联网企业2、生活服务、住宿、购物相关企业3、物流、出行、旅游4、金融相关企业5、视频游戏直播社交娱乐6、教育、问答、知识付费7、泛科技通讯物联网云服务8、安全企业9、其他 一、…

渗透测试的八大步骤

渗透测试 其实很多安全漏洞都属于Web应用漏洞,这些Web漏洞可以通过渗透测试验证。渗透测试是利用模拟黑客攻击的方式,评估计算机网络系统安全性能的一种方法。这个过程是站在攻击者角度对系统的任何弱点、技术缺陷或漏洞进行主动分析,并且有…

【深度学习】Mini-Batch梯度下降法

Mini-Batch梯度下降法 在开始Mini-Batch算法开始之前,请确保你已经掌握梯度下降的最优化算法。 在训练神经网络时,使用向量化是加速训练速度的一个重要手段,它可以避免使用显式的for循环,并且调用经过大量优化的矩阵计算函数库。…

Vulnstack红队(一)

刚开始了解内网的时候做的,可能细节有问题的地方会比较多 0 - 环境配置 Vmware虚拟网络配置 三个靶机设置 Win7(添加一张网卡) VMnet1VMnet2Win2003 VMnet1Win2008 VMnet1 攻击机(kali)设置 VMnet2 网络拓扑 kali …

当电脑遇到msvcp110.dll丢失怎么办?最新解决方法分享

在使用电脑过程中,我们经常会遇到一些系统文件丢失的问题。其中,msvcp110.dll是Windows操作系统中的一个重要的动态链接库文件,它包含了许多与C运行库有关的函数和类。当系统中缺少或损坏这个文件时,可能会导致一些应用程序无法正…

细谈left join和join

👏作者简介:大家好,我是爱发博客的嗯哼,爱好Java的小菜鸟 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦 📝社区论坛:希望大家能加入社区共同进步…

OpenCV 10(图像轮廓)

一、图像轮廓 图像轮廓是具有相同颜色或灰度的连续点的曲线. 轮廓在形状分析和物体的检测和识别中很有用。 轮廓的作用: - 用于图形分析 - 物体的识别和检测 注意点: - 为了检测的准确性,需要先对图像进行**二值化**或**Canny操作**。 - 画轮廓时会修改输入的图像…