下周末到了,您需要编写几十行代码并画一幅画,但是没有一个更好。 因此,上周末在最后一个周末之前,我展示了如何
进行光线追踪 ,甚至
炸毁所有东西。 这对于许多人来说是令人惊讶的,但是计算机图形是非常简单的事情,几百行裸C ++足以创建有趣的图片。
今天的对话主题是双目视觉,今天我们甚至无法达到一百行代码。 能够渲染三维场景,通过立体修复是很愚蠢的,今天我们将绘制如下内容:
Magic Carpet开发人员的愚蠢行为困扰着我。 对于那些没有找到它的人,该游戏使
在主要设置中的立体字和立体图
中进行3D渲染成为可能
,这些菜单仅在菜单中可用! 这个大脑专门爆炸了。
视差
因此,让我们开始吧。 首先,为什么我们的视觉设备可以让我们感知深度? 有一个聪明的词“视差”。 如果在手指上,那么我们将注意力集中在屏幕上。 我们大脑屏幕平面中的所有内容都存在一个副本中。 但是,如果突然有苍蝇在屏幕前飞过,那么(如果我们不改变视线!)我们的大脑将一式两份地记录下来。 同时,屏幕后面的墙上的蜘蛛也会分叉,分叉的方向取决于对象位于焦点的前面还是后面:

我们的大脑是一种非常有效的机器,用于分析略有不同的图像。 它使用
视差从二维视网膜图像中获取深度信息以进行
立体视 。 好吧,上帝保佑他们,用这些文字,让我们更好地画画吧!
假设我们的屏幕是进入虚拟世界的窗口:)

我们的任务是绘制两张图片,通过此“窗口”将可见。 在上图中,有两张图片,每只眼睛一张,我给它们显示了红色和蓝色的“三明治”。 我们不必担心我们如何将这些图片准确地馈送到视觉设备,我们只需要保存两个文件即可。 我对如何使用
光线追踪器获取这些图像特别感兴趣。
好吧,假设外观的方向不变,它是一个向量(0,0,-1)。 假设我们可以将摄像机的位置移动两眼之间的距离,还有什么呢? 有一个小妙处:通过我们的“窗口”的视锥是不对称的。 而且我们的光线追踪器只能渲染对称的凝视锥:

怎么办 阅读:)
实际上,我们可以将图片渲染得比所需的宽,而仅裁剪多余的图片:

浮雕
有了一般的渲染机制,应该很清楚,现在是时候问问自己将图像传输到我们的大脑了。 最简单的选择之一是红蓝色眼镜:

我们只是使两个预渲染器不是黑色而是白色,将左图写在红色通道中,将右图写成蓝色。 您将获得以下图片:

红色玻璃将切断一个通道,蓝色玻璃将切断另一个通道,以便每只眼睛都能收到自己的图片,我们可以用3D视角看世界。 这是对
第一篇文章的主要提交的
更改 ,其中显示了双眼和通道部件的相机设置。
浮雕效果渲染是查看(计算机!)立体图片的最古老的方法之一。 它们有很多缺点,例如,较差的色彩渲染(顺便说一下,请尝试在最终图片的绿色通道中记录右眼的绿色通道)。 好处之一-这种眼镜很容易用即兴的材料制成。
立体镜
随着智能手机的普及,我们记得什么是立体镜(有史以来第二个是在19世纪发明的)! 几年前,
Google建议使用两分钱的镜片(不幸的是,它们并没有在膝盖上完成),一些硬纸板(随处可见)和智能手机(躺在口袋中)来获得可忍受的虚拟现实眼镜:

在速卖通上,它们是堆,一块一百卢布。 与浮雕相比,您根本不需要做任何事情,只需拍摄两张照片并排组成,
这就是提交 。

严格来说,视镜头而定,可能需要
校正镜头
畸变 ,但我一点也不费心,而且在我的眼镜上看起来很棒。 但是,如果您确实需要应用桶形预失真来补偿镜头的畸变,那么这就是我的智能手机和眼镜的外观:

立体图
但是,如果您不想使用其他设备怎么办? 然后只有一种选择-麻木。 一般来说,上一张图片足以观看立体声,只需使用技巧即可观看立体声。 查看立体图有两个原理:移开眼睛或移开眼睛。 因此,我绘制了一个图表,其中显示了如何查看上一张图片。 上一张是双图,图中的两个红色条显示了左视网膜上的两个图像,右边的两个蓝条。

如果我们将视线集中在屏幕上,那么在四张图像中,我们会得到两张。 如果我们斜着眼睛看鼻子,就很有可能显示大脑的“三张”照片。 反之亦然,如果睁开眼睛,还可以获得“三张”照片。 叠加中央图像将使大脑具有立体效果。
这些方法以不同的方式提供给不同的人,例如,我根本不知道如何移动眼睛,但我很容易繁殖。 重要的是,必须以相同的方式查看为一种方法构造的立体图,否则将获得倒置的深度图(请参见负视差和正视差)。 这种观看立体声的方法的问题在于,相对于正常状态,很难
强烈地移动眼睛,因此您必须对小图片感到满意。 而且,如果您想要大的呢? 让我们完全牺牲色彩,只想要深度的感觉。 展望未来,这是我们在本部分末尾看到的图片:

