OpenGL ES 片元操作

2023-10-09 02:30
文章标签 es 操作 opengl 片元

本文主要是介绍OpenGL ES 片元操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • 剪裁测试
  • 模板测试
  • 深度测试
  • 混合

片元着色器后续操作还包括剪裁测试、模板测试、深度测试、混合等,最终才会被送到帧缓冲区。

剪裁测试

剪裁测试可以在渲染时用来限制绘制区域,通过制定一个矩阵进一步限制帧缓冲区可以写入的像素,启用剪裁测试后,绘制不会在整个屏幕(帧缓冲区)进行,而是在指定的矩形区域进行。不在矩形区域中的片元被丢弃,在矩形区域内的片元才能被送往帧缓冲区,实际效果就是在屏幕上开辟了一个小窗口。剪裁矩形确定的范围内的视口区域的物体才会最终进入帧缓冲区。

使用函数glScissor指定剪裁区域

public static native void glScissor(int x, int y,  // 以视口坐标指定的剪裁矩形左下角int width, // 剪裁矩形像素宽度int height // 剪裁矩形像素高度);

指定剪裁矩形后,需要通过glEnable(GL_SCISSOR_TEST)函数启用剪裁测试,启用后所有的渲染操作都只对剪裁矩形生效。

模板测试

模板测试是对每一帧中的每个像素进行测试,通过和一个模板指定的对应像素进行对比决定该像素是否能通过测试,模板测试就是通过模板缓冲区中记录的模板信息完成的,首先需要初始化模板缓冲区,这可以通过渲染几何形状并制定模板缓冲区的更新方式来完成;接下来就可以使用指定的模板缓冲区中的模板信息进行其他的绘制了。

模板测试原理,比如模板缓冲区如图所示,当绘制图形时设置模板缓冲区值等于1时才绘制的话,那么接下来绘制的时候就只会在模板缓冲区设置为1的部分绘制,其他位置的片元都会被丢弃。
模板测试原理
使用glEnable(GL_STENCIL_TEST)函数启用模板测试,同时使用glClear和glClearStencil设置模板缓冲区模板值。

使用函数glStencilFunc控制模板测试的运算符

public static native void glStencilFunc(int func, // 指定模板测试的比较函数int ref,  // 指定模板测试比较值int mask  // 指定在于参考值比较之前与模板缓冲区中的值进行&运算);

比较函数取值

比较函数含义
GL_NEVER从不通过模板测试
GL_ALWAYS总是通过模板测试
GL_LESS只有参考值<(模板缓存区的值&mask)时才通过
GL_LEQUAL只有参考值<=(模板缓存区的值&mask)时才通过
GL_EQUAL只有参考值=(模板缓存区的值&mask)时才通过
GL_GEQUAL只有参考值>=(模板缓存区的值&mask)时才通过
GL_GREATER只有参考值>(模板缓存区的值&mask)时才通过
GL_NOTEQUAL只有参考值!=(模板缓存区的值&mask)时才通过

设置了模板测试的方式后,还需要设置模板测试通过或是未通过时OpenGL ES将采取什么样的操作,使用glStencilOp函数进行设置。

public static native void glStencilOp(int fail,  // 指定片元未通过模板测试时,对应模板缓冲区模板值(像素)的操作int zfail,  // 指定片元通过模板测试但没有通过深度测试时的操作int zpass  // 既通过了模板测试又通过了深度测试时执行的操作);

对模板缓冲区中的模板值(像素值)所执行的操作

模板操作含义
GL_KEEP保持当前的模板缓存区值
GL_ZERO把当前的模板缓存区值设为0
GL_REPLACE用glStencilFunc函数所指定的参考值替换模板参数值
GL_INCR/GL_INCR_WRAP增加当前的模板缓存区值,已经是最大值保持不变/变为0
GL_DECR/GL_DECR_wrap减少当前的模板缓存区值,已经是最大值保持不变/变为0
GL_INVERT将当前的模板缓存区值进行逐位的翻转

利用模板测试可以达到剪裁效果,并且可以实现不规则的剪裁。

  • 开启模板测试,使用glClear方法设置模板缓存区中的模板值都为0,然后设置模板测试的操作。
GLES20.glStencilFunc(GLES20.GL_ALWAYS, 1, 1); // 总是通过
GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, GLES20.GL_REPLACE); // 通过测试未设置深度测试,认为也通过深度测试,因此是GL_REPLACE,即用glStencilFunction设置的参考值1来代替深度缓冲区中的值

进行上面的设置后再绘制不规则的剪裁区域,绘制玩该剪裁区域后,该区域对应的模板缓冲区的模板值为1,其他区域对应的模板缓冲区中的模板值为0,

  • 重新设置模板测试参数
GLES20.glStencilFunc(GLES20.GL_EQUAL, 1, 1); // 只有模板缓冲区中的模板值为1的地方才被绘制
GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, GLES20.GL_KEEP);

进行上面的设置后,只有模板缓冲区中的模板值为1的地方才被绘制,也就是只有处于该不规则的图形范围内的部分会被绘制,其他位置的片元都会被丢弃。

绘制一个矩形,用该矩形产生该区域内的模板缓冲区的数据,然后再设置符合需求的模板参数设置,比如模板值为1时通过测试,再绘制三角形。关键代码在onDrawFrame函数中

GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_STENCIL_BUFFER_BIT);
Matrix.setIdentityM(mModuleMatrix, 0);
Matrix.rotateM(mModuleMatrix, 0, xAngle, 1, 0, 0);
Matrix.rotateM(mModuleMatrix, 0, yAngle, 0, 1, 0);
Matrix.multiplyMM(mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mViewProjectionMatrix, 0, mModuleMatrix, 0);
// 未开启模板测试
if (!openStencilTest) {mShape.drawRanctangle(mMVPMatrix);mShape.drawTrangle(mMVPMatrix);
} else {// 使用模板测试GLES20.glEnable(GLES20.GL_STENCIL_TEST);GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT);GLES20.glStencilFunc(GLES20.GL_ALWAYS, 1, 0xff); // 总是通过GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, GLES20.GL_REPLACE);mShape.drawRanctangle(mMVPMatrix);GLES20.glStencilFunc(GLES20.GL_EQUAL, 1, 0xff); // 只有模板缓冲区中的模板值为1的地方才被绘制GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, GLES20.GL_KEEP);mShape.drawTrangle(mMVPMatrix);GLES20.glDisable(GLES20.GL_STENCIL_TEST);
}

