通过 Autostereograms 案例学习 OpenGL 和 OpenCL 的互操作性 【已翻译100%】(2/3)

立体渲染

这最后一步的坐标“形象”,并重复平铺图像作为输入,并简单地通过在合适的位置采样平铺图像最终渲染图像。 它会从输入的坐标“图像“得到水平纹理坐标。他将从输出的坐标"图像"中计算垂直坐标(只是简单的重复自身).

这个采样过程是在GPU上通过自定义着色器完成的。 一个屏幕对齐的四边形开始呈现,接下来的像素着色器则被用来计算最终的颜色渲染。

#version 150
smooth in vec2 vTexCoord;
out vec4       outColor;

// Sampler for the generated offset texture.
uniform sampler2D uOffsetTexture;
// Sampler for the repeating pattern texture.
uniform sampler2D uPatternTexture;
// Scaling factor (i.e. ratio of height of two previous textures).
uniform float     uScaleFactor;

void main( )
{
    // The horizontal lookup coordinate comes directly from the
    // computed offsets stored in the offset texture.
    float lOffsetX = texture( uOffsetTexture, vTexCoord ).x;

    // The vertical coordinate is computed using a scaling factor
    // to map between the coordinates in the input height texture
    // (i.e. vTexCoord.y) and where to look up in the repeating pattern.
    // The scaling facture is the ratio of the two textures' height.
    float lOffsetY = ( vTexCoord.y * uScaleFactor );

    vec2  lCoords  = vec2( lOffsetX , lOffsetY );
    outColor = texture( uPatternTexture , lCoords );
};

这样就完成了算法概述。下一节介绍了CPU执行的坐标生成阶段。

CPU实现
在CPU上实现的算法只涉及到从输入的深度值产生偏移量(即纹理坐标)。上面的给出的是一段简单的C++版本的伪代码。这个算法主要分为三个步骤:

首先,从GPU中读取深度值到CPU
然后,根据深度值产生相应的偏移量
最后,将计算产生的偏移量从CPU写回GPU

第一步 : 从GPU中读取深度值

在进行场景渲染之后,深度值会被保存在GPU的一个纹理中。为了得到CPU算法中所需要的深度值,必须首先从GPU中获取深度值并保存到CPU能够访问的内存中。在这里,一个标准的浮点向量std::vector被用来保存这些用于在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;
    }
}

第三步 : 将偏移量从CPU写入GPU

在上一步偏移量计算结束以后,这些值必须被写回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核函数来产生立体图的方法以及所需的要素进行展示。

渲染场景要做出的修改

CPU版本的深度贴图算法不能被用于GPU。这贴图会同时被OpenCL使用,而OpenCL能直接使用的OpenGL贴图格式有限。依据文档clCreateFromGLTexture2D其中提到的支持的图像通道格式,GL_DEPTH_COMPONENT32不是可以被OpenCL使用的图像格式,非常不幸,因为这个图像格式和我们想要使用的非常像,但是我们可以避开这个问题。

为从场景渲染步骤中获取深度纹理,第二个纹理对象将填充到帧缓冲区。切记只有单一深度纹理会附属于CPU版本。这个深度纹理仍然需要填充到深度缓冲区进行深度测试才能显示。不管怎样,另一个纹理会作为成颜色填充除非接收到相应颜色单元值,它将会接收深度值。下面的代码展示了如何创建纹理以及如何将它帧缓冲区对象。

// Skipped code to allocate depth texture...

