V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
razrlele
V2EX  ›  问与答

OpenGL 种子填充算法相同代码在 Linux 下正常运行,在 Windows 下爆栈或无法得到正确结果

  •  
  •   razrlele · 2015-05-25 23:04:13 +08:00 · 2061 次点击
    这是一个创建于 3462 天前的主题,其中的信息可能已经有所发展或是发生改变。
    用C++写的种子填充模拟程序,两个程序一个是递归的一个是非递归的,在Linux上运行完美(Archlinux,freeglut),两个程序都运行正常,效果图都如下:


    在Windows 8.1 + Codeblocks 13.12 上运行程序结果不正确,如下图:

    递归版本:



    循环版本:



    在Windows 8.1 + Visual Studio 2013 + freeglut 上运行程序爆栈,如下图:

    递归版本:



    循环版本:



    程序源代码:

    循环版本:



    递归版本:



    感觉代码的核心部分应该是没有问题的,求OpenGL老司机带一脚,指点一下问题可能在哪里。。。
    10 条回复    2015-05-26 22:26:02 +08:00
    canautumn
        1
    canautumn  
       2015-05-26 05:36:49 +08:00
    不明觉厉,不过我在Mac下运行了你的程序,没出错不过结果和以上四个图都不一样,只涂了左半边多一点。
    razrlele
        2
    razrlele  
    OP
       2015-05-26 07:43:08 +08:00 via iPhone
    @canautumn 是没有完全填充么?
    canautumn
        3
    canautumn  
       2015-05-26 08:51:20 +08:00
    comicfans44
        4
    comicfans44  
       2015-05-26 08:59:35 +08:00   ❤️ 1
    https://www.opengl.org/documentation/specs/version1.1/glspec1.1/node44.html#SECTION00630000000000000000

    这个问题应该是你绘制点时使用的坐标不正确,原来的代码是绘制在整数坐标点上,导致相邻的两个像素都可能被填充成了newColor,后续readPixel读取时也就不准确了。浮点误差的积累就导致了readPixel一段读出来是oldColor,一段读出来是newColor,导致你的填充结果是一段有一段没有。

    因此你的setPixel应该是

    glVertex2f(x+0.5,y+0.5)


    依靠读取帧缓存进行点判断很难保证不同平台的统一,至于linux和windows表现不同只能是因为边界条件下不同实现的表现不一致。
    razrlele
        5
    razrlele  
    OP
       2015-05-26 10:53:34 +08:00
    @comicfans44

    把setPixel改成了glVertex2f(x+0.5, y+0.5)在Windows下递归算法还是会爆栈,而循环算法则是错误结果(按道理把应该只会碰见黑色像素才会填充但是结果把红色边框也填充成绿色了,所以应该还是读取像素错误),与此同时在Linux下运行正确。

    后来我还尝试直接使用实心矩形,Windows递归算法下依旧爆栈,并且跟边框多边形爆栈过程几乎一致(先不断地向上填充然后上半部分还没有填充完就爆栈),循环算法下则是没有出爆栈,最后结果也是成功填充,但是填充过程却不正确(左下部分最后填充,按道理应该是均匀渐渐填充的)。。。

    期间还尝试直接避免浮点数,把所有的像素数据类型换成unsigned byte,然而并没有用。。。

    能不能麻烦您再指导一下该如何正确地绘制坐标以及填充像素。。。谢谢!
    razrlele
        6
    razrlele  
    OP
       2015-05-26 11:28:37 +08:00
    @comicfans44 目测是编译器的锅,各种CB可以跑VS不能跑。。。。
    comicfans44
        7
    comicfans44  
       2015-05-26 12:35:07 +08:00   ❤️ 1
    @razrlele 仔细观察填充过程,红色边框是没有填充成绿色的,因为你的draw_rec没有设定glColor,填充完点之后glColor还是点的颜色,所以画出来边框也是绿色了
    comicfans44
        8
    comicfans44  
       2015-05-26 12:37:20 +08:00   ❤️ 1
    @razrlele linux看以来边框没变色应该是没有触发重绘事件,没有重绘边框。不然也会变绿。你可以在绘制完拖动下窗口或者最大化最小化一下确认这个现象
    comicfans44
        9
    comicfans44  
       2015-05-26 18:08:06 +08:00   ❤️ 1
    @razrlele 指导是不敢说的,我也不是完全明白,遇到需要准确确定像素位置的情况,最好是去查opengl 的spec,主要是光栅化部分的说明,也就是之前链接的那部分。

    楼主的主攻方向是什么?如果OpenGL只是用来画图表现结果,那我推荐你使用基于内存的填充和判断,最终只用OpenGL把结果画出来(这样可以避免OpenGL相关的很多细节问题)

    如果主攻的是计算机图形学,那建议配合一本按照OpenGL讲计算机图形学的书来对照学习。正确绘制坐标的部分主要是参考 矩阵变换,透视投影,归一化,OpenGL的光栅化spec这些内容。将你的绘制坐标按照浮点数带入公示计算,相信你可以明确知道你画的点最终出现在屏幕的哪个像素上。

    apitrace是一个很好的跟踪工具,用它来跟踪你的程序,可以回放到任意一个OpenGL函数上查看结果
    razrlele
        10
    razrlele  
    OP
       2015-05-26 22:26:02 +08:00
    @comicfans44

    其实这只是一个CS小本图形学实验课作业罢了。。。

    自己有买蓝宝书。。。但是翻了一下感觉似乎没有glReadPixel这些函数(是太老太过时了?)。。。最后上OpenGLl的man page去找也没有找到相关细节问题(当然也有可能看的不仔细),现在我感觉问题有可能出现在环境配置相关,因为同是Windows系统下结果也会五花八门,我断点调试之后发现问题的关键都是各种诡异的像素读取错误。。。

    基于内存的填充和判断还没有尝试过,这个我再去研究一下。。。

    这两天是真心被OpenGL折腾怕了 冏rz~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2857 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 07:46 · PVG 15:46 · LAX 23:46 · JFK 02:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.