惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
L
LangChain Blog
Microsoft Azure Blog
Microsoft Azure Blog
雷峰网
雷峰网
Recent Announcements
Recent Announcements
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
博客园_首页
The Cloudflare Blog
M
MIT News - Artificial intelligence
博客园 - 【当耐特】
MyScale Blog
MyScale Blog
S
SegmentFault 最新的问题
P
Proofpoint News Feed
Y
Y Combinator Blog
Jina AI
Jina AI
博客园 - 聂微东
A
About on SuperTechFans
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
F
Full Disclosure
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Microsoft Security Blog
Microsoft Security Blog
爱范儿
爱范儿
T
Tailwind CSS Blog
J
Java Code Geeks
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
小众软件
小众软件
酷 壳 – CoolShell
酷 壳 – CoolShell
T
The Blog of Author Tim Ferriss
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 三生石上(FineUI控件)
W
WeLiveSecurity
PCI Perspectives
PCI Perspectives
Attack and Defense Labs
Attack and Defense Labs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
宝玉的分享
宝玉的分享
IT之家
IT之家
Hacker News: Ask HN
Hacker News: Ask HN
The Register - Security
The Register - Security
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs

音视频开发进阶

音视频教程-第三节 音视频教程-第二节 真的,AI 可能就是新时代的信息差 充值 Cursor 之后,工作有了哪些变化?🤔 个人'蒸馏'大模型能做哪些有意思的事情 DeepSeek 大模型在 Mac 上的部署和运行 音视频教程-第一节 【WebRTC 专栏】-- Android 开发集成 WebRTC 库的几种方式 【WebRTC 专栏】-- 在 Mac M1 等系列芯片编译和开发 WebRTC-Android 库 Meta Llama3 大模型在 Mac 上的部署和运行 iOS VideoToolBox 解码 HEVC Open-GOP 视频的问题排查 Flutter 状态管理之 InheritedWidget 使用和分析 用 ChatGPT 回答技术问题怎么样 ? 音视频开发系统入门大致路线 UE 4.27 添加自定义 ShadingModel 用 UE4 虚幻引擎做个捏脸小功能~~ UE4 材质练习 之 凹凸贴图偏移的使用 UE4 材质练习系列基础 OpenGL上下文创建以及共享机制 007 | 播放器系列专栏-解析 MP4 文件读取信息 006 | 播放器系列专栏-在 Mac 上查看 MP4 格式信息 干货 | 快速抽取缩略图是怎么练成的? 关于直播、WebRTC、FFmpeg 的那些事 005 | 播放器系列专栏-在 Windows 上查看 MP4 格式信息 将音视频中的花屏、绿屏、黑屏问题一网打尽 关于音视频里面的解码帧率和渲染帧率 004 | 播放器系列专栏-认识MP4视频(下) 003 | 播放器系列专栏-认识MP4视频(上) 入门或者转行音视频,应该要怎么做? H264视频文件如何缩放分辨率 002 | 播放器系列专栏-FFmpeg依赖库的配置 001 | 播放器系列专栏-关于播放器项目的一个小实践 Seek策略以及在有B帧情况下的处理 目前流媒体开发工程师工作内容主要是什么? 一个音视频领域专业问答的小圈子 干货收藏 || Vulkan Game Engine 视频教程 音视频春节假期内卷指南(实操) Vulkan 在 FFmpeg 中的支持 Windows 下 FFmpeg 和 LibX264 的编译和配置 Metal 开发 | 使用 C++ 进行接口调用 音视频开发工作经验分享 || 视频版 FFmpeg 调用 MediaCodec 硬解码到 Surface 上 代码吸猫 | 用 OpenGL 图像渲染的养猫计划 百倍变速--解码到底能不能丢 非参考帧 ?FFmpeg 有话说!!! 老生常谈-FFmpeg 的编译问题轻松搞定 FFmpeg 调用 Android MediaCodec 进行硬解码(附源码) 【WebRTC 专栏】--创建相机预览 Unity Shader 光照基础之 Half Lambert 光照模型 Unity Shader 光照基础之Lambert光照模型 Unity Shader 光照基础内容 Unity Shader 显示一张图片纹理 UnityShader 的基本概念 Unity 物体的基本操作 C++ 模板系列小结07-尾置返回类型 C++ 模板系列小结06-可变参数模板特性 C++ 中的多线程的使用和线程池建设 C++ 模板系列小结05-模板类型作为模板参数 C++ 模板系列小结04-类模板中的成员模板 C++ 模板系列小结03-在模板中指定变量类型 C++ 模板系列小结02-非类型模板参数 C++ 模板系列小结01-函数模板和类模板 从零打造渲染引擎系列01-什么是渲染引擎 iOS开发 - 在 Swift 中去调用 C/C++ 代码 2021 技术新番 - 从零打造渲染引擎系列 iOS 音视频开发的一些基础准备工作 音视频交流群又来啦~~~ 【WebRTC 专栏】WebRTC & Android 开发学习环境搭建~ 【喜大普奔】域名终于备案通过啦 Shader 优化 | OpenGL 绘制网格效果 【音视频连载-011】第二季 FFmpeg 一层一层获取文件信息 KodeLife | Shader 实时编辑预览的强大工具使用实践 推荐几个堪称教科书级别的 Android 音视频入门项目 【音视频连载-010】第二季 FFmpeg 日志打印 【音视频连载-008】基础学习篇-SDL 播放 PCM 音频文件(下) 【音视频连载-007】基础学习篇-SDL 播放 PCM 音频文件(上) 【音视频连载-006】基础学习篇-SDL 播放 YUV 视频文件 【音视频连载-005】基础学习篇-SDL 加载 YUV 文件并显示 【音视频连载-004】基础学习篇-SDL 加载图片并显示 【音视频连载-003】基础学习篇-SDL 消息循环和事件响应 【音视频连载-002】基础学习篇-SDL 创建窗口并显示颜色 【音视频连载-001】基础学习篇- SDL 介绍以及工程配置 LearnOpenGL 源码在 MAC 上的编译与调试 2019 年终总结与回顾 Android NDK 开发的免费技术视频来啦~~ OpenGL 实现视频编辑中的转场效果 OpenGL 实践之贝塞尔曲线绘制 图像库 libjpeg-turbo 编译与实践 图像库 libpng 编译与实践 rust 开发编译 Android 动态库实践 Android NDK 开发 —— 从 Assets 文件夹加载图片并上传纹理 简单易用的图像解码库介绍 —— stb_image 博客图床迁移记 进击的 Vulkan 移动开发之 SwapChain 进击的 Vulkan 移动开发之 Command Buffer 进击的 Vulkan 移动开发之 Instance & Device & Queue 进击的 Vulkan 移动开发(一)之今生前世 Java 显式锁 Lock 与条件队列 C++ 标准容器库小结 一文读懂 YUV 的采样与格式 《OpenGL ES 3.x 游戏开发》碰撞检测之 AABB 包围盒
《OpenGL ES 3.x 游戏开发》之颜色混合和使用
音视频开发进阶 · 2018-07-16 · via 音视频开发进阶

