OpenGLES 透视变换与屏幕UV坐标

OpenGLES 透视变换与屏幕UV坐标,第1张

一、从需求说起

本人在做3D贴纸的时候,遇到这样的一个需求,在3D贴纸需要和图像进行混合。做远小近大的3D效果,需要将二维的贴纸经过透视变换绘制到屏幕上,如果要添加混合效果,则必须知道变换后的坐标,如果坐标对不上,则会导致混合后的贴纸绘制到屏幕上可能会出现错乱、重叠的情况,因此如何计算重叠部分的准确坐标是实现混合的关键。为此,重新拾起已经被遗忘了的数学知识,将透视变换过程重新推导一遍,以便更好地理解透视变换以及如何转换到屏幕的空间坐标的。

二、透视变换推导

1、透视投影公式

下图给出了一个空间点(x, y, z)到一般的投影参考点(xv, yv, zv)的投影路径。该投影线与观察平面交于坐标位置(xp, yp, zvp), 其中zvp是观察平面上选择的位于z轴上的点。

由此我们可以计算得到坐标位置的参数方程如下:

当投影参考点在Z轴上时,xv = yv = 0,此时有:

因此,我们可以建立一个变换矩阵将一个空间位置转为齐次坐标位置,使得矩阵仅包含透视参数而不包含坐标值。然后观察坐标系的透视投影变换分两步实现。先用透视变换矩阵计算齐次坐标:

Ph 是齐次点(xh, yh, zh, h) 的列矩阵表示, 而P是坐标(x, y, z, 1)的列矩阵表示,在实际当中,透视矩阵需要跟观察矩阵(ViewMatrix)合并,然后将组合矩阵应用于场景的世界坐标描述以生成齐次坐标。

为了防止z轴除以齐次参数h后出现扭曲,我们需要通过为z变换设定矩阵元素从而对透视投影zp的坐标进行规范化。有多种方法选择矩阵元素。下面是一种可能形成透视投影矩阵的方法:

5、对称的视锥体

从投影参考点到裁剪窗口中心平穿过观察体的线就会说透视投影棱台的中心线。棱台中心线与观察平面相交于坐标(xv, yv, zvp)位置,用窗口尺寸表示裁剪窗口的对角位置,可得:

整体如下图所示:

可以求得裁剪窗口高度:

6、斜透视投影棱台

如果透视投影观察体中心线并不垂直于观察平面,则得到一个斜棱台(oblique frustum)。

为了方便计算,将投影参考点(xv, yv, zv) = (0, 0, 0),可得到错切变换矩阵的元素:

如果将观察平面放在近裁剪平面处,则透视投影矩阵可以进一步简化。将裁剪窗口中心移到观察平面坐标位置(0, 0)处,需要选择的错切参数值满足:

当投影参考点位于观察坐标原点切近裁剪平面与观察平面重合时,透视投影矩阵可以简化为:

将透视矩阵和错切矩阵综合,就可以得到下面的将场景坐标位置转换成齐次正交坐标的斜透视投影矩阵。该变换的投影参考点是观察坐标原点,而近裁剪平面是观察平面。

7、规范化透视投影变换坐标

矩阵将观察坐标系中的对角位置变换到透视投影齐次坐标。使用齐次参数h 除齐次坐标,可得实际的正交投影坐标。该透视投影将棱台观察体中所有点变换成矩形平行管道观察体中的位置,变换过程的最后一步是将该平行管道映射到规范化观察体(normalized view volume)中,其实也就是设备标准化坐标系(NDC)中的坐标。

转换过程遵循评语投影的规范化过程。从棱台观察体变换而来的矩形平行管道映射到对称左手参考系的规范化立方体中。完成规范化的缩放矩阵是:

将透视矩阵与缩放矩阵综合得到规范化矩阵:

将该规范化透视矩阵进行一般化,可以得到以下形式:

如果透视投影观察体一开始就指定为对称棱台,则可用裁剪窗口的视场角和尺寸来表达规范化透视变换的元素。将投影参考点位于原点且观察平面在近裁剪平面位置时,可以得到:

三、Android OpenGLES 的三维观察函数

1、观察变换函数