//  DIFFERENCE FROM CPU IMPLEMENTATION 
// However, because OpenCL can't bind itself to depth textures, we also create
// a "normal" floating point texture that will also hold depths.
// This texture will be the input for our stereogram generation algorithm.
glGenTextures( 1 , &mColorTexture );
glBindTexture( GL_TEXTURE_2D , mColorTexture );
glTexImage2D(
    GL_TEXTURE_2D ,
    0 ,
    GL_R32F ,
    kSceneWidth ,
    kSceneHeight ,
    0 ,
    GL_RED ,
    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 the depth texture and the color texture (to which depths will be output)
glBindFramebuffer( GL_FRAMEBUFFER , mDepthFramebufferObject );
glFramebufferTexture2D(
    GL_FRAMEBUFFER ,
    GL_DEPTH_ATTACHMENT ,
    GL_TEXTURE_2D ,
    mDepthTexture ,
    0
    );
glFramebufferTexture2D(
    GL_FRAMEBUFFER ,
    GL_COLOR_ATTACHMENT0 ,
    GL_TEXTURE_2D ,
    mColorTexture ,
    0
    );
glBindFramebuffer( GL_FRAMEBUFFER , 0 );

片断着色器会使用深度值对颜色填充进行渲染。正如下面代码所示的那样简洁。

#version 150
out vec4 outColor;

void main( )
{
    float lValue = gl_FragCoord.z;
    outColor = vec4( lValue , lValue , lValue , 1.0 );
}

这些修改将使纹理适用于 withclCreateFromGLTexture2D(),以便于在OpenCL的上下文共享,正如下面的部分展示的那样。

创建OpenCL context

通常执行如下步骤来创建一个OpenCL context:

List OpenCL platforms and choose one (usually the first one).
List OpenCL devices on this platform and choose one (usually the first one).
Create an OpenCL context on this device.

然而,对于多边形生成算法而言,必须注意合理分配OpenCL context,才能从现存的context中访问OpenCL资源。额外的参数将会被传递给OpenCL context创建例程来请求一个兼容的context。这意味着context创建可能会失败,例如 OpenGL context创建在一个我们试图分配一个OpenCL context的设备上。因此,创建步骤需要作适当修改以增强兼容性。

List OpenCL platforms and choose one (usually the first one).
List OpenCL devices on this platform
For each device:
    Try to allocate a context
        on this device
        compatible with current OpenGL context
    if context successfully created:
        stop

时间: 2024-10-01 07:53:49

通过 Autostereograms 案例学习 OpenGL 和 OpenCL 的互操作性 【已翻译100%】(2/3)的相关文章

通过 Autostereograms 案例学习 OpenGL 和 OpenCL 的互操作性 【已翻译100%】(1/3)

**引言 ** 在过去的十年里, GPU (图形处理单元)已经从特殊硬件(特供)转变成可以在数值计算领域开辟新篇章的高性能计算机设备. 许多算法可以使用拥有巨大的处理能力的GPU来高速执行和处理大数据量.即使在通常的情况下,不可能将图形硬件编程化, 图形硬件也可以加快算法与图像的处理. 举个例子:通常情况下可以用来计算图形差分,模糊图像, 合并图像,甚至是进行图像(或数组)平均值计算. 随后,可编程方式的出现给编程者带来了极大的便利. 可编程方式所提供的新的可能性,更广泛类别的算法可以移植到GP

通过 Autostereograms 案例学习 OpenGL 和 OpenCL 的互操作性 【已翻译100%】(3/3)

注意到所有平台都可以遍历确保正确的context 被创建.下面的代码演示了OpenCL context 的创建. cl_int lError = CL_SUCCESS; std::string lBuffer; // // Generic OpenCL creation. // // Get platforms. cl_uint lNbPlatformId = 0; clGetPlatformIDs( 0 , 0 , &lNbPlatformId ); if ( lNbPlatformId ==

开始在 OS X 开发 OpenGL 应用 【已翻译100%】

有一段时间了,我一直想着进入某些视频游戏开发领域. 对于OpenGL我也很感兴趣,并且因此我也想到了为什么不深入去研究研究OpenGL,再去做一款视频游戏呢?我已经有了许多我想要去探索的游戏的点子,但第一都是得要婴儿学步的不是吗? 我的原始计划是要去构建: 1.一个类似AI的 Pong 游戏克隆 2.一个 几何战争 游戏的克隆 3.开始着手实现属于我自己的疯狂想法 然而,我最近在浏览HackerNew时偶然发现了 (游戏机制探索器)Game Mechanic Explorer. 它是一个有Joh

《C语言程序设计》一 2.5 案例学习——华氏温度与摄氏温度的转换

2.5 案例学习--华氏温度与摄氏温度的转换 [例2-7] 你的一个朋友要去某地旅游,当地天气预报是用华氏温度报告的,请你编写程序,帮助你的朋友进行温度的转换,计算并输出对应的摄氏温度. 1.问题分析 1)确定预期的输出.问题中有"计算并输出对应的摄氏温度",明确了输出项为摄氏温度celsius. 2)确定输入项.本需求中,输入项是华氏温度fahrenheit. 3)列出输入与输出关系的公式.本例中由华氏温度计算摄氏温度的公式为celsius = 5/9(fahrenheit-32).

