OpenGL中的亚像素渲染 - 精度问题

7
我需要为摄影测量应用程序生成一系列测试图像。这些图像应包含简单的对象(圆盘,矩形等),其位置非常精确。
考虑到黑色矩形在白色背景上的8位灰度图像,在插值后最小可观测位移应为1/256像素,因为每个像素有256个可能的强度级别。
我决定使用OpenGL(具有python + pyglet)来渲染这些图像,因为后来我将不得不渲染更复杂的(3D场景,立体图像对)。
不幸的是,我已经实现的最佳精度大约是每个像素/10,完整的强度深度没有被使用。
是否可能做得更好,理想情况下 - 实现完全的1/256像素的准确性?如果有,请提供如何达到这一目标的任何提示?
示例代码,生成部分圆盘图像,每帧移动0.01个像素。
#-*- coding: utf-8 -*-
import pyglet
from pyglet.gl import *
from PIL import Image, ImageGrab

config = pyglet.gl.Config(width = 800, height = 600, sample_buffers=1, samples=16)
window = pyglet.window.Window(config=config, resizable=True) 
window.set_size(800, 600)

printScreenNr = 0

@window.event
def on_draw():
     global printScreenNr
     window.clear()
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

     glLoadIdentity()
     glEnable(GL_LINE_SMOOTH)
     glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

     glTranslated(200, 200,0)

     circleQuad = gluNewQuadric()
     glTranslated(200+(printScreenNr*0.01), 0, 0)
     gluPartialDisk(circleQuad, 0, 50, 500, 500, 0, 180)

@window.event
def on_resize(width, height):
    glViewport(0, 0, width, height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho (0, width, height, 0, 0, 1);
    glMatrixMode(GL_MODELVIEW)
    return pyglet.event.EVENT_HANDLED

@window.event
def on_text(text):
    global printScreenNr
    if(text == "p"):
        pyglet.image.get_buffer_manager().get_color_buffer().save('photo'+str(printScreenNr)+'.tiff')
        printScreenNr += 1

pyglet.app.run()

以上代码使用的是gluPartialDisk,但我也使用了四边形测试了该问题,结果的准确性没有区别。


你尝试过使用32位色深吗?我这么说的原因是,32位路径是现今图形驱动程序中最常用和最维护的代码路径。如果这样可以解决问题,那么之后你就可以进行高质量的色深缩放。 - Ani
尝试过了,但结果没有改变... 仍然谢谢。 - Pietro
2个回答

4

一个简单的方法就是按照比例缩放图片。如果按照4.0的比例进行缩放,则16个原始像素将合并为一个目标像素,这样在缩放纯黑白图像时会得到16种灰度。

但这里有一个问题,可能会导致你遇到的问题。如果你有这样的情况:

  ...#####
  ...#####
  ...#####
  ...#####

(左侧:白色,右侧:黑色填充矩形),那么您将有12个白色像素和4个黑色像素对一个输出像素进行贡献。要获得1个黑色像素,输入需要是这样的:
  ....####
  ....####
  ....####
  ...#####

看到了吗?即使黑盒子只泄漏一个像素到白色空间中,但它会泄漏四次。因此,为确保您的亚像素渲染代码正常工作,您需要查看单个像素或角落,而不是边缘。


渲染一个(更大的)图像可能是一个解决方案,但我认为屏幕的大小是一个限制?所以这不会帮助太多。至于你回答的另一部分,我不确定我是否理解正确...如果图像在每个方向上缩放了4倍,那么在观察边缘时会产生4种可能的阴影(在观察点时会产生16种阴影),但仍然应该有一种方法能够以完整深度精度生成定位的边缘。 - Pietro
边缘只有四个不同的位置,因此您只能获得16种中的4种。 - Aaron Digulla
假设图像已经按256倍缩放。 边缘将有256个位置,而点则有256^2个(将四舍五入)。我不知道在OpenGL中是否可以做到这样的事情(在这种情况下性能不是问题), 但我认为满足这些要求的图像通常是可以生成的。我的问题只是:如何做到这一点... - Pietro
你可能想要按16进行缩放(16x16 = 256)。将其渲染到离屏纹理中,然后将纹理缩放到一个完全适合屏幕的矩形上。 - Aaron Digulla
感谢您提供离屏纹理建议。但是,我不想按16缩放 - 我需要每个方向至少0.01像素的精度,因此这还不够。最大纹理尺寸受限,因此我不知道在OpenGL中是否可能实现。 - Pietro
PIL应该能够创建(几乎)任何大小的图像。如果您将原始图像创建为B / W(每像素1位),则应该能够使其变得非常巨大。 - Aaron Digulla

1

即使您正在使用正交投影,GL_PERSPECTIVE_CORRECTION_HINT 对渲染精度可能会产生影响。至少我依稀记得很多年前使用 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); 修复了我的正交投影场景中的一些间隙。


谢谢,已添加,目前没有改变结果,但也许将来需要3D模式。 - Pietro

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