【问题标题】:How to manipulate texture content on the fly?如何动态操作纹理内容?
【发布时间】:2011-04-22 16:55:51
【问题描述】:

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

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

这样的事情有没有有效的技术? 我正在尝试了解需要完成的工作以及操作的繁重程度。

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

【问题讨论】:

  • @arul,这个问题已经很老了,但今天人们很可能会使用 ES2。我会对 ES2 非常感兴趣。
  • 这里真的不是问OpenGL的地方吗?
  • @Radu 为什么这个地方不适合问OpenGL....这是一个编程网站,这个问题绝对没问题
  • @RohanKapur,但还没有答案……我敢打赌,即使我为赏金多加 1000 分,也没有人会给出好的答案。
  • 哦,哈哈,哎呀,我以为你在降级/降级这个问题,抱歉哇这个问题很久以前就被问过了吧?

标签: ios ipad opengl-es textures


【解决方案1】:

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

1.更新像素(我假设这就是你在问题中的意思)

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

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

您说您希望它与 OpenGL ES 一起工作,所以我建议执行以下步骤:

  • 将 glTexImage2D() 替换为 glTexSubImage2D() - 如果您获得足够的性能,就这样吧;
  • 使用 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、补丁。

【讨论】:

  • 您能否发布一个示例,说明如何在带有 OpenGL ES 2.0 的 iOS 中使用 FBO 更改纹理中的像素?
  • 我仍然不确定我是否理解它是如何工作的。假设您有一个 10 x 10 像素阵列(= 300 GLubytes,每种颜色 1 GLubyte)。 1) 你将如何使用 FBO 将这个像素数组渲染到纹理? 2) 然后你将如何使用 FBO 更改纹理中的一些像素? (我知道你不能有 10 x 10 的纹理,因为尺寸不是 POT,只是为了说明目的)
  • 绑定您的帧缓冲区,绑定纹理(如您所说的 10x10)并使用普通着色器在所需位置渲染四边形。
  • 1) 您可以从该数组创建初始纹理。就像您使用普通纹理一样。 2) 您应该使用 OpenGL 命令和图元渲染“像素”。
  • @SergeyK.,因此,如果您需要使用其他像素更改某些坐标处的像素(不涉及几何,而是原始像素颜色),FBO 没有帮助吗?在 iOS 上,glTexSubImage2D() 是最快的方法吗?
【解决方案2】:

我尝试了两件事可能会解决您的问题。这些方法非常不同,所以我想您的具体用例将确定哪个是合适的(如果有的话)。

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

第二种方法更类似于您在问题中概述的内容。创建纹理时,请保留该数据,以便您可以直接在那里操作像素。然后调用类似

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

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

我希望这会有所帮助,尽管它的细节很少。

【讨论】:

    【解决方案3】:

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

    所以,我们有:

    1. TW 由 TH 屏幕外缓冲区(与纹理相关联) - 我们称之为 Dest
    2. 新的“像素数组”,IW by IH 纹理 - 我们称之为 Src
    3. 希望将 (IW x IH) 纹理放置到 Dest 纹理的位置 (TX,TY)

    将 Src 放到 Dest 的技巧是

    1. 将生成的 FBO 绑定为渲染目标
    2. 使用具有平凡顶点坐标 (TX,TY)、(TX+IW,Y)、(TX+IW,TY+IH)、(TX,TY+IH) 和纹理坐标 (0 ,0), (1,0), (1,1), (0,1)
    3. 绑定一个普通像素着色器,它读取绑定到第一个纹理单元的 Src 纹理并将其输出到“屏幕”(也称为渲染目标,Dest)
    4. 渲染四边形
    5. 取消绑定 FBO

    要正确渲染 Src,您必须使用正交投影和身份相机变换。

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

    希望这能解决 FBO 的问题。

    【讨论】:

      猜你喜欢
      • 2015-09-30
      • 2010-12-18
      • 2017-02-20
      • 1970-01-01
      • 1970-01-01
      • 2017-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多