如何动态操纵纹理内容?

时间:2010-10-08 04:12:02

标签: ios ipad opengl-es textures

我正在处理一个iPad应用程序,我们正在考虑的一个可能功能是允许用户触摸图像并使其变形。

基本上,图像就像一幅画,当用户将手指拖过图像时,图像会变形,触摸的像素将沿着图像“拖动”。很抱歉,如果这很难理解,但最重要的是我们想要在用户与之交互时动态编辑纹理的内容。

对于像这样的事情,有没有一种有效的技术? 我试图弄清楚需要做什么以及操作会有多重。

现在,我唯一能想到的是根据触摸的位置搜索纹理内容并复制像素数据,并在手指移动时对现有像素数据进行某种混合。然后定期用glTexImage2D重新加载纹理以获得此效果。

3 个答案:

答案 0 :(得分:31)

至少有两种根本不同的方法:

<强> 1。更新像素(我认为这就是你在问题中的意思)

更改纹理中像素的最有效技术称为渲染到纹理,可以通过FBOs在OpenGL / OpenGL ES中完成。在桌面OpenGL上,您可以使用像素缓冲区对象(PBOs)直接在GPU上操作像素数据(但OpenGL ES尚不支持)。

在未扩展的OpenGL上,您可以更改系统内存中的像素,然后使用glTexImage2D / glTexSubImage2D更新纹理 - 但这是效率低下的最后解决方案,应尽可能避免使用。 glTexSubImage2D 通常要快得多,因为它只更新现有纹理内的像素,而glTexImage2D创建全新纹理(作为一个好处,你可以改变纹理的大小和像素格式)。另一方面,glTexSubImage2D只允许更新纹理的一部分。

您说您希望它与OpenGL ES一起使用,因此我建议您执行以下步骤:

  • 用glTexSubImage2D()替换glTexImage2D() - 如果你获得了足够的性能,就让它成为现实;
  • 使用FBO和着色器实现渲染到纹理 - 它将需要 更多的工作来重写你的代码,但会更好 性能

对于FBO,代码可能如下所示:

// setup FBO
glGenFramebuffers( 1, &FFrameBuffer );
glBindFramebuffer( GL_FRAMEBUFFER, FFrameBuffer );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, YourTextureID, 0 );
glBindFramebuffer( GL_FRAMEBUFFER, 0 );

// render to FBO
glBindFramebuffer( GL_FRAMEBUFFER, FFrameBuffer );
glViewport( 0, 0, YourTextureWidth, YourTextureHeight );
your rendering code goes here - it will draw directly into the texture
glBindFramebuffer( GL_FRAMEBUFFER, 0 );

// cleanup
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
glDeleteFramebuffers( 1, &FFrameBuffer );

请记住,并非所有像素格式都可以呈现。 RGB / RGBA通常很好。

<强> 2。更新几何

您还可以更改纹理所映射的对象的几何图形。应该对几何体进行足够的细分以允许平滑的交互并防止出现伪影。几何变形可以通过不同的方法完成:参数曲面,NURBS,补丁。

答案 1 :(得分:2)

我尝试过两件可能解决问题的方法。方法是完全不同的,所以我想你的具体用例将决定哪一个是合适的(如果有的话)。

首先,我用几何体完成了图像变形。也就是说,将我的纹理映射到细分网格,然后将此网格与另一个贝塞尔控制点网格重叠。然后,用户将移动这些控制点,从而以平滑的方式变形渲染几何体的顶点。

第二种方法更类似于您在问题中提出的方法。在创建纹理时,请保留周围的数据,以便您可以直接操作像素。然后打电话给

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

每次画画。这可能非常低效,因为它可能涉及每帧重新上传所有纹理数据,我很想听听是否有更好的方法 - 比如直接操作GPU上的纹理数据。就我而言,虽然表现足够。

我希望这会有所帮助,即使它的细节相当低。

答案 2 :(得分:2)

使用FBO修改纹理渲染目标很棘手,但非常简单。

所以,我们有:

  1. TW by TH offscreen buffer(与纹理相关联) - 我们称之为Dest
  2. 新的“像素数组”,IW by IH纹理 - 我们称之为Src
  3. 希望将(IW x IH)纹理放置在Dest纹理中(TX,TY)
  4. 将Src“放”到Dest的技巧是

    1. 将生成的FBO绑定为渲染目标
    2. 使用具有平凡顶点坐标(TX,TY),(TX + IW,Y),(TX + IW,TY + IH),(TX,TY + IH)和纹理坐标(0)的虚拟4顶点四边形,0),(1,0),(1,1),(0,1)
    3. 绑定一个普通的像素着色器,它读取绑定到第一个纹理单元的Src纹理并将其输出到“屏幕”(a.k.a.渲染目标,Dest)
    4. 渲染四边形
    5. 取消绑定FBO
    6. 要正确渲染Src,您必须使用正交投影和身份相机变换。

      我的4步解决方案中的(TX,TY)和(IW,IH)坐标必须分别用TW和TH分割才能正确映射到[0..1,0..1]帧缓冲区大小。为了避免着色器中的这些划分,您可以使用适当的正交投影[0..TW,0..TH]视口。

      希望这能解决FBO的问题。