在Android中,你可以使用MatrixsetLookAtM方法来设置观察变换矩阵,方法原型如下:

在OpenGL中的方法跟Android中的OpenGLES 不一样。OpenGL建模观察模式用下列语句来设定的:

观察参数用GLU函数指定,该函数如下:

其中(x0, y0, z0) 跟Android中的方法setLookAtM的(eyeX, eyeY, eyeZ) 点均表示观察参考点在世界坐标系的位置。而(xref, yref,zref) 和 (centerX, centerY, centerZ) 表示参考点的坐标,(Vx, Vy, Vz) 和 (upX, upY, upZ) 表示向上向量。默认情况下 gluLookAt的参数是 P0 = (0, 0, 0), Pref = (0,0, -1), V = (0, 1, 0);

2、对称透视投影棱台

OpenGL中对称透视投影棱台观察体用gluPerspective表示,原型如下:

在Android的OpenGLES 中也存在类似的方法:

其中 theta 和 fovy 表示视场角,0~180度可选。aspect表示长宽比。far 和near 表示观察参考点到远近裁剪平面的距离。

3、通用透视投影函数

在OpenGL中,通用棱台一般使用glFrustum函数来实现:

当选择 xwmin = - xwmax 且 ywmin = - ywmax 时,表示的是一个对称棱台。

在Android的OpenGLES中,可以使用类似的方法:

四、空间坐标投影到观察平面上的UV坐标计算。

好了,至此,我们讨论了透视投影矩阵变换的整个过程,以及OpenGL 和OpenGLES 设置投影矩阵的方法。那么,如何求得空间坐标经过透视投影矩阵的变换后,得到屏幕的UV坐标呢?