产生的的效果如图所示
模板测试

代码

深度测试

深度就是像素点在距离摄象机的距离,对应的也有一个深度缓冲区存储着每个像素点(绘制在屏幕上的)的深度值,深度值越大,表示离摄像机越远。深度缓冲区中保存着渲染表面上每个像素与视点最近物体的距离值。

如果没有深度缓冲区,当要绘制多个物体时,必须先绘制远处的物体再绘制近处的物体,因为如果先绘制近处的物体,那么后绘制的远处的物体会覆盖掉近处的物体。而有了深度缓冲区后,绘制的顺序就不重要了,因为深度缓冲区代表了物体离视点的距离

深度测试的默认做法:对于每个新的输入片元,将其与视点的距离和深度缓冲区中的值进行比较,如果输入的片元的深度值小于深度缓冲区中的深度值,则表示它离视点更近,则输入片元的深度将代替深度缓冲区中的值,其颜色值将代替颜色缓冲区中的颜色值。否则,表示当前要绘制的片元在已绘制的部分物体后面,则无需绘制该图形,即丢弃。

启用深度测试glEnable(GL_DEPTH_TEST)

  • glClear(GL_DEPTH_BUFFER_BIT) 清空 Depth Buffer (赋值为1.0),通常清空 Depth Buffer 和 Color Buffer 同时进行。
  • glClearDepthf(float depth) 指定清空 Depth Buffer 是使用的值,缺省为 1.0,通常无需改变这个值。

