在CPU上实现的算法只涉及到从输入的深度值产生偏移量(即纹理坐标)。上面的给出的是一段简单的C++版本的伪代码。这个算法主要分为三个步骤:
首先,从GPU中读取深度值到CPU
然后,根据深度值产生相应的偏移量
最后,将计算产生的偏移量从CPU写回GPU
第一步 : 从GPU中读取深度值在进行场景渲染之后,深度值会被保存在GPU的一个纹理中。为了得到CPU算法中所需要的深度值,必须首先从GPU中获取深度值并保存到CPU能够访问的内存中。在这里,一个标准的浮点向量std::vector<float>被用来保存这些用于在CPU进行计算的深度值。实现的代码如下:
// 从GPU中读取深度值.
glBindTexture( GL_TEXTURE_2D , mDepthTexture );
glGetTexImage(
GL_TEXTURE_2D ,
0 ,
GL_DEPTH_COMPONENT ,
GL_FLOAT ,
mInputDepths.data()
);
glBindTexture( GL_TEXTURE_2D , 0 );
深度值将被储存在 mInputDepths 这个浮点型向量中。
第二步 : 计算偏移量计算偏移量就是简单地实现了上面的伪代码所描述的过程并将计算结果保存到内存数组中。下面的代码展示了如何将输入的深度值转换成相应的偏移量输出。
const int lPatternWidth = pPatternRenderer.GetPatternWidth();
const int lStereogramWidth = kSceneWidth + lPatternWidth;
for ( int j = 0; j < kSceneHeight; ++j )
{
// 首先初始化偏移量数组.
for ( int i = 0, lCountI = lPatternWidth; i < lCountI; ++i )
{
float& lOutput = mOutputOffsets[ j * lStereogramWidth + i ];
lOutput = i / static_cast< float >( lPatternWidth );
}
// 然后计算偏移量.
for ( int i = lPatternWidth; i < lStereogramWidth; ++i )
{
float& lOutput = mOutputOffsets[ j * lStereogramWidth + i ];
// 得到该像素所对应的深度值.
const int lInputI = i - lPatternWidth;
const float lDepthValue = mInputDepths[ j * kSceneWidth + lInputI ];
// Get where to look up for the offset value.
const float lLookUpPos = static_cast< float >( lInputI ) + kMaxOffset * ( 1 - lDepthValue );
// 在两个像素之间进行线性插值.
const int lPos1 = static_cast< int >( lLookUpPos );
const int lPos2 = lPos1 + 1;
const float lFrac = lLookUpPos - lPos1;
const float lValue1 = mOutputOffsets[ j * lStereogramWidth + lPos1 ];
const float lValue2 = mOutputOffsets[ j * lStereogramWidth + lPos2 ];
// 我们对线性插值的量加1以保证偏移量在一个给定的行中总是递增(以保证任何偏移量之间的线性插值都是有意义的)
const float lValue = 1.0f + ( lValue1 + lFrac * ( lValue2 - lValue1 ) );
lOutput = lValue;
}
}
在上一步偏移量计算结束以后,这些值必须被写回GPU中进行渲染。这个操作和上面第一步的操作正好相反,具体实现的代码如下:
glBindTexture( GL_TEXTURE_2D , mOffsetTexture );
glTexSubImage2D(
GL_TEXTURE_2D ,
0 ,
0 ,
0 ,
lStereogramWidth ,
kSceneHeight ,
GL_RED ,
GL_FLOAT ,
mOutputOffsets.data()
);
glBindTexture( GL_TEXTURE_2D , mOffsetTexture );
偏移量将会被写入到GPU的存储器中。
这样就完成了算法的CPU实现。这种方法的最大缺点是每一帧都需要在CPU和GPU之间进行大量的数据交换。从GPU进行图像数据的读取然后再写回GPU,这严重影响了实时程序的性能。
为了防止这个问题,第二步的处理将直接在GPU的存储器上执行,以避免CPU和GPU之间的往返读写。这一方法将在下面的部分中进行具体描述。
GPU 实现为了避免CPU和GPU之间不必要的往返读写,深度数据应该直接在GPU上进行处理。然而,stereogram生成算法需要得到先前输出图像同一行中设置的值。和使用片段着色器一样,对相同的纹理/图像缓冲区同时进行读取和写入对于传统GPU来说是非常不友好的的处理方法。
这里可以使用一个“带”为基础的方法,其中垂直条带会被从左至右呈现出来,每个频带的大小都不会超过左边的最小距离。在所提供的例子的源代码中可以看到,重复图案的宽度是85个像素,而最大的偏移量是30个像素(kMaxOffset的值),所以产生的最大的频带宽度为55个像素。由于不能过对于将要写入的纹理进行随机读取的操作,因此被渲染的纹理必须同时保存两个副本:一个用于读取,一个用于写入。那么刚才所写的必须被复制到另一个的纹理中去。
这种使用两个纹理的方法并不是最佳的。另外,频带的宽度对渲染的次数有直接的影响,这也将对性能产生直接的影响。不过,这个宽度是依赖于重复图案的,它可以根据具体的情况而改变,同时最大偏移量也是一个可以根据实时性需求改变的参数。性能会受到参数变化的影响,这并不是理想的情况。
一种更加灵活的方法是使用可编程渲染管线。使用OpenCL。GPGPU的API中“通用”的部分在类似的应用程序中发挥着十分重要的作用。这将允许使用GPU进行更通用,而非面向渲染的算法。这种灵活性使得我们能够有效地利用GPU进行立体图的生成。
首先,我们需要对前面CPU实现的算法做一些改变。然后对创建一个OpenCL的上下文,以及利用OpenCL对OpenGL的上下文的共享资源的使用进行说明。最后,将对使用OpenCL核函数来产生立体图的方法以及所需的要素进行展示。