假设在棱台(frustum)中的一点的坐标为(x, y, z),经过投影变换后的坐标为(x', y', z')。则我们可以得到以下计算公式:

新得到的坐标(x', y', z', w) 是经过透视投影变换后的坐标,该坐标就是前面第7小节规范化透视投影变换坐标中讨论的透视投影齐次坐标。由于OpenGL的观察平面就是近裁剪平面,因此该坐标就是坐标(x,y,z)经过透视变换后在近裁剪平面上的三维坐标。其中w记录了深度信息。当设置为对称棱台投影后,矩阵的实际值就是前面计算得到的对称棱台规范化的透视投影矩阵:

那么换算得到的新坐标是投影齐次坐标,那么接下来如何转换成屏幕UV坐标呢?我们只需要将得到的新坐标进行归一化为NDC坐标系,然后将其转成UV坐标的表达形式即可。

1、转成NDC坐标系

这里将所有坐标均除以w值,即可得到NDC坐标,此时的w将被规整为-1 ~ 1 之间。

2、换算成屏幕UV坐标

根据前面计算得到的NDC坐标,可以换算成屏幕的UV坐标。由于NDC坐标的范围时-1 ~ 1 的立方体,而屏幕UV坐标则是0~1的平面。如何计算? 其实很简单,只需要从NDC坐标中缩小到原来的一般然后平移到UV屏幕中间(05, 05),即可求得UV坐标。

五、OpenGLES 中计算透视变换到裁剪平面上的uv坐标

看到前面的一大堆计算,估计很多人都会头晕眼花的。其实你完全可以不了解前面的推导过程,在OpenGL 和OpenGLES 中经过透视投影矩阵变换后的屏幕uv坐标甚至是一件非常简单的事情,变换过程如下:

通过将棱台(frustum)中的坐标,与总变换mvpMatrix的乘积,得到一个vec4的变量,该变量就是gl_Position的值。gl_Position代表着总变换后的空间位置。也就是我们所说的齐次坐标。然后将其转成NDC坐标,重新构建一个新的屏幕UV坐标即可。

六、最后说一下3D贴纸需求实现思路

实现3D贴纸并将贴纸与图像按照自定义的方式进行混合的整体过程实现如下:

1、根据人脸关键点坐标反推算出正脸的坐标(考虑歪头、转脸的情况,如果人脸关键点检测得到的是三维空间点另外计算)

2、根据正脸的坐标计算出贴纸处于屏幕空间四个顶点的UV坐标

3、根据计算出的贴纸处于屏幕空间的UV坐标,反推算出贴纸实际所处平面的空间坐标(假设未做歪头等动作时的坐标)

4、根据得到的贴纸实际空间的坐标,经过平移、旋转后,计算出投影变换后的综合矩阵。

5、在glsl中计算总最终的位置:

6、将得到的最终的空间坐标vPosition传递给Fagment Shader,将坐标转换成屏幕的uv坐标

7、获取图像与贴纸重叠部分的texture进行混合

每个像素用1个bit表示,可表示的颜色范围为双色,即最传统的黑和白。1个bit只能表示0,1两种值。需要调色板,不过调色板只包含两种颜色。

每个像素用4个bit表示,4个bit所能够表示的索引范围是0-15,共16个。也就是可以表示16种颜色。即调色板中包含16中颜色。

每个像素用8个bit表示。8个bit所能够表示的索引范围是0-255,共256个。也就是可以表示256中颜色。即调色板中包含256中颜色。

RGB像素格式中的bit存储的是每一个像素点的R,G,B值

一个像素用16个bit = 2个byte表示 ,R=5 G=6 B=5

为什么绿色为6位?

一个像素用16个bit = 2个byte,但是最高位不用,R=5 G=5 B=5

RGB24图像每个像素用8个bit,共24个位表示,共3个字节,注意:在内存中RGB各分量的排列顺序为:BGR

RGB32图像每个像素用32个bit表示,占4个byte,R,G,B分量分别用8个bit表示,存储顺序为B,G,R,最后8个字节保留。注意:在内存中RGB各分量的排列顺序为:BGR

RGB32图像每个像素用32个bit表示,占4个字节,R,G,B分量分别用8个bit表示,存储顺序为B,G,R,最后8个为透明像素。注意:在内存中RGB各分量的排列顺序为:BGRA

注意:java默认使用大端字节序,c/c++默认使用小端字节序,android平台下BitmapconfigARGB_8888的Bitmap默认是大端字节序,当需要把这个内存数据给小端语言使用的时候,就需要把大端字节序转换为小端字节序。例如:java层的ARGB_565传递给jni层使用时,需要把java层的ARGB_565的内存数据转换为BGRA565。

详细验证请看: Android Bitmap像素排列与JNI *** 作

YUV有很多变种,我们常说的YUV指的是YCbCr,YUV三个字母中,其中”Y”表示明亮度(Lumina nce或Luma),也就是灰阶值;而”U”和”V”表示的则是色度(Chrominance或Chroma)作用是描述影像色彩及饱和度,用于指定像素的颜色。Cb指蓝色色度分量,而Cr指红色色度分量,是标准 YUV 的一个翻版(还有YPbPr等),此文中,我们就用 YUV 指代 YCbCr 了。

首先,YUV按照数据大小分为三个格式,YUV420,YUV422,YUV444。由于人眼对Y的敏感度远超于对U和V的敏感,所以可以多个Y分量共用一组UV,这样既可以极大的节省空间,又可以不太损失质量。

按照多个 Y 分量共用一个 UV 的方式,我们可以把 YUV 分为 420,422,444 三种类型,而在这三种类型之下,我们又可以按照 YUV 的排列储存顺序,将其细分为好多种格式,这些格式数量繁多,又不好记忆,这为我们学习过程中造成了不少困难。下面我就为大家一一介绍。

首先,我们将可以按照 YUV 的排列方式,再次将 YUV 分成三个大类,Planar,Semi-Planar 和 Packed。

Planar YUV 三个分量分开存放

Semi-Planar Y 分量单独存放,UV 分量交错存放

Packed YUV 三个分量全部交错存放

按照这三种方式,我们就可以将 YUV 格式进行比较细致的分类了。

YUV的所有格式列表

一张从上到下分别为原图、Y、U 和 V:

YUV 4:4:4 采样,意味着 Y、U、V 三个分量的采样比例相同,因此在生成的图像里,每个像素的三个分量信息完整,都是 8 bit,也就是一个字节。

如下图所示:

其中,Y 分量用叉表示,UV 分量用圆圈表示。

举个例子 :

假如图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]

那么采样的码流为:Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3

最后映射出的像素点依旧为 [Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]

假如图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]

