美文网首页从八开始——图形渲染/Metal专题
Metal与图形渲染八:透视纠正与坐标系

Metal与图形渲染八:透视纠正与坐标系

作者: 肠粉白粥_Hoben | 来源:发表于2021-09-13 20:51 被阅读0次

零. 前言

之前一段时间一直和团队的其他小伙伴研究很炫酷的特效,遇到了很多掉头发的问题,幸好大家都很给力,一一给解决了。今天抽空来复盘和总结一下当时遇到的一些难点吧,就是标题所说的,透视纠正和坐标系这两个大难题~

一. 透视纠正

我们的特效需求支持了不规则的图形遮罩,也就意味着我们不能直接简单粗暴地取一个图形的最大最小四个顶点坐标。

于是我们想到初步的方案是用OpenCV识别出四边形的四个顶点位置,直接传给业务端进行渲染,想法很不错,确实识别出来了,但是却发现渲染出来的图像完全扭曲了

有种百思不得其解的感觉,后面查到文章才知道是透视纠正的原因。。

在我们执行渲染操作的时候,顶点着色器会要求我们返回一个含(x, y, z, w)的四维坐标,w称为比例因子,当w不为0时(一般设1),表示一个坐标,一个三维坐标的三个分量x,y,z用齐次坐标表示为变为x,y,z,w的四维空间,变换成三维坐标是方式是(x/w, y/w, z/w)。

w的作用可不仅仅是使一个顶点等比例压缩,他还有透视纠正的功能,如下面公式所示,当渲染操作进行纹理渲染的时候,他会根据当前渲染点到两个顶点的距离、以及两个顶点的w值进行透视纠正,可以看到,某个点w值越大,就离a点越远。w的设置符合近大远小的透视变换。

如果我们直接传入顶点坐标,使w=1,则原透视纠正公式就会变为线性插值,最终导致了纹理变形:

因此,为了使得纹理不变形,我们需要获取两个参数,一个是图像的外接矩形坐标,一个是将外接矩形变换为真实顶点坐标的透视变换矩阵。当透视变换矩阵(4*4)乘以外接矩形坐标(4*1)时,即可得到真实的顶点坐标,纹理插值也不会变形了。

至于怎么得到透视变换矩阵嘛,那是工具的事儿啦,大概原理就是根据外接矩形坐标、真实顶点坐标,调用OpenCV的透视变换函数求出来的。

二. 坐标系

通过上一章,我们可以知道,需要用工具产生的顶点坐标、透视矩阵确定最终的顶点坐标坐标。

但由于这个特效是Web、Android、iOS三端的,而且iOS端渲染还包含Metal渲染和OpenGL渲染,各种渲染机制的坐标系不完全相同,可以简单地区分为左手坐标系和右手坐标系,我们需要根据这些坐标系来为工具定制出具体的透视变换矩阵求解方案。

什么是左手坐标系和右手坐标系呢?顾名思义,需要伸出你的左手和右手,并作出两两垂直的手势,如下图所示,可以发现,当x轴和y轴方向一致的时候,z轴会朝向两个相反的地方。

OpenGL和OpenCV同属右手坐标系,工具正常求透视矩阵即可,但对于Metal的透视矩阵,我们在计算的过程中需要将y坐标置反,这样就相当于z轴翻转了,才可以适配左手坐标系。

再来看看他们x、y轴的方向,可以发现OpenGL和Metal是以中间点为原点,往右、往上递增,而OpenCV是以左上角为原点,往右、往下递增。所以工具求出顶点位置后还需要一发x、y坐标转换操作。

这里的originX和originY就是将OpenCV的坐标系y坐标取反后,归一化得到的真实坐标。

GLfloat originX, originY, width, height;
originX = -1 + 2 * rect.origin.x / videoSize.width;
originY = 1 - 2 * rect.origin.y / videoSize.height;
width = 2 * rect.size.width / videoSize.width;
height = 2 * rect.size.height / videoSize.height;

在解决完顶点坐标翻转后,我们还需要留意OpenGL和Metal之间的纹理坐标系的差异,OpenGL纹理坐标系以左下角为原点,而Metal以左上角为原点。

OpenGL渲染——GPUImage提供的默认纹理坐标如下:

static const GLfloat noRotationTextureCoordinates[] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
    };

由此可见,对于OpenGL渲染,顶点的构建四个点分别是左下、右下、左上、右上。

而自研Metal链式渲染也采用了相同的纹理坐标数值:

- (NSArray *)defaultTextureCoordinates {
    return @[
        @0.0f, @0.0f,
        @1.0f, @0.0f,
        @0.0f, @1.0f,
        @1.0f, @1.0f,
    ];
}

但由于纹理坐标系与OpenGL不一致,因此对于Metal渲染,顶点的构建四个点分别是左上、右上、左下、右下。

因此,对于同一个点来说,OpenGL的顶点y坐标还需要再翻转一次。得到以下代码:

OpenGL的四个顶点:

float oriVertices[] = {
    originX,            -originY,
    originX + width,    -originY,
    originX,            height - originY,
    originX + width,    height - originY,
};

Metal的四个顶点:

NSArray *result = @[
    @(originX), @(originY),
    @(originX+width), @(originY),
    @(originX), @(originY-height),
    @(originX+width), @(originY-height),
];

三. 总结

w坐标是透视变换的重要因子,以近大远小的方式决定了渲染图形的坐标和纹理纠正。

OpenGL、Metal的顶点坐标系、纹理坐标系各不相同,需要根据具体坐标系去决定顶点坐标和纹理坐标。

四. 参考文章

WebGL 纹理映射的透视纠正

相关文章

  • Metal与图形渲染八:透视纠正与坐标系

    零. 前言 之前一段时间一直和团队的其他小伙伴研究很炫酷的特效[https://mp.weixin.qq.com/...

  • Metal语言规范

    Metal Shading Language Metal Shading Language是用来编写 3D图形渲染...

  • 1.6Metal坐标系

    Metal定义了几个标准坐标系,以表示沿渲染管线处于不同阶段的变换后的图形数据。四维均匀向量 ( x,y,z,w)...

  • Metal与图形渲染番外篇:不同填充样式的实现

    在Metal与图形渲染二:透明图片的渲染[https://www.jianshu.com/p/5de8fed023...

  • 初识MetalView

    Metal metal是iOS设备的一个底层图形API,功能类似OpenGL,支持图形渲染和GPU通用计算.met...

  • Metal Shader Language

    Metal Shading Language介绍 Metal 着⾊语⾔ 是⽤来编写 3D 图形渲染逻辑 和 并⾏计...

  • 001-Metal介绍

    关于 Metal 及指南 Metal Framework 支持利用 GPU 加速的高级 3D 图形渲染和数据并行计...

  • Metal Shading Language - 语法小结

    Metal Shading Language简述 Metal 着色语言是用来编写3D图形渲染逻辑 和 并行计算核心...

  • 十八、Metal着色语言

    Metal Shading Language Metal着⾊语⾔是⽤来编写 3D 图形渲染逻辑和并⾏计算核⼼逻辑的...

  • 初探Metal

    Metal简介 Metal和OpenGL ES类似的面向底层的图形编程接口,是渲染高级3D图形和执行数据计算使用图...

网友评论

    本文标题:Metal与图形渲染八:透视纠正与坐标系

    本文链接:https://www.haomeiwen.com/subject/qzxfgltx.html