一个专注音视频领域的小圈子

避免图片丢失,建议阅读微信原文:

https://mp.weixin.qq.com/s/40ss1fbLh3Qr5X4o76GTjA 在 Android 中有一个类 PorterDuffXfermode ,它是用来设置颜色混合方式的,也就是在已有颜色的基础上再绘制一笔颜色,这两个颜色是如何进行混合的,是新绘制的颜色覆盖了原有颜色,还是新绘制的颜色和原有颜色混合组成另一种颜色呢。 在 OpenGL 中同样有这样颜色混合的问题。 在 OpenGL 的世界模型中是有深度的概念的,也就是由 z 轴坐标值来决定物体距离坐标原地的远近,但到最后世界模型里的物体都要投影到近平面,最后映射到视口上。而且,距离相机也就是视口越近的物体,就会遮住后面的物体,就和用肉眼去观察物体一下,后面的形状会被前面的形状挡住。

但和肉眼观察不同的是,OpenGL 里最终呈现的颜色,是将两个片元混合之后计算的值,我们可以改变这片元混合的方式,这就和前面 Android 里面提到的 PorterDuffXfermode 混合模式一样。

颜色混合基础知识

OpenGL 中的颜色混合就是将通过各种测试准备进入帧缓冲的片元(源片元)与帧缓冲中的原有片元(目标片元)按照设定的比例加权计算最终片元的颜色值。新片元不一定是直接覆盖缓冲区中的源片元。

混合因子

OpenGL 通过设置混合因子来指定两个片元的加权比例,每次都需要给出两个混合因子:

  • 源因子,用于确定将进入帧缓冲的片元在最终片元中的比例
  • 目标因子,用于确定原帧缓冲中的片元在最终片元中的比例

由于 OpenGL 中每个颜色值包括 4 个色彩通道,因此,两种混子因子都有 4 个分量值,分别对应一个色彩通道,具体混合计算细节如下:

  • 设源因子和目标因子分别为 $[S_r,S_g,S_b,S_a]$ 和 $[D_r,D_g,D_b,D_a]$ ,$S$ 表示是源因子,$D$ 表示是目标因子,r,g,b,a 下标分别表示 红、绿、蓝、透明度 4 个色彩通道。
  • 设源片元和目标片元的颜色值分别为 $[R_s,G_s,B_s,A_s]$ 和 $[R_d,G_d,B_d,A_d]$,R,G,B,A 分别表示红、绿、蓝、透明度 4 个色彩通道,s 下标表示源片元,d 下标表示目标片元。
  • 混合后最终片元颜色各个色彩通道的值是由颜色混合方程式计算而来,系统提供的常用颜色混合方程式如下:

混合方程式

方程式名 最终片元颜色各个色彩通道的值
GL_FUNC_ADD $[R_sS_r+R_dD_r, G_sS_g+G_dD_g, B_sS_b+B_dD_b, A_sS_a+A_dD_a]$
GL_FUNC_SUBTRACT $[R_sS_r-R_dD_r, G_sS_g-G_dD_g, B_sS_b-B_dD_b, A_sS_a-A_dD_a]$
GL_FUNC_REVERSE_SUBTRACT $[R_dD_r-R_sS_r, G_dD_g-G_sS_g, B_dD_b-B_sS_b, A_dD_a-A_sS_a]$
GL_MIN $[min(R_sS_r,R_dD_r),min(G_sS_g,G_dD_g),min(B_sS_b,B_dD_b),min(A_sS_a,A_dD_a)]$
GL_MAX $[max(R_sS_r,R_dD_r),max(G_sS_g,G_dD_g),max(B_sS_b,B_dD_b),max(A_sS_a,A_dD_a)]$

默认情况下,系统会自动调用 GL_FUNC_ADD 混合方程式来计算最终片元颜色各个色彩通道的值,如果想要调用其他混合方程式来计算最终的片元颜色,系统也有提供对应的方法:

方法签名 说明
glBlendEquation(int mode) mode 参数的含义为指定混合方程式,用来计算最终片元的颜色,其值为混合方程式的名字
glBlendEquationSeparate(int modeRGB,int modeAlpha) modeRGB 参数为颜色的 RGB 通道进行混合时所使用的混合方程式名,modeAlpha 参数的含义是颜色的 Alpha 透明度通道进行混合时所使用的混合方程式名字,通过其可以实现 RGB 和 Alpha 通道单独指定混合方程式的功能

源因子和目标因子

对于颜色混合来说,选定了混合方程式,接下来最重要的就是设置混合因子了。

在 OpenGL 中预置了一些混合因子,如下表:

常量名 RGB 混合因子 A 混合因子
GL_ZERO $[0,0,0]$ 0
GL_ONE $[1,1,1]$ 1
GL_SRC_COLOR $[R_s,G_s,B_s]$ $A_s$
GL_ONE_MINUS_SRC_COLOR $[1-R_s,1-G_s,1-B_s]$ $1-As$
GL_DST_COLOR $[R_d,G_d,B_d]$ $A_d$
GL_ONE_MINUS_DST_COLOR $[1- R_d, 1- G_d, 1- B_d]$ $1-A_d$
GL_SRC_ALPHA $[A_s, A_s, A_s]$ $A_s$
GL_ONE_MINUS_SRC_ ALPHA $[1- A_s, 1- A_s, 1- A_s]$ $1-As$
GL_DST_ ALPHA $[A_d, A_d, A_d]$ $A_d$
GL_ONE_MINUS_DST_ ALPHA $[1- A_d, 1- A_d, 1- A_d]$ $1-A_d$
GL_SRC_ALPHA_SATURATE $[f , f, f] f=min(A_s, 1-A_d)$ 1
GL_CONSTANT_COLOR $[R_c, G_c, B_c]$ $A_c$
GL_ONE_MINUS_CONSTANT_COLOR $[1- R_c, 1- G_c, 1- B_c]$ $1-A_c$
GL_CONSTANT_ALPHA $[Ac, Ac, Ac]$ $A_c$
GL_ONE_MINUS_CONSTANT_ALPHA $[1- Ac, 1- Ac, 1- Ac]$ $1- Ac$

其中,常量名称中有 SRC 代表各通道值来自源片元,有 DST 代表各通道值来自目标片元,另外 GL_SRC_ALPHA_SATURATE 只能用作源因子。

对于常量名中有 CONSTANT 的代表使用预设颜色常量值对应色彩通道的值作为相应的因子值,其中的 $R_c$、$G_c$、$B_c$、$A_c$ 分别代表预设颜色常量值的 RGBA 通道的值,如果没有设置则默认值为 $[0.0f、0.0f、0.0f、0.0f]$ 。

设置颜色常量值使用的是 glBlendColor 方法,如下:

    public static native void glBlendColor(
        float red,
        float green,
        float blue,
        float alpha
    );

设置混合的方法