那么采样的码流为:Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3

最后映射出的像素点依旧为 [Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]

可以看到这种采样方式的图像和 RGB 颜色模型的图像大小是一样,并没有达到节省带宽的目的,当将 RGB 图像转换为 YUV 图像时,也是先转换为 YUV 4:4:4 采样的图像。

YUV 4:2:2 采样,意味着 UV 分量是 Y 分量采样的一半,Y 分量和 UV 分量按照 2 : 1 的比例采样。如果水平方向有 10 个像素点,那么采样了 10 个 Y 分量,而只采样了 5 个 UV 分量。

如下图所示:

举个例子 :

假如图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3] 那么采样的码流为:Y0 U0 Y1 V1 Y2 U2 Y3 V3

其中,每采样过一个像素点,都会采样其 Y 分量,而 U、V 分量就会间隔一个采集一个。

最后映射出的像素点为 [Y0 U0 V1]、[Y1 U0 V1]、[Y2 U2 V3]、[Y3 U2 V3]

假如图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、[Y2 U2 V2]、[Y3 U3 V3]

那么采样的码流为:Y0 U0 Y1 V1 Y2 U2 Y3 V3 其中,每采样过一个像素点,都会采样其 Y 分量,而 U、V 分量就会间隔一个采集一个。

最后映射出的像素点为 [Y0 U0 V1]、[Y1 U0 V1]、[Y2 U2 V3]、[Y3 U2 V3]

采样的码流映射为像素点,还是要满足每个像素点有 Y、U、V 三个分量。但是可以看到,第一和第二像素点公用了 U0、V1 分量,第三和第四个像素点公用了 U2、V3 分量,这样就节省了图像空间。

一张 1280 720 大小的,在 YUV 4:2:2 采样时的大小为:

可以看到 YUV 4:2:2 采样的图像比 RGB 模型图像节省了三分之一的存储空间,在传输时占用的带宽也会随之减少。

YUV 4:2:0 采样,并不是指只采样 U 分量而不采样 V 分量。而是指,在每一行扫描时,只扫描一种色度分量(U 或者 V),和 Y 分量按照 2 : 1 的方式采样。比如,第一行扫描时,YU 按照 2 : 1 的方式采样,那么第二行扫描时,YV 分量按照 2:1 的方式采样。对于每个色度分量来说,它的水平方向和竖直方向的采样和 Y 分量相比都是 2:1 。

如下图所示:

假设第一行扫描了 U 分量,第二行扫描了 V 分量,那么需要扫描两行才能够组成完整的 UV 分量。

举个例子 :

假设图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、 [Y2 U2 V2]、 [Y3 U3 V3][Y5 U5 V5]、[Y6 U6 V6]、 [Y7 U7 V7] 、[Y8 U8 V8]

那么采样的码流为:Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8其中,每采样过一个像素点,都会采样其 Y 分量,而 U、V 分量就会间隔一行按照 2 : 1 进行采样。

最后映射出的像素点为:[Y0 U0 V5]、[Y1 U0 V5]、[Y2 U2 V7]、[Y3 U2 V7][Y5 U0 V5]、[Y6 U0 V5]、[Y7 U2 V7]、[Y8 U2 V7]

假设图像像素为:[Y0 U0 V0]、[Y1 U1 V1]、 [Y2 U2 V2]、 [Y3 U3 V3][Y5 U5 V5]、[Y6 U6 V6]、 [Y7 U7 V7] 、[Y8 U8 V8]

那么采样的码流为:Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8其中,每采样过一个像素点,都会采样其 Y 分量,而 U、V 分量就会间隔一行按照 2 : 1 进行采样。

最后映射出的像素点为:[Y0 U0 V5]、[Y1 U0 V5]、[Y2 U2 V7]、[Y3 U2 V7][Y5 U0 V5]、[Y6 U0 V5]、[Y7 U2 V7]、[Y8 U2 V7]

从映射出的像素点中可以看到,四个 Y 分量是共用了一套 UV 分量,而且是按照 22 的小方格的形式分布的,相比 YUV 4:2:2 采样中两个 Y 分量共用一套 UV 分量,这样更能够节省空间。

