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

相关文章

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

Python利用自带模块实现屏幕像素高效操作

《Python利用自带模块实现屏幕像素高效操作》这篇文章主要为大家详细介绍了Python如何利用自带模块实现屏幕像素高效操作,文中的示例代码讲解详,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、获取屏幕放缩比例2、获取屏幕指定坐标处像素颜色3、一个简单的使用案例4、总结1、获取屏幕放缩比例from

通过prometheus监控Tomcat运行状态的操作流程

《通过prometheus监控Tomcat运行状态的操作流程》文章介绍了如何安装和配置Tomcat,并使用Prometheus和TomcatExporter来监控Tomcat的运行状态,文章详细讲解了... 目录Tomcat安装配置以及prometheus监控Tomcat一. 安装并配置tomcat1、安装

Python中操作Redis的常用方法小结

《Python中操作Redis的常用方法小结》这篇文章主要为大家详细介绍了Python中操作Redis的常用方法,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解一下... 目录安装Redis开启、关闭Redisredis数据结构redis-cli操作安装redis-py数据库连接和释放增

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言