该立体图旨在“稀释”眼睛(壁眼立体图)。 对于那些喜欢反向观看的人,
请在此处拍照 。 如果您不习惯于立体图,请尝试不同的条件:全屏图像,小图像,明亮的光线,黑暗的环境。 任务是睁开眼睛,使两个相邻的涡旋带重合。 专注于左上方的最简单方法是 她很平坦。 例如,我被哈勃(Habr)的环境所包围,我全屏打开图片。 不要忘记从中删除鼠标!
对缺陷的3D效果不满意。 如果您只是模糊地意识到随机点中间的圆形以及一些微弱的3D效果,那肯定是不完整的幻觉! 如果您正确看待,球应该清楚地从屏幕平面向观察者伸出,并且由于对图像的每个部分(前景和背景)都进行了持续而细致的研究,效果应该稳定并保持不变。 立体视觉有滞后现象:一旦获得稳定的图像,您看得越久越清晰。 屏幕离眼睛越远,深度效果就越大。
该立体图是根据Thimbleby等人在25年前提出的方法绘制的,该方法在他们的文章“
显示3D图像:单图像随机点立体图的算法 ”中。
起点
渲染立体图的起点是深度图(我们忘记了颜色)。
这是一个渲染图
的提交 :

我们渲染中的深度被近平面和远平面所截断,也就是说,我的地图中最远的点的深度为0,最接近的点为0。
基本原理
让我们的眼睛与屏幕保持距离d。 将(假想的)远平面(z = 0)放在屏幕后面相同的距离处。 我们选择一个常数μ,该常数决定了近平面的位置(z = 0):它将与另一平面相距μd。 我在代码中选择了μ= 1/3。 总的来说,我们整个世界的生活距离d-μd到屏幕后面的d。 让我们在两眼之间定义一个距离e(以像素为单位,在我的代码中,我选择了400像素)。

如果我们查看对象在图上标记为红色的点,则两个标记为绿色的像素在立体图中应具有相同的颜色。 如何找到这些像素之间的距离? 很简单 如果当前投影点的深度为z,则视差与眼睛之间距离的比率等于相应深度的比率:p / e =(d-dμz)/(2d-dμz)。 顺便说一下,请注意d正在缩小,并且在其他任何地方都没有涉及! 也就是说,p / e =(1-μz)/(2-μz),这意味着视差等于p = e *(1-μz)/(2-μz)像素。
也就是说,构造立体图的基本原理是:我们遍历整个深度图,对于每个深度值,我们确定哪些像素应具有相同的颜色,并将其写入我们的限制系统。 然后,我们从任意图片开始,并尝试满足所有先前施加的限制。
准备原始图片
在此阶段,我们将准备一张图片,稍后将对其施加视差限制。
在这里,提交 ,他画出这幅画:

请注意,通常,颜色只是随机的,除了我在红色通道中放入rand()* sin以提供周期性波。 这些波产生的距离为200个像素,这(选择的μ= 1/3和e = 400)是我们世界上的最大视差值,它也是一个遥远的平面。 这些波是可选的,但它们将有助于视觉的必要聚焦。
渲染立体图
实际上,与立体图相关的完整代码如下所示:
int parallax(const float z) { const float eye_separation = 400.;
如果有的话,
在这里提交 。 int视差(const float z)函数给出当前深度值相同颜色的像素之间的距离。 由于线彼此独立(我们没有垂直视差),因此我们逐行绘制立体图。 因此,主循环仅遍历所有行。 对于它们中的每一个,我们从一组完整的无限制像素开始,然后在其上施加成对相等限制,最后,我们将具有一定数量的相同颜色(不相连)像素的簇。 例如,具有左索引的像素和具有右索引的像素最终应该是相同的。
如何存储这组限制? 最简单的答案是
并集-查找数据结构 。 我不会描述它,这只是三行代码,您可以在Wikipedia上阅读它。 主要思想是,对于每个群集,我们将对其有一定的“负责任”,它也是一个根像素,我们将使其与原始图片中的颜色相同,并重新绘制群集中的所有其他像素:
for (size_t i=0; i<width; i++) {
结论
好吧,实际上,仅此而已。 二十行代码-我们的立体图已准备就绪,睁开眼睛和头脑,画画! 顺便说一句,仅立体图中的随机颜色通常是一种奢侈,原则上,如果您尝试尝试,还可以部分传输图片的颜色。
例如,其他立体声观看系统
与极化有关 ,我超出了讨论范围,因为它们超出了一百卢布的预算。 如果您错过了某些内容,请添加并更正!