【问题标题】:OpenGL flood fill not recognizing boundaryOpenGL洪水填充无法识别边界
【发布时间】:2019-10-26 12:46:40
【问题描述】:

我正在尝试在 OpenGL 中实现泛洪填充算法,但这样做时遇到了错误。错误在于算法不会在边界处停止,而是一直运行到窗口边缘,最终由于内存访问错误而崩溃。我正在开发 MacOS Mojave 10.14.4。

我认为我的实现逻辑是正确的,但是,我已经从getPixel 打印出每个像素的颜色,并且它始终是白色(背景颜色),即使获取边界像素的颜色也是如此。

下面的代码使用 Bresenham 的 Line 算法(中点算法)画了一个圆,然后用洪水填充它(不成功)。

#include <GLUT/GLUT.h>
#include <iostream>


struct Color {
    GLubyte r;
    GLubyte g;
    GLubyte b;
};

Color getPixelColor(GLint x, GLint y) {
    Color color;
    glReadPixels(x, y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &color);
    return color;
}

void setPixel (GLint x, GLint y) {
    glBegin(GL_POINTS);
    glVertex2i(x, y);
    glEnd();
    Color color = getPixelColor(x, y);
}


void setPixelColor(GLint x, GLint y, Color color) {
    glColor3ub(color.r, color.g, color.b);
    setPixel(x, y);
    glEnd();
    glFlush();
}

void floodFill4 (GLint x, GLint y, Color fillColor, Color interiorColor) {
    Color color = getPixelColor(x, y);
    if (color.r == interiorColor.r && color.g == interiorColor.g &&
        color.b == interiorColor.b) {
        setPixelColor(x, y, fillColor);
        floodFill4 (x + 1, y, fillColor, interiorColor);
        floodFill4 (x - 1, y, fillColor, interiorColor);
        floodFill4 (x, y + 1, fillColor, interiorColor);
        floodFill4 (x, y - 1, fillColor, interiorColor);
    }
}

void drawCirclePoint(GLint x, GLint y, GLint cx, GLint cy) {
    setPixel(cx+x, cy+y);
    setPixel(cx+y, cy+x);
    setPixel(cx-y, cy+x);
    setPixel(cx-x, cy+y);
    setPixel(cx-x, cy-y);
    setPixel(cx-y, cy-x);
    setPixel(cx+y, cy-x);
    setPixel(cx+x, cy-y);
}

void drawCircle(GLint cx, GLint cy, GLint radius) {
    int p = 1 - radius;
    GLint x = 0;
    GLint y = radius;
    while (x < y) {
        drawCirclePoint(x, y, cx, cy);
        if (p < 0) {
            x++;
            p += (2 * x) + 1;
        } else {
            x++;
            y--;
            p += (2 * x) + 1 - (2 * y);
        }
    }
}


void displayMe(void) {
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3ub(0, 0, 0);
    GLint cx = 0;
    GLint cy = 0;
    GLint radius = 200;
    // Draw head
    glColor3ub(0, 0, 0);
    drawCircle(cx, cy, radius);
    glEnd();
    glFlush();
    Color interiorColor = {255, 255, 255};
    Color fillColor = {0, 0, 255};
//    floodFill4(100, 100, fillColor, interiorColor);
}

void init (void) {
    glClearColor(1.0, 1.0, 1.0, 0.0);  // Set display-window color to white.
    glMatrixMode(GL_PROJECTION);       // Set projection parameters.
    glLoadIdentity();
    gluOrtho2D(-1000.0, 1000.0, -1000.0, 1000.0);
    glMatrixMode(GL_MODELVIEW);
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(1000, 1000);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("My Drawing");
    init();
    glutDisplayFunc(displayMe);
    glutMainLoop();
    return 0;
}

我查看了this post,看起来很相似,但找不到解决方案。

【问题讨论】:

    标签: c++ macos opengl glut flood-fill


    【解决方案1】:

    如果您要坚持使用 glVertex() 进行点绘图,请确保设置矩阵堆栈变换 (GL_PROJECTION/GL_MODELVIEW),以便它们匹配 glReadPixels() 坐标系:

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluOrtho2D( 0, glutGet(GLUT_WINDOW_WIDTH), 0, glutGet(GLUT_WINDOW_HEIGHT) );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    

    或者切换到glRasterPos() + glDrawPixels() 换成setPixel*()

    更好的是,在主机端执行洪水填充逻辑并将结果上传到 GL 纹理以进行显示。

    无论哪种方式,即使在相当合理大小的输入上,使用递归解决方案,您都会遇到堆栈溢出,因此您可能希望切换到显式堆栈/queue

    void floodFill4( GLint aX, GLint aY, Color fillColor, Color interiorColor )
    {
        typedef std::pair< GLint, GLint > Location;
        std::queue< Location > locations;
    
        locations.push( Location( aX, aY ) );
        while( !locations.empty() )
        {
            const Location loc = locations.front();
            locations.pop();
    
            GLint x = loc.first;
            GLint y = loc.second;
    
            Color color = getPixelColor( x, y );
            if( color.r == interiorColor.r &&
                color.g == interiorColor.g &&
                color.b == interiorColor.b )
            {
                setPixelColor( x, y, fillColor );
                locations.push( Location( x, y - 1 ) );
                locations.push( Location( x, y + 1 ) );
                locations.push( Location( x - 1, y ) );
                locations.push( Location( x + 1, y ) );
            }
        }
    }
    

    【讨论】:

    • 我已经用你的方法替换了我的init() 方法中的相关代码,现在它递归到(cx + radius, cy),而不是直到显示边缘,因为它应该。但是,颜色仍然保持白色,因此递归不会停止。你知道为什么会这样吗?
    • 更新:它实际上没有到达边缘 15 左右像素。我认为仍然存在错位问题,但我不确定在哪里。
    猜你喜欢
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多