**引言
**
在过去的十年里, GPU (图形处理单元)已经从特殊硬件(特供)转变成可以在数值计算领域开辟新篇章的高性能计算机设备。
许多算法可以使用拥有巨大的处理能力的GPU来高速执行和处理大数据量。即使在通常的情况下,不可能将图形硬件编程化, 图形硬件也可以加快算法与图像的处理。 举个例子:通常情况下可以用来计算图形差分,模糊图像, 合并图像,甚至是进行图像(或数组)平均值计算。
随后,可编程方式的出现给编程者带来了极大的便利。 可编程方式所提供的新的可能性,更广泛类别的算法可以移植到GPU来执行。需要转换一定的思路来适应使用屏幕渲染的形式来表达出算法。
现如今可编程GPU支持更高级别的编程范例,可以把它们称之为GPGPU (通用图形处理单元). 新模式允许执行更加通用的算法其不涉及到GPU硬件设备相关内容,可不关心图形化来编制程序了。
本文探讨不通过GPGPU的API而是用可扩展可编程渲染管道autostereogram与GPGPU进行交互.(译注:这个意思是autostereogram为GPGPU的一个框架么?)autostereogram场景渲染深度缓冲使用GPU的OpenGL和OpenCL (GPGPU) 内核,并且OpenGL GLSL (可编程渲染通道) 着色器的深度数据不必被CPU读取.
案例分析:Autostereogram
本文只提供autostereogram生成算法基础,将不涉及到更多细节,更具体的信息请参阅其他资料。
Autostereograms的普遍使用将使立体图像重新流行。Autostereograms可以使单个图片不聚焦在平面上,以3D场景的形式进行显示 ,最常用的方式是三维场景的展示。
autostereograms编码3D场景的能力并不是最好的, 但很快, 观看隐藏在autostereograms的这些"奥秘" 场景将变的非常容易。
本文所实现的算法是最简单的autostereogram生成算法之一,该算法简单地重复某个可平铺模式(可以是一个可平铺纹理或者一个随机生成的纹理),并根据输入深度图中像素的z深度来改变其“重复长度”:
对于输出图的每一行:
复制该重复纹理的一整行 (该瓦片)
对输入深度图中该行的每一个像素:
复制离左边 one-tile-width 个像素的那个像素的颜色, 然后减去偏移量 X
对于最大深度(离眼睛最远),X为0,对于最小深度(离眼睛最近),X为最大像素偏移量(~30 像素)
因此,查看器中的像素越是接近,重复模式就越短。这就是诱导眼睛和大脑认为该图像是三维图像的基础。输出图的宽度将会是重复图与输入深度图之和,这样就可以给最初那个未经改变的重复图的拷贝留出足够的空间。
当为之后的结果和性能比较呈现一个参考实现时,一个有着确切描述的CPU实现会在稍后被测试,而不是提供一个更加正式的算法描述。
参考:
- stereograms描述:http://en.wikipedia.org/wiki/Stereogram
- autostereogram描述: http://en.wikipedia.org/wiki/Autostereogram
- autostereogram查看技术:http://www.hidden-3d.com/how_to_view_stereogram.php
- stereograms的非技术性与技术性讨论:http://www.techmind.org/stereo/stereo.html
一般实现预览:
下图给出了总体的算法流程
3d场景渲染
使用opengl核心外形管线来完成3d场景的渲染,在此文中用作的示例场景,它包括一个简单反弹的开发箱壁的动画球。此动态场景的选择能提供更多来自不同实现的“实时”效果。
使用aframebufferobject渲染场景纹理是为了更容易操作计算得到的数据,使用纹理作为渲染目的,而不是依靠主要后备缓冲区拥有的某一优势。
- 输出尺寸(宽与高)变得更容易控制
- 能避免渲染窗口与别的窗口重叠的问题。
- 通常来说,用这种后处理流水线能更自然地符合场景纹理的使用。
然而,它很可能使用标准后备缓冲区来渲染和简单地来回读取此缓冲区。
场景渲染通常有两个输出结果:颜色缓存和深度缓存。深度缓存是立体图产生的一部分。当渲染场景的时候,就没有必要存储颜色,只需要深度信息。所以,当创建帧缓存对象的时候,就不需要添加颜色纹理信息。下面的代码就展示了以深度纹理作为目的的,帧缓存的创建。
// Allocate a texture to which depth will be rendered.
// This texture will be used as an input for our stereogram generation algorithm.
glGenTextures( 1 , &mDepthTexture );
glBindTexture( GL_TEXTURE_2D , mDepthTexture );
glTexImage2D(
GL_TEXTURE_2D ,
0 ,
GL_DEPTH_COMPONENT32 ,
kSceneWidth ,
kSceneHeight ,
0 ,
GL_DEPTH_COMPONENT ,
GL_FLOAT ,
0
);
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE );
glBindTexture( GL_TEXTURE_2D , 0 );
// Create a framebuffer object to render directly to the depth texture.
glGenFramebuffers( 1 , &mDepthFramebufferObject );
// Attach only the depth texture: we don't even bother attaching a target
// for colors, because we don't care about it.
glBindFramebuffer( GL_FRAMEBUFFER , mDepthFramebufferObject );
glFramebufferTexture2D(
GL_FRAMEBUFFER ,
GL_DEPTH_ATTACHMENT ,
GL_TEXTURE_2D ,
mDepthTexture ,
0
);
glBindFramebuffer( GL_FRAMEBUFFER , 0 );
当场景渲染的时候,还有一些初始化的任务。尤其是:
- 创建,加载和编译渲染着色器
- 创建顶点缓存
在主渲染循环中,下面的任务是必须加入的:
- 设置帧缓存对象作为渲染的目标
- 设置着色器程序作为当前活跃程序
- 渲染场景
为了保持文章在合理的长度内,这些内容在此都不一一解释了。这些在这个算法中都是很常用,很直接的,没有特别的地方。
下面就是用这个程序渲染的场景结果:
水平坐标计算
这是该算法一个有趣的部分,它将由于不同CPU和GPU的实现而不同。
在此之后,将依据每个像素计算出的纹理坐标堆积出最终的图像。
计算垂直纹理坐标不是主要的挑战,因为只需要垂直重复计算即可。垂直纹理坐标甚至无需在此时计算,只需在接下来的步骤中进行简单处理。这个立体图生成算法的核心实际上就是在输出图像的每个像素中计算出水平纹理坐标。
这一步的操作结果将最终输出相同的小的立体图,其中每个像素保存一个单一的浮点值,它表示在水平纹理坐标的二维图像。 这些浮点值将不断地从左向右出现,其中小数部分将代表(0~1范围内)的实际坐标和整数部分将代表该模式的重复次数。 这种表示是为了避免在寻找关键值时出现的混合值. 例如,如果值0.99和0.01之间的算法,插值将产生0.5左右的样值,但是这是完全错误的。 通过使用值0.99和1.01,插值将产生约1.0样值,这才是对的。
上面的伪码稍作修改就能实现这一中间步骤。在为每一个重复瓷片坐标的整个行设置第一个像素标记之后(比如在0——1之间增加取值个数来获取整个瓷片行),查询步骤就可以开始了。查询步骤是通过查询靠左的一个瓷片宽度数减去一个关于深度的值进行的。所以伪码如下:
For 输出坐标图片的每一行
为所有重复瓷片的第一行写入坐标
For 输入的深度映射行中的每个像素点
在当前写入行中,将靠左的一瓷片宽度像素减去X的偏移量,并实例化坐标系。
where 对于最大深度(人眼识别的最大深度),X值为0 and 对于最小深度(最接近人眼的深度)X是偏移像素的最大值(~30像素)
这个值加“1”,这样可以使得结果连续递增。
在输出坐标图片中存入计算得到的值。
更细致的应用细节由CPU的相关性能、应用来决定,不过考虑到各种运行细节,这个方法依然是水平较高的算法。