混合

混合就是把某一像素位置原来的颜色和将要画上去的颜色通过某种方式混在一起。 即某个片元的颜色(源片元)和颜色缓冲区中的像素颜色(目标片元中的像素颜色)进行混合。

Cfinal=fsourceCsourceopfdestinationCdestination

其中, fsource Csource 分表表示输入的片元的比例因子和颜色, fdestination Cdestination 分表表示颜色缓冲区中的对应位置的像素比例因子和颜色

使用函数glBlendFunc和glBlendFuncSeparate设置混合因子

public static native void glBlendFunc(int sfactor, // 源片元的混合因子int dfactor  // 目标像素的混合因子);
public static native void glBlendFuncSeparate(int srcRGB,  // 源片元的RGB颜色分量的混合因子int dstRGB,  // 目标像素的RGB颜色分量混合因子int srcAlpha,  // 源片元的alpha值的混合因子int dstAlpha   // 目标像素的alpha值的混合因子);

混合因子

混合系数枚举值RGB混合因子Alpha混合因子
GL_ZERO[0,0,0]0
GL_ONE[1,1,1]1
GL_SRC_COLOR[Rs,Gs,Bs]As
GL_ONE_MINUS_SRC_COLOR[1-Rs,1-Gs,1-Bs]1-As
GL_DST_COLOR[Rd,Gd,Bd]Ad
GL_ONE_MINUS_DST_COLOR[1-Rd,1-Gd,1-Bd]1-Ad
GL_SRC_ALPHA[As,As,As]As
GL_SRC_MINUS_SRC_ALPHA[1-As,1-As,1-As]1-As
GL_DST_ALPHA[Ad,Ad,Ad]Ad
GL_SRC_MINUS_DST_ALPHA[1-Ad,1-Ad,1-Ad]1-Ad
GL_SRC_ALPHA_STAURATE[f,f,f] f=min(As,1-Ad)1

(Rs, Gs, Bs, As)是与输入片段颜色相关的颜色分量,(Rd, Gd , Bd, Ad)是与输入片段颜色相关的颜色分量, (Rc,Gc,Bc,Ac)是通过 glBlendColor 设定的颜色常量。 使用 GL_SRC_ALHPA_SATURATE时,计算出来的最小值只适用于源颜色。

public static native void glBlendColor(float red, float green, float blue, float alpha // 指定常量混合颜色的分量值);

混合因子取值含义

  • glBlendFunc(GL_ONE, GL_ZERO) 只取源颜色,这也是默认值。
  • glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 典型的半透明遮挡效果,如果源色 alpha 为0,则取目标色,如果源色alpha为1,则取源色,最终绘制物体的颜色与物体的透明度有关。
  • glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR) 实现滤镜效果,透过有色眼镜观察事物的效果。

输入片元和像素颜色乘以各自的混合因子后,由函数glBlendEquation和glBlendEquationSeparate指定的运算来进行组合。默认情况下,混合后的颜色使用GL_FUNC_ADD运算进行加和。

public static native void glBlendEquation(int mode  // 对源片元和目标像素的操作);
public static native void glBlendEquationSeparate(int modeRGB,  // RGB分量的混合操作int modeAlpha  // alpha分量的混合操作);

上面的操作可以除了GL_FUNC_ADD还可以使用GL_FUNC_SUBTRACT、GL_FUNC_REVERSE_SUBTRACT,GL_FUNC_SUBTRACT表示从输入的片元值中减去颜色缓冲区中的像素值,GL_FUNC_REVERSE_SUBTRACT表示从当前颜色缓冲区中的像素值减去输入片元的颜色。

在上面模板测试的代码的基础上,修改onDrawFrame中增加混合部分的代码

if (!openStencilTest) {mShape.drawRanctangle(mMVPMatrix);GLES20.glEnable(GLES20.GL_BLEND);GLES20.glBlendFunc(GLES20.GL_SRC_COLOR, GLES20.GL_ONE_MINUS_SRC_COLOR);mShape.drawTrangle(mMVPMatrix);GLES20.glDisable(GLES20.GL_BLEND);
} else {GLES20.glEnable(GLES20.GL_STENCIL_TEST);GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT);GLES20.glStencilFunc(GLES20.GL_ALWAYS, 1, 0xff);GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, GLES20.GL_REPLACE);mShape.drawRanctangle(mMVPMatrix);// 开启混合GLES20.glEnable(GLES20.GL_BLEND);// 使用的模式为两个颜色混合GLES20.glBlendFunc(GLES20.GL_SRC_COLOR, GLES20.GL_ONE_MINUS_SRC_COLOR);GLES20.glStencilFunc(GLES20.GL_EQUAL, 1, 0xff);GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, GLES20.GL_KEEP);mShape.drawTrangle(mMVPMatrix);GLES20.glDisable(GLES20.GL_STENCIL_TEST);GLES20.glDisable(GLES20.GL_BLEND);  
}

产生如下的效果
混合

代码

这篇关于OpenGL ES 片元操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/169719

相关文章

Mysql表的简单操作(基本技能)

《Mysql表的简单操作(基本技能)》在数据库中,表的操作主要包括表的创建、查看、修改、删除等,了解如何操作这些表是数据库管理和开发的基本技能,本文给大家介绍Mysql表的简单操作,感兴趣的朋友一起看... 目录3.1 创建表 3.2 查看表结构3.3 修改表3.4 实践案例:修改表在数据库中,表的操作主要

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

Java使用Curator进行ZooKeeper操作的详细教程

《Java使用Curator进行ZooKeeper操作的详细教程》ApacheCurator是一个基于ZooKeeper的Java客户端库,它极大地简化了使用ZooKeeper的开发工作,在分布式系统... 目录1、简述2、核心功能2.1 CuratorFramework2.2 Recipes3、示例实践3

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

Python使用DrissionPage中ChromiumPage进行自动化网页操作

《Python使用DrissionPage中ChromiumPage进行自动化网页操作》DrissionPage作为一款轻量级且功能强大的浏览器自动化库,为开发者提供了丰富的功能支持,本文将使用Dri... 目录前言一、ChromiumPage基础操作1.初始化Drission 和 ChromiumPage

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

Redis中管道操作pipeline的实现

《Redis中管道操作pipeline的实现》RedisPipeline是一种优化客户端与服务器通信的技术,通过批量发送和接收命令减少网络往返次数,提高命令执行效率,本文就来介绍一下Redis中管道操... 目录什么是pipeline场景一:我要向Redis新增大批量的数据分批处理事务( MULTI/EXE

使用Python高效获取网络数据的操作指南

《使用Python高效获取网络数据的操作指南》网络爬虫是一种自动化程序,用于访问和提取网站上的数据,Python是进行网络爬虫开发的理想语言,拥有丰富的库和工具,使得编写和维护爬虫变得简单高效,本文将... 目录网络爬虫的基本概念常用库介绍安装库Requests和BeautifulSoup爬虫开发发送请求解

Oracle存储过程里操作BLOB的字节数据的办法

《Oracle存储过程里操作BLOB的字节数据的办法》该篇文章介绍了如何在Oracle存储过程中操作BLOB的字节数据,作者研究了如何获取BLOB的字节长度、如何使用DBMS_LOB包进行BLOB操作... 目录一、缘由二、办法2.1 基本操作2.2 DBMS_LOB包2.3 字节级操作与RAW数据类型2.

JDK多版本共存并自由切换的操作指南(本文为JDK8和JDK17)

《JDK多版本共存并自由切换的操作指南(本文为JDK8和JDK17)》本文介绍了如何在Windows系统上配置多版本JDK(以JDK8和JDK17为例),并通过图文结合的方式给大家讲解了详细步骤,具有... 目录第一步 下载安装JDK第二步 配置环境变量第三步 切换JDK版本并验证可能遇到的问题前提:公司常