当前位置: 首页 > news >正文

OpenGL 利用 Alpha 透明度进行测试

公众号回复:OpenGL,领取学习资源大礼包

在前面的博客文章中有提到 OpenGL 裁剪测试及注意点,并且裁剪测试只能裁剪一个矩形区域,相当于就是把整个内容都绘制上去了,但是透过一个小矩形区域来看绘制的物体。

除了透过矩形区域,还可以实现透过任意形状区域来观察物体,这就是要用到 OpenGL 的 Alpha 透明度测试。

关于 Alpha 透明度测试,在 用 OpenGL 对视频帧内容进行替换 也用实践用到过。

在 OpenGL 中,每个颜色都有四个色彩通道 --- RGBA,其中 A 就是 Alpha 透明度通道,可以利用它的值进行检测。

Alpha 测试的基本原理为,当绘制一个片元时,首先检测其 Alpha 值,若 Alpha 值满足要求,就通过测试,绘制此片元,否则丢弃此片元,不进行绘制。

比如如下的场景:

如果是裁剪测试,只能屏幕上透过一个矩形区域来观察实际场景。

而换成 Alpha 测试,就可以实现透过任意形状的观察,效果如下:

图上的内容实际由三部分组成,树、地形、中间带透明度的边框。

正因为绘制的边框带透明度,才不会全部遮住后面的内容。

具体绘制代码如下:

 1            GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT or GLES20.GL_COLOR_BUFFER_BIT)
 2            // 打开深度检测
 3             GLES20.glEnable(GLES20.GL_DEPTH_TEST)
 4            // 设置相机和观察矩阵,绘制地形和树
 5            MatrixState.setProjectFrustum(-ratio!!, ratio!!, -1f, 1f, 1f, 100f)
 6            MatrixState.setCamera(cx, 0f, cz, 0f, 0f, 0f, 0f, 1.0f, 0f)
 7
 8            MatrixState.pushMatrix()
 9            MatrixState.translate(0f, -2f, 0f)
