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

相关文章

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与

Java Multimap实现类与操作的具体示例

《JavaMultimap实现类与操作的具体示例》Multimap出现在Google的Guava库中,它为Java提供了更加灵活的集合操作,:本文主要介绍JavaMultimap实现类与操作的... 目录一、Multimap 概述Multimap 主要特点:二、Multimap 实现类1. ListMult

Python中文件读取操作漏洞深度解析与防护指南

《Python中文件读取操作漏洞深度解析与防护指南》在Web应用开发中,文件操作是最基础也最危险的功能之一,这篇文章将全面剖析Python环境中常见的文件读取漏洞类型,成因及防护方案,感兴趣的小伙伴可... 目录引言一、静态资源处理中的路径穿越漏洞1.1 典型漏洞场景1.2 os.path.join()的陷

Python使用Code2flow将代码转化为流程图的操作教程

《Python使用Code2flow将代码转化为流程图的操作教程》Code2flow是一款开源工具,能够将代码自动转换为流程图,该工具对于代码审查、调试和理解大型代码库非常有用,在这篇博客中,我们将深... 目录引言1nVflRA、为什么选择 Code2flow?2、安装 Code2flow3、基本功能演示

Python中OpenCV与Matplotlib的图像操作入门指南

《Python中OpenCV与Matplotlib的图像操作入门指南》:本文主要介绍Python中OpenCV与Matplotlib的图像操作指南,本文通过实例代码给大家介绍的非常详细,对大家的学... 目录一、环境准备二、图像的基本操作1. 图像读取、显示与保存 使用OpenCV操作2. 像素级操作3.

python操作redis基础

《python操作redis基础》Redis(RemoteDictionaryServer)是一个开源的、基于内存的键值对(Key-Value)存储系统,它通常用作数据库、缓存和消息代理,这篇文章... 目录1. Redis 简介2. 前提条件3. 安装 python Redis 客户端库4. 连接到 Re

Java Stream.reduce()方法操作实际案例讲解

《JavaStream.reduce()方法操作实际案例讲解》reduce是JavaStreamAPI中的一个核心操作,用于将流中的元素组合起来产生单个结果,:本文主要介绍JavaStream.... 目录一、reduce的基本概念1. 什么是reduce操作2. reduce方法的三种形式二、reduce

MySQL表空间结构详解表空间到段页操作

《MySQL表空间结构详解表空间到段页操作》在MySQL架构和存储引擎专题中介绍了使用不同存储引擎创建表时生成的表空间数据文件,在本章节主要介绍使用InnoDB存储引擎创建表时生成的表空间数据文件,对... 目录️‍一、什么是表空间结构1.1 表空间与表空间文件的关系是什么?️‍二、用户数据在表空间中是怎么

Python对PDF书签进行添加,修改提取和删除操作

《Python对PDF书签进行添加,修改提取和删除操作》PDF书签是PDF文件中的导航工具,通常包含一个标题和一个跳转位置,本教程将详细介绍如何使用Python对PDF文件中的书签进行操作... 目录简介使用工具python 向 PDF 添加书签添加书签添加嵌套书签Python 修改 PDF 书签Pytho