【问题标题】:Cuda + OpenGL : How to display elements of an arrayCuda + OpenGL:如何显示数组的元素
【发布时间】:2013-03-26 04:59:53
【问题描述】:

假设一个数据数组

int data[256][256][256];

包含值 0 和 1。 该数据将被复制到设备内存(展平)并由 cuda 内核处理,将某些值从 0 更改为 1,反之亦然。 我怎样才能以这种方式显示数据

if data[x]][y][z]==1
{
//a point of size 1 will be displayed in (x,y,z) coords
}

无需将该数组复制回主机内存。我想直接从设备(gpu)内存中完成。 一个简单的例子会很有帮助。

【问题讨论】:

  • CUDA 的 SDK 示例 volumeRender 可能有助于了解 OpenGL 和 Cuda 如何与 3D 体积对象一起工作。因为显示它不是那么简单,你必须使用 OpenGL 管道,一个例子实际上是一个 OpenGL 教程。

标签: arrays opengl cuda


【解决方案1】:

想到的第一种方法是首先使用简单的流压缩技术,从流中消除所有 0 值。由于您已经将数据采用了概念上的布尔格式,因此这相当于这些值的 scan/prefix-sum,以便之后每个数组元素存储其前面元素的数量。然后,您使用这些计算的偏移量将数组元素重新定位到新缓冲区(有效地连续存储所有 1 项),但不是仅复制(现在不相关的)偏移量,而是存储 3D 位置(可以从旧数组索引轻松计算) )。

然后,您拥有的是缓冲区中的 3D 点的简单列表,您可以使用 OpenGL 轻松绘制,无需任何副本到主机。如果您将最终的 1 点数(扫描的副产品)写入额外的缓冲区,您甚至可以使用 间接渲染,甚至不需要从主机,因此可以在没有任何回读的情况下绘制整个内容。

这些只是关于这可能如何工作的一些一般想法,如果您不知道我在说什么,请随时在 Google 上搜索各个关键字。如果您不知道如何使用 OpenGL 绘制任何东西,则需要先熟悉 OpenGL 或 Direct3D,然后才能从 GPU 绘制任何东西(CUDA 不会为您做这件事)。


编辑:嗯,有一种方法可以在 CUDA 中绘制整个东西。如果它真的只是一堆点并且你不需要任何复杂的光栅化,你也可以像 OpenGL 那样简单地将这些点从 3D 转换为 2D(OpenGL 也不是魔法,只是一堆 CUDA 内核和一些小的之间的专用硬件位)。因此,每个数据项都有一个线程,如果该项是1,您只需使用通常的模型视图和均匀 4D 坐标上的投影变换来变换点的 3D 坐标(由线程 ID 给出)(但不要忘记自己做透视分割和视口变换,因为现在没有固定功能的硬件可以为你做这件事)。并使用这个最终的 2D 坐标在输出图像中设置一个像素。

当然,这不会让您从 OpenGL 的其他复杂事物中获利(至少不是那么容易或直接),例如光栅化以及您能想到的其他效果。但它避免了流压缩步骤,您至少可以在已经存在的 OpenGL 渲染图像(包含可视化场景的其余部分,如坐标系或文本或其他)之上渲染这些点,甚至可以使用您自己的深度测试实现。


编辑: 避免流压缩的另一种方法是直接使用 OpenGL 绘制 0s 和 1s 的整个缓冲区,使用几何着色器进行坐标计算和0-删除。因此,您所做的是绘制所有点,使用整个整数数组来提供单个 int 属性并将其放入以下着色器。首先是一个简单的直通顶点着色器:

layout(location=0) in int flag;    //single integer attribute
out int vFlag;                     //just passed through

void main()
{
    vFlag = flag;        //GS does the real work
}

几何着色器决定该点是否有效(值为1),如果是则为它发出一个实际点(如果不是什么都不做,即没有渲染点),从图元计算它的 3D 坐标ID,实际上是数组索引,因为我们只是在绘制点:

layout(points) in;
layout(points,max_vertices=1) out;

uniform mat4 modelViewProj;

in int vFlag[];

void main()
{
    if(vFlag[0] == 1)
    {
        vec3 position = simpleIndexMagic(gl_PrimitiveID);
        gl_Position = modelViewProj * vec4(position, 1.0);
        EmitVertex();
    }
}

片段着色器只是一个普通的着色器。当然,这些着色器可以按照您认为合适的方式进行定制。但是一般的方法很清楚,直接绘制整数值,将数组索引转换为仅在几何着色器中为1 的值的点。但是,与之前的流压缩相比,这在多大程度上会有所改进,需要评估。

【讨论】:

  • 我还在努力理解上面的意思。事实是我对opengl或cuda不是很熟悉。我以前在opengl中绘制顶点的方式是code glBegin(GL_POINTS); if (volume[p,q,r]==1) glVertex3f(p, q, r); glEnd();我想要这样的东西,所以它可以在cuda内核中调用。
  • @PhilipXenos “我想要这样的东西,所以它可以在 cuda 内核中调用。” - 那是不可能的。 CUDA 内核不能调用 OpenGL 操作。但是我建议您在尝试理解此答案之前先阅读有关 modern OpenGL 的优秀教程或书籍,因为您使用的是 15 年前的 CUDA 硬件但 OpenGL 功能。如果您对缓冲区对象或着色器或现代 GPU 的使用方式一无所知,那么您真的不会有太多帮助。正如 George 在他的评论中所写,提供 OpenGL 教程不是这个答案的目的。
  • @PhilipXenos 如果您对 CUDA 和 OpenGL 不太熟悉,第一种方法可能是最佳选择。首先,您可以查看here,了解前缀和和 CUDA 流压缩的一些介绍。然后查看herehere 了解顶点缓冲区对象的一些介绍。然后,您只需要一些 CUDA-OpenGL 互操作性函数来直接使用压缩的 CUDA 缓冲区作为 OpenGL 顶点缓冲区来绘制点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-04
  • 2020-04-07
  • 1970-01-01
  • 2015-03-07
  • 1970-01-01
  • 1970-01-01
  • 2021-04-10
相关资源
最近更新 更多