使用GLKit进行按需OpenGL ES渲染

17

我正在研究将我的OpenGL渲染代码转换为利用GLKit的一些功能(特别是异步纹理加载和GLKView/Controller提供的自动化)。但是,它看起来主要是设计给使用动画循环进行渲染的人使用,而我正在处理按需渲染。此外,有些渲染是到纹理而不是GLKView帧缓冲区,所以我应该只是子类化GLKView并添加其他FBOs吗?

这种类型的设置是否有推荐的方法?我期望有以下内容:

  • 将视图控制器的preferredFramesPerSecond设置为0,或者仅暂停帧更新?
  • 忽略glkViewControllerUpdateglkView:drawInRect:方法,只在需要时绘制所需内容。
  • 像普通的UIView一样使用视图的setNeedsDisplay以显示帧(鉴于我也将渲染到纹理,是否需要调用bindDrawable?)。

也许如果这不是新API的设计用途,那么这样做就不值得努力?我希望文档能更加详尽一些。也许在API“成熟”一点时会提供更多示例...

谢谢


你仍然可以与GLKit一起使用,将渲染到辅助帧缓冲区对象。http://mickyd.wordpress.com/2012/05/20/creating-render-to-texture-secondary-framebuffer-objects-on-ios-using-opengl-es-2/ - user585968
3个回答

24
我最终采用的方法是不使用 GLKViewController,而是直接在 UIViewController 子类下使用 GLKView。显然,GLKViewController 适用于需要一致渲染循环的应用程序,例如游戏。没有它,向 GLKView 绘制就像调用 [glkView setNeedsDisplay] 一样简单。请务必将 enableSetNeedsDisplay 设置为 YES,以启用此行为。
如果您仍想使用 GLKViewController,则可以在 viewWillAppear 中禁用动画渲染循环,如下所示:
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];    // setPaused automatically set to NO in super's implementation

    [self setPaused:YES];
}

另外,将resumeOnDidBecomeActive设置为NO可防止视图控制器再次自动恢复。

然而,使用普通的UIViewControllerGLKView完全可以接受,并且我已经看到一位苹果工程师建议这是执行按需绘制的适当方式。


如果您能澄清在哪些方法中需要设置 enableSetNeedsDisplay 和 resumeOnDidBecomeActive,将会很有帮助,因为您没有将它们放在 viewWillAppear(BOOL)animated 中。谢谢。 - M-V
在创建视图/视图控制器时设置这些属性。例如,如果您在代码中创建GLKViewController,可以在alloc/init之后的一行上设置它。如果您在界面构建器中创建它,则在视图控制器中实现-awakeFromNib方法可能是一个不错的选择。通常,在对象创建后但在使用之前应设置此类属性。 - Stuart
你有开源代码可用来展示这个样例吗?谢谢。 - aehlke

3
我刚刚将我的代码从使用我自己编写的EAGLContext管理器转换为使用GLKit类。您建议可能会“忽略glkView:drawInRect:方法,只在需要时绘制所需内容”。从性能上来看,这似乎是一个明智的选择;我假设(尽管没有尝试过),如果您简单地不指定GLKViewDelegate或提供一个子类化的GLKView并定义其drawInRect,则不会发生动画循环渲染。您尝试过吗?
另一种选择是在您的MyController:GLKViewController 类中创建一些@property(assign, nonatomic) BOOL shouldUpdate;,仅在有事情要做时更新:
[self setDelegate:self]; // in init or awakeFromNib or other..

-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    if ([self shouldUpdate]) { ...

我相信你已经明白了,这并不复杂。

值得一提的是:官方API文档指出,viewDidLoad应该在你的GLKViewController中用于初始GL设置。但我遇到了问题:不知何故,我的glCreateShader调用总是返回零。这可能是由于我在初始化后设置了EAGLContext所致;因为我是在Storyboard中创建控制器,所以无法将其作为init参数传递。然而,代码逻辑上没有任何错误,所以如果你遇到类似的问题,我提供这个友好的警告。我的解决方案就是在我的drawInRect中加入以下内容:

-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    if ([self initialGLSetupDone] == NO) {
        [self beforeFirstRender];
        [self setInitialGLSetupDone:YES];
    }
    // .. rest of render code goes here.
}

显然,在那里不必要地使用IF语句并不理想,但这是一个简单的解决方案。

如果您尝试更新使用GLKit,请告诉我它的效果如何。


关于 viewDidLoad 问题,你必须在创建着色器之前设置好你的 EAGLContext,正如你自己注意到的那样。在 viewDidLoad 中这样做也没有任何问题,在设置其余的 GL 代码之前直接进行即可。在 Xcode 的“OpenGL Game”模板项目中查看一个很好的示例来执行此设置。 - Stuart
感谢锁定提示。比手动绘制CAEAGLLayer或其他解决方法要容易得多。 - Hari Honor

1
在创建GLKView之后,添加以下代码行: glkView.enableSetNeedsDisplay = TRUE; (这样就不会自动重绘视图了)
当您想要重新绘制时,请插入以下代码行:
[glkView setNeedsDisplay];

...然后drawInRect例程将只被调用一次。

希望这有所帮助。


1
-1. 这是不正确的 - 将 enableSetNeedsDisplay 设置为 NO禁用 GLKViewsetNeedsDisplay 方法。当使用 GLKViewController 时,此属性会自动设置为 NO,因此在 setNeedsDisplay "模式" 下绘制时,您实际上需要手动将其设置为 YES - Stuart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接