10            // 绘制地形
11            desert!!.drawSelf(desertId)
12            MatrixState.popMatrix()
13            // 开启混合,因为树要和地形有混合的地方
14            GLES20.glEnable(GLES20.GL_BLEND)
15            // 设置混合因子
16            GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA)
17            MatrixState.pushMatrix()
18            MatrixState.translate(0f, -2f, 0f)
19            // 绘制树
20            tg!!.drawSelf(treeId)
21            MatrixState.popMatrix()
22            // 关闭混合
23            GLES20.glDisable(GLES20.GL_BLEND)
24            // 清除深度缓冲
25            GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT)
26            // 设置相机和投影矩阵来绘制带透明度的边框
27            MatrixState.pushMatrix()
28            MatrixState.setProjectOrtho(-1f, 1f, -1f, 1f, 1f, 100f)
29            MatrixState.setCamera(0f, 0f, 3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
30            rect!!.drawSelf(maskTextureId)
31            MatrixState.popMatrix()

这里面绘制带透明度的边框时清除了一次深度缓冲,因为带透明度边框不属于主场景,不应该和主场景中的物体以同一标准进行深度检测,否则可能会产生不正确的视觉效果。

具体实现

Alpha 测试的重点在于片段着色器的处理,在这里可以对每一个片段进行处理,根据 Alpha 来决定是否抛弃它。

 1precision mediump float;
 2varying vec2 vTextureCoord; //接收从顶点着色器过来的参数
 3uniform sampler2D sTexture;//纹理内容数据
 4void main() { 
 5    //给此片元从纹理中采样出颜色值 
 6   vec4 bcolor = texture2D(sTexture, vTextureCoord);
 7   if(bcolor.a<0.6) {
 8           discard;
 9   } else {
10      gl_FragColor=bcolor;
11}}

根据具体的图片透明度,这里,如果透明度小于 0.6 ,就抛弃这个片元,否则的话就赋值给原本采样的颜色。

其中,在 GLSL 着色器语言中,discard 代表放弃该片元。

而 texture2D 方法代表纹理采样,它的结果类型是一个向量,向量在着色器代码中的地位非常重要,可以方便地存储以及操作 颜色位置纹理坐标 等不仅包含一个组成部分的量。

对于一个向量,有时需要单独访问向量中的某个分量,基本语法为 <向量名>.<分量名>,从不同的角度来看,它的每个分量代表的含义是不同的:

  • 将向量看作颜色时,可以使用 r、g、b、a 四个分量名,分别代表红、绿、蓝、透明度 4 个色彩通道。

1vec4 aColor;
2aColor.r = 0.6;
3aColor.g = 0.8;

若向量是 4 维的,可以使用的分量名为 r、g、b、a;若向量是 3 维的,可以使用的分量名为 r、g、b;若是 2 维的,可以使用的 r、g 两个分量名。

  • 将向量看作位置时,可以使用 x、y、z、w 四个分量名,分别代表 x 轴、y 轴、z 轴分量及 w 值。

1vec4 aPosition;
2aPosition.x = 34.2
3aPosition.z = 12.2

若向量是 4 维的,可以使用的分量名为 x、y、z、w;若向量是 3 维的,可以使用的分量名为 x、y、z;若是 2 维的,可以使用的 x、y 两个分量名。

  • 将向量看作纹理坐标时,可以使用 s、t、p、q 四个分量名,分别代表纹理坐标的不同分量。

1vec4 aTexCoor;
2aTexCoor.s = 0.45;
3aTexCoor.t = 0.22;

若向量是 4 维的,可以使用的分量名为 s、t、p、q;若向量是 3 维的,可以使用的分量名为 s、t、p;若是 2 维的,可以使用的 s、t 两个分量名。

除了使用 . 的形式来访问分量名,还可以将向量看作一个数组,用下标来进行访问:

1aColor[0] = 0.4;
2aPostion[2] = 0.34;
3aTexCoorp[1] = 0.12;

理解了 discard 的作用和向量各分量的含义,就很容易明白 Alpha 测试的原理了。

实际上,Alpha 测试并不是渲染管线上要进行的操作流程,而是我们自己想出的根据 Alpha 透明度值对片元进行处理罢了。

具体的实现可以参考我的 Github 项目,求一波 Star 。

https://github.com/glumes/AndroidOpenGLTutorial

OpenGL 相关文章推荐:

  1. 用 OpenGL 对视频帧内容进行替换

  2. OpenGL ES 学习资源分享

  3. OpenGL 裁剪测试及注意点

  4. OpenGL 学习系列---观察矩阵


技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

扫码关注公众号【音视频开发进阶】,一起学习多媒体音视频开发~~~

喜欢就点个「在看」吧 ▽

相关文章:

  • 【音视频连载-005】基础学习篇-SDL 加载 YUV 文件并显示
  • 【音视频连载-006】基础学习篇-SDL 播放 YUV 视频文件
  • Android 使用 OpenGL ES 绘制球面
  • 【音视频连载-007】基础学习篇-SDL 播放 PCM 音频文件(上)
  • PBO是OpenGL最高效的像素拷贝方式吗?
  • 游戏开发入门(一)游戏发展史
  • 【音视频连载-008】基础学习篇-SDL 播放 PCM 音频文件(下)
  • memcpy速度太慢?掌握这个技术让内存拷贝效率成倍提升
  • DXOMark是如何评价音频质量的
  • 【每周一记-001】~~~
  • 【音视频连载-009】第二季 FFmpeg 打造简易播放器
  • 【每周一记-002】
  • iOS中使用OpenGL 实现增高功能
  • 五分钟用C++11实现Android系统的Handler机制
  • 从入门到进阶|如何基于WebRTC搭建一个视频会议
  • Fundebug计费标准解释:事件数是如何定义的?
  • input实现文字超出省略号功能
  • java小心机(3)| 浅析finalize()
  • React+TypeScript入门
  • vue-loader 源码解析系列之 selector
  • 码农张的Bug人生 - 见面之礼
  • 推荐一个React的管理后台框架
  • 微信小程序:实现悬浮返回和分享按钮
  • 微信支付JSAPI,实测!终极方案
  • 一天一个设计模式之JS实现——适配器模式
  • 因为阿里,他们成了“杭漂”
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • AI算硅基生命吗,为什么?
  • 继 XDL 之后,阿里妈妈开源大规模分布式图表征学习框架 Euler ...
  • 如何用纯 CSS 创作一个货车 loader
  • ​io --- 处理流的核心工具​
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • $.ajax()
  • (LeetCode C++)盛最多水的容器
  • (二)Linux——Linux常用指令
  • (二)斐波那契Fabonacci函数
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (七)理解angular中的module和injector,即依赖注入
  • (四)Controller接口控制器详解(三)
  • (一)UDP基本编程步骤
  • ***通过什么方式***网吧
  • .NET MVC第五章、模型绑定获取表单数据
  • .Net Web项目创建比较不错的参考文章
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • :中兴通讯为何成功
  • @我的前任是个极品 微博分析
  • [2019.3.20]BZOJ4573 [Zjoi2016]大森林
  • [3D基础]理解计算机3D图形学中的坐标系变换
  • [android] 看博客学习hashCode()和equals()
  • [BZOJ2850]巧克力王国
  • [codeforces]Checkpoints
  • [codevs1288] 埃及分数
  • [COGS 622] [NOIP2011] 玛雅游戏 模拟
  • [C语言]——分支和循环(4)