如何在Mac OS上使用Retina显示屏在OpenGL中绘制文本

5
我正在使用OpenGL在Mac OS上进行绘图。当我的代码在Retina显示器上运行时,除了文本绘制外,一切都正常。 在Retina显示器下,文本的大小是应该大小的两倍。这是因为字体大小以点为单位,而在Retina下每个点为2个像素,但OpenGL是基于像素的。
这是标准显示下正确的文本绘制: Correct text drawing under standard display 这是Retina显示下不正确的文本绘制: Incorrect text drawing under Retina display 这是我通常绘制字符串的方式。由于OpenGL没有文本绘制函数,为了绘制文本,我执行以下操作:
1.获取字体: NSFontManager fontManager = [NSFontManager sharedFontManager]; NSString font_name = [NSString stringWithCString: "Helvetica" encoding: NSMacOSRomanStringEncoding]; font = [fontManager fontWithFamily: font_name traits:fontStyle weight:5 size:9]; attribs = [[NSMutableDictionary dictionaryWithCapacity: 3] retain]; [attribs setObject:font forKey:NSFontAttributeName];
2.创建和测量字符串: NSString* aString = [NSString stringWithCString: "blah blah" encoding: NSMacOSRomanStringEncoding]; NSSize frameSize = [aString sizeWithAttributes: m_attribs];
3.分配具有尺寸的NSImage: NSImage* image = [[NSImage alloc] initWithSize:frameSize]; [image lockFocus];
4.将字符串绘制到图像中: [aString drawAtPoint:NSMakePoint (0, 0) withAttributes:m_attribs];
5.获取位: NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, frameSize.width, frameSize.height)]; [image unlockFocus];
6.创建OpenGL纹理: GLuint texture = 0; glGenTextures(1, &texture); glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, GLsizei(frameSize.width), GLsizei(frameSize.height), 0, GL_RGBA, [bitmap bitmapData]);
7.绘制纹理: glBindTexture …. 其他OpenGL绘图代码
我的问题是如何使NSString以像素分辨率绘制,而不是点数。 我尝试了以下方法:
  1. 使用一半的字号进行绘制:4.5而不是9。这样可以得到正确的大小,但文本会变得模糊。
  2. 在OpenGL中以点的大小进行绘制,并将纹理缩小到一半的大小,但这样并不会得到好看的结果:
    Texture shrink
3个回答

1

OpenGL的坐标系统实际上是基于点而不是像素的,但你可以决定这些点是什么。通过glOrtho函数(或手动构建正交矩阵),上下文定义了2D坐标系,设置屏幕上的最小和最大x、y坐标。例如,正交投影可以被设置为0在屏幕左侧,100在右侧,而不管你渲染到的帧缓冲区的大小。

字体纹理似乎创建得很好。问题在于你将其渲染到了比它需要的两倍大的几何体上。在OpenGL中,纹理大小不会影响屏幕上渲染的对象大小。屏幕上的大小由传递给glDrawArrays或glBegin等函数的几何体定义,而不是纹理。

我认为问题在于你使用字体纹理的像素大小来定义用于屏幕渲染的四边形大小。这将把你的问题放在“其他OpenGL绘图代码”部分。要解决这个问题,你可以对绘图应用某种比例因子。在视网膜模式下,比例因子为0.5,在普通屏幕上为1.0(UIKit使用类似的思路来渲染UIView内容)

四边形计算可能看起来像这样:

quad.width = texture.width * scaleFactor 
quad.height = texture.height * scaleFactor

另一个选择是将四边形渲染大小与纹理完全分离。如果您有一个用于绘制文本的函数或类,它可以具有字体大小参数,该参数将用作实际四边形大小,而不是使用纹理大小。

谢谢Justin。我尝试了创建2倍大小的纹理并将其绘制到实际像素大小的方法。我编辑了问题并添加了一张图片来展示这样做效果不好。谢谢Shai。 - Periodic Maintenance
@user558738 你的帧缓冲区大小是视网膜屏幕尺寸吗?还是普通屏幕分辨率,只是被拉伸了?如果是这种情况,那么我认为你无法充分利用视网膜文本。 - Justin Meiners
这是Mac OS负责将OpenGL缓冲区拉伸到视网膜分辨率。当我在应用程序上执行“获取信息”并选择“以低分辨率打开”时,我就没有这个问题了。至于利用视网膜文本的方法,那正是我的问题:“在离线绘制文本时,如何不利用视网膜文本?” - Periodic Maintenance
如果操作系统负责将您的缓冲区缩放,则您完全取决于它们,如果这就是 Mac 设计工作的方式,那么这是一种极其糟糕的设计。如果没有办法更改帧缓冲大小,您将不得不像非 retina 屏幕一样在低分辨率下降低文本大小。 - Justin Meiners

0

对于我的Retina显示器,我遇到了帧缓冲区不适合实际窗口的问题(完整的缓冲区渲染为窗口的四分之一)。在这种情况下,使用加倍的视口可以解决问题。

# Instead of actual window size width*height,
# double the dimensions for retina display 

glViewport(0, 0, width*2, height*2)

在你的情况下(这只是一种假设,因为我无法运行你的代码),将你传递给纹理创建gl的框架大小进行更改可能会解决问题。
GLsizei(frameSize.width*2), GLsizei(frameSize.height*2)

0

假设您有一个NSBitmapImageRep并从中获取像素光栅数据,那么在从位图创建纹理时应使用bitmap.pixelsWidebitmap.pixelsHigh而不是frameSize


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