有了混合因子,接下来就是设置混合因子了,OpenGL 提供了如下方法来设置:

方法签名 说明
glBlendFunc(GLenum sfactor, GLenum dfactor) 第一个参数为设置的源因子,第二个参数为设置的目标因子,其值如上表
glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha,GLenum dstAlpha) 第一个参数为设置源因子的 RGB 值;第二个参数为设置目标因子 RGB 值;第三个参数为设置源因子 Alpha 的值;第四个参数为设置目标因子 Alpha 的值。该方法实现了 RGB 和 Alpha 通道单独指定混合因子值的功能

常用混合组合

对于混合因子和混合 方程式的组合太多了,恰当的组合可以产生很好的效果,下面给出两组常用的组合:

  • 源因子 GL_SRC_ALPHA ,目标因子 GL_ONE_MINUS_SRC_ALPHA ,即源因子和目标因子分别为 $[A_s, A_s, A_s, A_s]$ 和 $[1-A_s, 1-A_s, 1-A_s, 1-A_s]$ 。此组合实现的是最典型的半透明遮挡效果。若源片元是透明的,则根据透明度透过后面的内容;若源片元不透明,则仅能看到源片元,因此,使用此组合时往往会采用半透明的纹理或颜色对源片元着色。

  • 源因子 GL_SRC_COLOR ,目标因子 GL_ONE_MINUS_SRC_COLOR ,即源因子和目标因子分别为 $[R_s, G_s, B_s, A_s]$ 和 $[1- R_s, 1- G_s, 1- B_s, 1-A_s$ 。此组合可以实现滤光镜效果,也就是平时透过有色眼镜或玻璃观察事物的感觉。与第一种常用组合不同,此组合不要求应用于源片元的颜色或者纹理是半透明的。

具体使用

前面讲了这么多理论,其实就是阐述两个颜色的 RGBA 值如何计算得到最后的 RGBA 值,并且每一个 R、G、B、A 分量都是两个颜色的 R、G、B、A 对应乘以不同的混合因子后相加得到的,这个混合因子的设置可以根据源片元的颜色来设定,也可以根据目标片元的颜色来设定。

先假设有这样的场景:

通过这样的图片经过混合后去查看后面的内容,类似于刺激战场上开镜效果,

显然,图片的黑色区域是要可以透过看到后面内容的,绿色区域也是一样,不然就全遮盖住后面内容了。

这里就可以用到上面的混合因子组合,源因子 GL_SRC_COLOR,目标因子 GL_ONE_MINUS_SRC_COLOR ,这个混合因子是根据源片元的颜色来设定的。

根据这两个混合因子和混合方程式计算,可以得出最后的颜色值。

根据源因子 GL_SRC_COLOR 计算,由于黑色的 RGB 值都为 0 ,RGB 混合因子都为 0,也就是透明的,源片元不会进入到最终片元。

根据源因子 GL_ONE_MINUS_SRC_COLOR 计算,由于黑色的 RGB 值都为 0 ,那么目标颜色的混合因子都为 1,也就是目标颜色都会被显示,可以看到后面的物体。

对于绿色部分,就不存在为0 或者为 1 的情况了,就是两个颜色按照混合因子计算后的值了。

具体的使用过程很简单,大致代码如下:

			// 先绘制好背景内容 
			// 开启颜色混合进行绘制
            GLES20.glEnable(GLES20.GL_BLEND)
	        // 设置混合因子
            GLES20.glBlendFunc(GLES20.GL_SRC_COLOR, GLES20.GL_ONE_MINUS_SRC_COLOR)
			// 绘制操作
            rect!!.drawSelf(textureId)
            MatrixState.popMatrix()

最后效果如下:

当然,还可以使用另外一种混合因子组合 GL_SRC_ALPHA 和 GL_ONE_MINUS_SRC_ALPHA,根据源因子的透明度来设置混合因子。

关于如何使用 GL_SRC_ALPHA 和 GL_ONE_MINUS_SRC_ALPHA 混合因子,可以参考之前的文章 用 OpenGL 对视频帧内容进行替换,大概原理都一样的,就是图片换成带透明度的,并且更改一下混合因子组合,就不赘述了。

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

https://github.com/glumes/AndroidOpenGLTutorial

参考

  1. 《OpenGL ES 3.x 游戏开发》

最后

俗话说:

独学而无友,则孤陋而寡闻

学习的路上是需要交流和分享的,这里有个二维码,如果你对 OpenGL ES 感兴趣欢迎一起来交流讨论。

加群讨论

要是二维码过期了,加微信 ezglume 好友,备注 OpenGL ,拉你入群~

原创文章,转载请注明来源:    《OpenGL ES 3.x 游戏开发》之颜色混合和使用