一张 1280 720 大小的,在 YUV 4:2:0 采样时的大小为:

可以看到 YUV 4:2:0 采样的图像比 RGB 模型图像节省了一半的存储空间,因此它也是比较主流的采样方式。

YUV 的存储格式,有两种:

YUYV 格式是采用打包格式进行存储的,指每个像素点都采用 Y 分量,但是每隔一个像素采样它的 UV 分量,排列顺序如下:

UYVY 格式也是采用打包格式进行存储,它的顺序和 YUYV 相反,先采用 U 分量再采样 Y 分量,排列顺序如下:

YUV 422P 格式,又叫做 I422,采用的是平面格式进行存储,先存储所有的 Y 分量,再存储所有的 U 分量,再存储所有的 V 分量。

基于 YUV 4:2:0 采样的格式主要有 YUV 420P 和 YUV 420SP 两种类型,每个类型又对应其他具体格式。

I420 的单帧结构示意图如下(Planar 方式)

这幅图的上面一幅可以看出 Y1、Y2、Y7、Y8 共用 U1 和 V1。后面的线性数组为其存储顺序,可以看出 Y、U 和 V 都是顺序存储的,往外写的时候,先按顺序将 Y 分量写出,然后再根据 U、V 分别将它们依次写出即可。

NV12的单帧结构示意图如下(Planar 方式)

可以看出与 YV12 不同的时,它的 Y 虽然也是顺序存储,但 U、V 却是交错存储的,这种方式存储在往外写出时则先直接顺序写出 Y,然后对 UV 分别依次写出。

PS:Android的Camera Preview默认图像格式为NV21。

把RGB和YUV的范围都缩放到[0,255]

YUV转RGB

RGB转YUV

参考资料:

RGB数据格式

一文读懂 YUV 的采样与格式

视音频数据处理入门:RGB、YUV像素数据处理

Android Bitmap像素排列与JNI *** 作

YUV420_SVG

1、PV是page view的缩写,即页面浏览量,通常是衡量一个网络新闻频道或网站甚至一条网络新闻的主要指标。

网页一般通过URL或标题(html title)来标识,大多数工具都提供了类似的定义方法]关于PV的统计要考虑2种特殊情况:

一是从服务器返回错误网页或重定向网页时,是否计数以及如何配置;

二是本地或网关服务器的缓存生效时是否计数。

2、UV是unique visitor的简写,是指通过互联网访问、浏览这个网页的自然人。

UV是一个反映实际使用者的概念,每个UV相对于每个IP,更加准确地对应一个实际的浏览者。使用UV作为统计量,可以更加准确的了解单位时间内实际上有多少个访问者来到了相应的页面。

扩展资料:

pv的分类

1、网站流量指标

网站流量统计指标常用来对网站效果进行评价,主要指标包括:

独立访问者数量(unique visitors);

重复访问者数量(repeat visitors);

页面浏览数(page views);

每个访问者的页面浏览数(Page Views per user);

某些具体文件/页面的统计指标,如页面显示次数、文件下载次数等。

2、用户行为指标 

用户行为指标主要反映用户是如何来到网站的、在网站上停留了多长时间、访问了那些页面等,主要统计指标包括:

用户在网站的停留时间;

用户来源网站(也叫“引导网站”);

用户所使用的搜索引擎及其关键词;

在不同时段的用户访问量情况等。

3、浏览方式时间设备浏览器名称和版本 *** 作系统 

用户浏览网站的方式相关统计指标主要包括:

用户上网设备类型;

用户浏览器的名称和版本;

访问者电脑分辨率显示模式;

用户所使用的 *** 作系统名称和版本;

用户所在地理区域分布状况等。

参考资料来源:百度百科-pv (page view,页面浏览量)

参考资料来源:百度百科-UV (网站独立访客)

以上就是关于OpenGLES 透视变换与屏幕UV坐标全部的内容,包括:OpenGLES 透视变换与屏幕UV坐标、RGB与YUV、统计里面PV 和 UV代表什么意思等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/9310240.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-27
下一篇 2023-04-27

发表评论

登录后才能评论

评论列表(0条)

保存