OpenGL 学习教程
Android OpenGL ES 学习(一) – 基本概念
Android OpenGL ES 学习(二) – 图形渲染管线和GLSL
Android OpenGL ES 学习(三) – 绘制平面图形
Android OpenGL ES 学习(四) – 正交投影
Android OpenGL ES 学习(五) – 渐变色
Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序
Android OpenGL ES 学习(七) – 纹理
Android OpenGL ES 学习(八) –矩阵变换
Android OpenGL ES 学习(九) – 坐标系统和。实现3D效果
Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL
Android OpenGL ES 学习(十一) –渲染YUV视频以及视频抖音特效
之前学习到的图片,这一章,我们使用OpenGL 来解析 yuv 视频,并实现一些效果,废话不多说,先上效果:
当然,在进入主题之前,先学习一些基础知识。
一. 什么是 YUV
在说 yuv 之前,就不得不说 RGB 图像空间,顾名思义,RGB 是值图像的每一个像素都有 R、G,B 三个值,且三个值一次排列存储;但不一定说一定是按照 R,G,B 顺序排列,也可以是 B,G,R 这样的顺序。其中 R,G,B 的位深为 8 bit。
我们常见的图片处理,都是用 R,G,B 的图像格式,比如bitmap,比如图像的存储,基本使用 R,G,B
1.1 那为什么还有 yuv 呢?
我们知道,视频是由一张张图片组成,假设有一个 1920 * 1080 分辨率、帧率为60帧的视频,如果不进行压缩处理,并且使用RGB进行存储的话,仅仅一分钟的视频就能达到 ( 1920 * 1080 * 8 * 60 * 60 )bit (约等于56G),这显然是很夸张的。
但R,G,B这三个颜色是彼此是由相关性的,不利于编码压缩,所以,我们需要另外一种图像格式,来解决图像压缩问题,这个时候,yuv 就被提升来了。
yuv 图像格式将亮度信息 Y 和 色彩信息 UV 分离开来,Y 表示亮度,是图像的总体轮廓,即我们常说的灰度值,UV 表示色度,主要描绘图像的色彩信息,即颜色饱和度。如下图(图片来源wiki百科):
yuv 最早用于电视系统和模拟视频领域,它兼容了黑白电视和彩色电视,如果你家有vcd,dvd 这种设备,就会发现有 YCbCr(YUV) 这种接口,如果是黑白电视,值需要接入Y分量即可。
从很早的时候,人们就发现,人类对亮度信息比较敏感,而对色彩信息不那么敏感,比如我们降低一些颜色值,并不影响人对这张图像感官。因此,yuv 的编码压缩,又可以分为 YUV 4:4:4、YUV 4:2:2、YUV 4:2:0 这几种常用的类型
1.2 YUV 格式
YUV 4:4:4、YUV 4:2:2、YUV 4:2:0,指的是U,V 分量像素点的个数和采集方式,其中又以 YUV 4:2:0 最为常用。
可以这样简单理解:
- YUV 4:4:4:每一个 Y 就对应一个 U 和一个 V分量
- YUV 4:2:2:每两个 Y 共用一个 U、一个 V 分量
- YUV 4:2:0:每四个 Y 共用一个 U、V分量
如下图(图片来源极客时间):
其中,YUV 又有不同的存储方式:
- packed :packed格式是先连续存储所有的Y分量,然后依次交叉储存U、V分量;
- planar:planar格式也会先连续存储所有的Y分量,但planar会先连续存储U分量的数据,再连续存储V分量的数据,或者先连续存储V分量的数据,再连续存储U分量的数据:
更多内容和YUV转RGB,可以参考:https://time.geekbang.org/column/article/449795
二. 视频解析
从之前OpenGL 的纹理教程中,我们是把一张图片,通过纹理的方式,传递给片段着色器,最终通过纹理采样,复制给片段颜色值,呈现出来的。
现在使用 YUV ,该如何处理呢?我们知道,视频最终的呈现还是RGB格式的数据,因此,我们需要把 YUV 的数据,所以需要在片段着色器赋值之前,把YUV转换成 RGB。
2.1 GL_LUMINANCE
在OpenGL 的api 中,可以发现有个 GL_LUMINANCE 格式,它表示只取一个颜色通道,这样的话,就可以把 YUV 拆分成3个通道来读取,然后我们设置 3个纹理,把 YUV 数据传入其中,并最终把这三个通道合并在一起。
2.2 获取 YUV 视频
为了方便演示,我们使用 YUV420P 的视频,即4个Y共用一个U,V 分量,且存储是先存储Y,然后是U,最后再存储V分量。
这里我们可以用 ffmepg 的命令,轻松把一个 MP4 的视频转换成 YUV,由于 YUV 比较大,记得修改分辨率,这样小一些:
ffmpeg -i input.mp4 -s 288x512 -r 30 -pix_fmt yuv420p out.yuv
- 1
2.3 读取 yuv 文件数据
之后,就可以通过不断读取这个yuv文件,拿到y,u,v的数据,假设视频大小为 wxh ,则先读取 wh 个y,再读取 wh/4 个u,再读取 w*h/4 个 v;一帧读取完后,就进行渲染,然后再重复操作,直到文件被读取完毕。
我们把文件放在 assert 文件夹下:
/** * 读取yuv数据,注意 w,h 为视频宽高 */ private fun readYuvData(w: Int, h: Int) { val input = context.resources.assets.open(YUV_FILE) //视频时 yuv420p ,4 个 y 共用一个 uv,先存储y,再u,和v val y = ByteArray(w * h) val u = ByteArray(w * h / 4) val v = ByteArray(w * h / 4)
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>isExit<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token class-name">Log</span><span class="token punctuation">.</span><span class="token function">d</span><span class="token punctuation">(</span><span class="token constant">TAG</span><span class="token punctuation">,</span> <span class="token string">"readYuvData,手动退出"</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> val readY <span class="token operator">=</span> input<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span> val readU <span class="token operator">=</span> input<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>u<span class="token punctuation">)</span> val readV <span class="token operator">=</span> input<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span> <span class="token comment">//都读到分量</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>readY <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">&&</span> readU <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">&&</span> readV <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">//从这里触发刷新</span> bufferY <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">wrap</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span> bufferU <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">wrap</span><span class="token punctuation">(</span>u<span class="token punctuation">)</span> bufferV <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">wrap</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span> val glView <span class="token operator">=</span> view as <span class="token class-name">GLSurfaceView</span> <span class="token comment">//主动触发刷新</span> glView<span class="token punctuation">.</span><span class="token function">requestRender</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//延时30ms,控制速度</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">30</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token class-name">Log</span><span class="token punctuation">.</span><span class="token function">d</span><span class="token punctuation">(</span><span class="token constant">TAG</span><span class="token punctuation">,</span> <span class="token string">"readYuvData,文件末尾,退出"</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
2.4 着色器编写
顶点着色器,沿用上一章,不需要改变,但是我们把位置改一下,让它填充整个屏幕:
private val POINT_RECT_DATA2 = floatArrayOf(
// positions // texture coords
1f, 1f, 0.0f, 1.0f, 0.0f, // top right
1f, -1f, 0.0f, 1.0f, 1.0f, // bottom right
-1f, -1f, 0.0f, 0.0f, 1.0f, // bottom left
-1f, 1f, 0.0f, 0.0f, 0.0f // top left
)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
片段着色中,设置三个纹理,用来读取 yuv分量的数据:
private const val FRAGMENT_SHADER = """#version 300 es precision mediump float; out vec4 FragColor; in vec2 vTexture; uniform sampler2D textureY; uniform sampler2D textureU; uniform sampler2D textureV; void main() { //采样到的yuv向量数据 float y,u,v; //yuv转化得到的rgb向量数据 vec3 rgb; //分别取yuv各个分量的采样纹理 y = texture(textureY, vTexture).r; u = texture(textureU, vTexture).g - 0.5; v = texture(textureV, vTexture).b - 0.5; //yuv转化为rgb, https://en.wikipedia.org/wiki/YUV rgb.r = y + 1.540*v; rgb.g = y - 0.183*u - 0.459*v; rgb.b = y + 1.818*u; FragColor = vec4(rgb, 1.0);
<span class="token punctuation">}</span>
“”"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
可以看到,我们使用了三个纹理textureY,textureU,textureV,然后用了三个变量 y,u,v 用来接收纹理数据。
前面说到,OpenGL 的分量,除了包含位置信息{x,y,z,w},还有颜色(r,g,b,a)和纹理信息(s,t,r,q):
- x,y,z,w: 与位置相关的分量
- r,g,b,a: 与颜色相关的分量
- s,t,p,q: 与纹理坐标相关的分量
当我们设置 sampler2D 的类型为 GL_LUMINANCE,所以 texture().r 拿到的是yuv 的第一个颜色向量的第一个分量信息,就是y;
那这个 0.5 是什么?为啥要减去它?
先看到YUV与RGB 的转换公司,这里用高清模式(BT709),颜色空间为 Limited Range 的转换公式:(图片来源)
可以看到,有个转换偏差值,而 U,V 默认是127 ,Y 的偏移量为0。8 个 bit 位的取值范围是 0 ~ 255,由于在 shader 中纹理采样值需要进行归一化(注意,纹理的范围是[0,1]),所以 UV 分量的采样值需要分别减去 0.5 ,确保 YUV 到 RGB 正确转换。
2.5 纹理加载
编写完着色器,就可以编写纹理对象了。首先,设置纹理的下标:
private val textures = IntArray(3)
//三个纹理,需要设置纹理的下标
GLES30.glUniform1i(GLES30.glGetUniformLocation(programId, "textureY"), 0)
GLES30.glUniform1i(GLES30.glGetUniformLocation(programId, "textureU"), 1)
GLES30.glUniform1i(GLES30.glGetUniformLocation(programId, "textureV"), 2)
- 1
- 2
- 3
- 4
- 5
设置纹理的对象:
GLES30.glGenTextures(3, textures, 0) for (i in 0..2) { GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[i])
<span class="token comment">//纹理环绕</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glTexParameteri</span><span class="token punctuation">(</span><span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_WRAP_S</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_REPEAT</span><span class="token punctuation">)</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glTexParameteri</span><span class="token punctuation">(</span><span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_WRAP_T</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_REPEAT</span><span class="token punctuation">)</span> <span class="token comment">//纹理过滤</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glTexParameteri</span><span class="token punctuation">(</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_MIN_FILTER</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_NEAREST</span> <span class="token punctuation">)</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glTexParameteri</span><span class="token punctuation">(</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_MAG_FILTER</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_LINEAR</span> <span class="token punctuation">)</span> <span class="token comment">//解绑纹理对象</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glBindTexture</span><span class="token punctuation">(</span><span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
前面2.3章节,已经拿到了 yuv 的数据,这里,我们使用 glTexImage2D 把数据设置给纹理:
override fun onDrawFrame(gl: GL10?) { //步骤1:使用glClearColor设置的颜色,刷新Surface GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
<span class="token comment">//使用 y 数据</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glActiveTexture</span><span class="token punctuation">(</span><span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE0</span><span class="token punctuation">)</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glBindTexture</span><span class="token punctuation">(</span><span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> textures<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glTexImage2D</span><span class="token punctuation">(</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_LUMINANCE</span><span class="token punctuation">,</span> w<span class="token punctuation">,</span> h<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_LUMINANCE</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_UNSIGNED_BYTE</span><span class="token punctuation">,</span> bufferY <span class="token punctuation">)</span> <span class="token comment">//使用 u 数据</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glActiveTexture</span><span class="token punctuation">(</span><span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE1</span><span class="token punctuation">)</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glBindTexture</span><span class="token punctuation">(</span><span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> textures<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glTexImage2D</span><span class="token punctuation">(</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_LUMINANCE</span><span class="token punctuation">,</span> w <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">,</span> h <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_LUMINANCE</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_UNSIGNED_BYTE</span><span class="token punctuation">,</span> bufferU <span class="token punctuation">)</span> <span class="token comment">//使用 v 数据</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glActiveTexture</span><span class="token punctuation">(</span><span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE2</span><span class="token punctuation">)</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glBindTexture</span><span class="token punctuation">(</span><span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> textures<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glTexImage2D</span><span class="token punctuation">(</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TEXTURE_2D</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_LUMINANCE</span><span class="token punctuation">,</span> w <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">,</span> h <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_LUMINANCE</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_UNSIGNED_BYTE</span><span class="token punctuation">,</span> bufferV <span class="token punctuation">)</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glBindVertexArray</span><span class="token punctuation">(</span>vao<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token function">glDrawElements</span><span class="token punctuation">(</span><span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_TRIANGLE_STRIP</span><span class="token punctuation">,</span> <span class="token number">6</span><span class="token punctuation">,</span> <span class="token constant">GLES30</span><span class="token punctuation">.</span><span class="token constant">GL_UNSIGNED_INT</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span> bufferY<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span> bufferU<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span> bufferV<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
效果如下:
三.加抖音特效
从纹理那张可知
Android OpenGL ES 学习(七) – 纹理
我们可以通过:
FragColor = mix(texture1,texture2,0.5);
- 1
的方式去添加纹理的效果。因此,我们也可以修改片段着色器中的 RGB 数据,实现不同的效果。
3.1 灰度
比如灰度,只需要修改rgb的颜色,即可,你可以把 u,v 分量去掉:
// u = texture(textureU, vTexture).g - 0.5;
// v = texture(textureV, vTexture).b - 0.5;
u = 0.0;
v = 0.0;
- 1
- 2
- 3
- 4
也可以使用算法:
float gray = rgb.r * 0.2126 + rgb.g * 0.7152 + rgb.b * 0.0722;
FragColor = vec4(gray,gray,gray, 1.0);
- 1
- 2
得到灰度视频:
3.2 颜色反转
让每个颜色值反转,我们只需要1 - 颜色值即可:
rgb.r = 1.0 - (y + 1.540*v);
rgb.g = 1.0 - (y - 0.183*u - 0.459*v);
rgb.b = 1.0 - (y + 1.818*u);
FragColor = vec4(rgb, 1.0);
- 1
- 2
- 3
- 4
3.3 对称不同颜色值
什么意思呢?就是让左上角颜色反转,右下角灰色,其他区域正常显示,如下视频:
思路是对y进行分割,取中间(0.5,0.5)作为分割点:
if(vTexture.x <= 0.5 && vTexture.y <= 0.5){ //左上角,使用反色 float r = 1.0 - rgb.r; float g = 1.0 - rgb.g; float b = 1.0 - rgb.b; FragColor = vec4(r,g,b, 1.0); }else if(vTexture.x > 0.5 && vTexture.y > 0.5){
<span class="token comment">//右下角,使用灰度</span> <span class="token keyword">float</span> gray <span class="token operator">=</span> rgb<span class="token punctuation">.</span>r <span class="token operator">*</span> <span class="token number">0.2126</span> <span class="token operator">+</span> rgb<span class="token punctuation">.</span>g <span class="token operator">*</span> <span class="token number">0.7152</span> <span class="token operator">+</span> rgb<span class="token punctuation">.</span>b <span class="token operator">*</span> <span class="token number">0.0722</span><span class="token punctuation">;</span> <span class="token class-name">FragColor</span> <span class="token operator">=</span> <span class="token function">vec4</span><span class="token punctuation">(</span>gray<span class="token punctuation">,</span>gray<span class="token punctuation">,</span>gray<span class="token punctuation">,</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
}else{
FragColor = vec4(rgb, 1.0);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
3.4 二/三分屏
分屏这个原理呢,需要抽象一下,拿二分屏来说,其实最终操作的是纹理坐标的值。
如下图:
比如,因为要二分屏,实际上显示的,肯定不是全部内容,如显示区域为 0.25 到0.75 范围,实际就是把这个范围,填充到上下两个区域,分割线为0.5.
这样,上半部分(0,0)到(0,0.5) 要显示时,实际是从(0,0.25),(0,0.75)的内容,同理下半部分,也是(0,0.5)到(0,1.0),实际也是 (0,0.25),(0,0.75)。
因此,我们修改 y 分量的大小即可:
//输入是不能被修改的,所以使用一个vec2 分量
vec2 uv = vTexture.xy;
if(uv.y >= 0.0 && uv.y <= 0.5){
uv.y = uv.y + 0.25;
}else{
uv.y = uv.y - 0.25;
}
//分别取yuv各个分量的采样纹理
y = texture(textureY, uv).r;
u = texture(textureU, uv).g - 0.5;
v = texture(textureV, uv).b - 0.5;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
就可以得到二分屏:
同理,我们可以得到三分屏的效果:
if(uv.y >= 0.0 && uv.y <= 0.2){
uv.y = uv.y + 0.3;
}else if(uv.y > 0.8){
uv.y = uv.y - 0.5;
}
- 1
- 2
- 3
- 4
- 5
参考:
https://juejin.cn/post/7160304816877469733
https://juejin.cn/post/7168042219163779108
https://time.geekbang.org/column/article/449795
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/05%20Framebuffers/