《C语言程序设计》一3.4 案例学习——计算一年中某月份的总天数

3.4 案例学习--计算一年中某月份的总天数 [例3-8] 从键盘上输入年和月份,打印出相应年中某月份的总天数. 1.问题分析 这个问题要求从键盘上输入年份和月份,根据年份和月份计算出当月的总天数.年份和月份要考虑输入是否合法,并且二月份要考虑是否是闰年,如果是闰年则是29天,否则是28天. 2.算法设计 显示一个请求用户输入年份和月份的提示 接收用户的输入 假如年份和月份不合法 输出提示信息 否则 假如月份是1,3,5,7,8,10,12,则是31天; 假如月份是4,6,9,11,则是30天;

从零开始学习OpenGL ES之六 – 纹理及纹理映射

转自:http://bbs.ityran.com/article-9-1.html [转载]从零开始学习OpenGL ES之六 – 纹理及纹理映射 2011-9-14 23:49| 发布者: Iven| 查看: 9206| 评论: 15 摘要: 图形图像, 编程,编程, OpenGL ES, 教程,OpenGL ES 3D 在OpenGL ES中另一种为多边形定义颜色创建材质的方法是将纹理映射到多边形.这是一种很实用的方法,它可以产生很漂亮的外观并节省大量的处理器时间.比如说,你想在游戏中造一个

《C语言程序设计》一1.4 案例学习——计算三角形的面积

1.4 案例学习--计算三角形的面积 本节应用结构化程序设计方法根据如下需求进行分析和设计: 根据公式area=计算三角形的面积,其中a.b.c为三角形的三边,s=(a+b+c)/2为三角形半周长.编写程序,用以上信息计算边长为3.4.5的三角形面积. 1.问题分析 这一步主要是确认我们对需求有一个完整的理解. 1)确定预期的输出.为了确定预期的输出,应该首先在需求中查找计算.打印等关键词,在上面的需求中,关键的一句话为"计算边长为3.4.5的三角形面积",这明确了输出项为三角形的面积

opengl-linux下使用codeblocks学习OpenGL

问题描述 linux下使用codeblocks学习OpenGL 我最近开始在linux下学习OpenGL,系统是CentOS,下载了OpenGL超级宝典第五版及源码,安装了mesa.glut.glew等依赖项,使用源码中提供的makefile编译源码能成功,可是我把GLTools文件夹拷到自己的工程文件夹下面,并设置了GLTools/include为搜索路径,也将GLTools/src下的文件都添加到了项目中,编译第一个源码的时候总是通不过,先是说GLenum.GLuint等类型没有定义,我改动

《大数据导论》一1.4 案例学习背景

  本节书摘来自华章出版社<大数据导论>一书中的第1章,第1.4节,作者托马斯·埃尔(Thomas Erl),瓦吉德·哈塔克(Wajid Khattak),保罗·布勒(Paul Buhler),更多章节内容可以访问"华章计算机"公众号查看. 1.4 案例学习背景 ETI(Ensure to Insure)是一家领先的保险公司,为全球超过2500万客户提供健康.建筑.海事.航空等保险计划.该公司拥有超过5000名员工,年利润超过3.5亿美元. 1.4.1 历史背景 ETI早在