【问题标题】:How do you render OpenGL-ES to an external screen using the VGA out adapter?如何使用 VGA 输出适配器将 OpenGL-ES 渲染到外部屏幕?
【发布时间】:2011-01-25 00:47:27
【问题描述】:

我一直在为 iPad 和 iPhone 开发一个 3D 程序,并希望能够将它渲染到外部屏幕上。根据我的理解,您必须执行类似于以下代码的操作才能实现它,(位于:Sunsetlakesoftware.com):

if ([[UIScreen screens] count] > 1)
{
    // External screen attached
}
else
{
    // Only local screen present
}

CGRect externalBounds = [externalScreen bounds];
externalWindow = [[UIWindow alloc] initWithFrame:externalBounds];

UIView *backgroundView = [[UIView alloc]  initWithFrame:externalBounds];
backgroundView.backgroundColor = [UIColor whiteColor];

[externalWindow addSubview:backgroundView];

[backgroundView release];

externalWindow.screen = externalScreen;
[externalWindow makeKeyAndVisible];

但是,我不确定要对 OpenGL 项目进行哪些更改。有谁知道你会怎么做才能在 XCode 中将它实现到用于 iPad 或 iPhone 的默认 openGL 项目中?

【问题讨论】:

    标签: iphone objective-c xcode ipad opengl-es


    【解决方案1】:

    在外部显示器上渲染 OpenGL ES 内容所需要做的就是创建一个由 CAEAGLLayer 支持的 UIView 并将其添加为上述 backgroundView 的子视图,或者采用这样的视图并移动它成为backgroundView的子视图。

    事实上,您可以根据需要删除backgroundView,并将您的OpenGL 托管视图直接放在externalWindow UIWindow 实例上。该窗口附加到表示外部显示器的 UIScreen 实例,因此放置在其上的任何内容都将显示在该显示器上。这包括 OpenGL ES 内容。

    特定类型的 OpenGL ES 内容似乎确实存在问题,正如您在我尝试添加到我的 Molecules 应用程序的实验性支持中看到的那样。如果您查看那里的源代码,我会尝试将我的渲染视图迁移到外部显示器,但它从未出现。我对其他 OpenGL ES 应用程序也做过同样的事情,并且它们的内容渲染得很好,所以我相信外部显示器上的深度缓冲区可能存在问题。我仍在努力追查。

    【讨论】:

    • 如果我的解决方案可以帮助您正确显示分子代码,请告诉我。对于轻几何来说,它并不像只渲染两次那样快,但是对于我正在处理的重几何来说,它可能会更快,因为它不取决于您要渲染的多边形数量。我展示的一些模型有超过 800,000 个三角形。当然我的帧率只有大约 2fps。
    • @Davido - 在我的基准测试中,这将是一种将内容显示到外部屏幕的非常慢的方式。 glReadPixels() 是一个缓慢的操作,但不如将图像绘制到 Core Graphics 上下文然后更新相应的 UIImageView 慢。对于 1024x768 显示器,我怀疑您是否可以通过这种方式实现 30-60 FPS 的刷新率。这是一个有趣的想法,但在这种情况下不切实际。
    【解决方案2】:

    我已经弄清楚如何让任何 OpenGL-ES 内容呈现到外部显示器上。这实际上非常简单。您只需将渲染缓冲区复制到 UIImage,然后在外部屏幕视图上显示该 UIImage。获取渲染缓冲区快照的代码如下:

    - (UIImage*)snapshot:(UIView*)eaglview
    {
    // Get the size of the backing CAEAGLLayer
    GLint backingWidth, backingHeight;
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, defaultFramebuffer);
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
    
    NSInteger x = 0, y = 0, width = backingWidth, height = backingHeight;
    NSInteger dataLength = width * height * 4;
    GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));
    
    // Read pixel data from the framebuffer
    glPixelStorei(GL_PACK_ALIGNMENT, 4);
    glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
    
    // Create a CGImage with the pixel data
    // If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel
    // otherwise, use kCGImageAlphaPremultipliedLast
    CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
                                    ref, NULL, true, kCGRenderingIntentDefault);
    
    // OpenGL ES measures data in PIXELS
    // Create a graphics context with the target size measured in POINTS
    NSInteger widthInPoints, heightInPoints;
    if (NULL != UIGraphicsBeginImageContextWithOptions) {
        // On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
        // Set the scale parameter to your OpenGL ES view's contentScaleFactor
        // so that you get a high-resolution snapshot when its value is greater than 1.0
        CGFloat scale = eaglview.contentScaleFactor;
        widthInPoints = width / scale;
        heightInPoints = height / scale;
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(widthInPoints, heightInPoints), NO, scale);
    }
    else {
        // On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
        widthInPoints = width;
        heightInPoints = height;
        UIGraphicsBeginImageContext(CGSizeMake(widthInPoints, heightInPoints));
    }
    
    CGContextRef cgcontext = UIGraphicsGetCurrentContext();
    
    // UIKit coordinate system is upside down to GL/Quartz coordinate system
    // Flip the CGImage by rendering it to the flipped bitmap context
    // The size of the destination area is measured in POINTS
    CGContextSetBlendMode(cgcontext, kCGBlendModeCopy);
    CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref);
    
    // Retrieve the UIImage from the current context
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    // Clean up
    free(data);
    CFRelease(ref);
    CFRelease(colorspace);
    CGImageRelease(iref);
    
    return image;
    }
    

    尽管出于某种原因,我一直无法让 glGetRenderbufferParameterivOES 返回正确的 backingwidth 和 backingheight,所以我不得不使用自己的函数来计算它们。只需将其弹出到您的渲染实现中,然后使用计时器将结果放在外部屏幕上。如果有人可以对此方法进行任何改进,请告诉我。

    【讨论】:

    • 如果您只是想将渲染的内容镜像到外部显示器,更高效的方法可能是使用 glReadPixels() 获取当前渲染的场景,然后更新渲染到quad 在外部显示器上的 OpenGL ES 层中。这将避免昂贵的 Core Graphics 图像重绘和 UIImageView 的更新。我在这个 OpenGL ES 2.0 示例应用程序中做了类似的事情:sunsetlakesoftware.com/sites/default/files/ColorTracking.zip
    • 这是个好主意。我会尝试一下,看看它是否有效。我没有意识到 CoreGraphics 的更新速度如此之慢。我认为这只是因为我有一个半大型模型(100k 面)。如果能在外屏上获得超过 3-4 fps 就好了!
    • 顺便问一下,示例 ColorTracking.zip 应用程序是做什么的?我没有 iPhone 来测试它,它在 iPad 上似乎没有任何作用。
    • 我在这里解释一下:sunsetlakesoftware.com/2010/10/22/…,但它使用 iPhone 的摄像头执行基于颜色的实时对象跟踪。是的,没有连接相机很难使用,但是您可以提取像素读取和纹理映射部分(尽管您需要从我在这里使用的基于 OpenGL ES 2.0 着色器的方法中转换它们)。
    • 我需要将 3D 渲染转换为 UIImage,这似乎可行,除非我使用多重采样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-08
    • 2014-08-10
    • 2012-08-22
    相关资源
    最近更新 更多