【问题标题】:Getting smooth, big points in OpenGL在 OpenGL 中获得平滑的大点
【发布时间】:2009-10-03 14:00:07
【问题描述】:

我开始使用 OpenGL 和 GLUT。我想画一些点,但问题是它们变成了正方形,我希望它们是圆点(实心圆)。

这就是我的工作:

void onInitialization( ) 
{ 
    glEnable( GL_POINT_SMOOTH );
    glEnable( GL_BLEND );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
    glPointSize( 6.0 );
}    

void onDisplay()
{
    glClearColor( 1.0f, 1.0f, 1.0f, 1.0f );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glBegin( GL_POINTS );
        glColor3f( 0.95f, 0.207, 0.031f );
    for ( int i = 0; i < g_numPoints; ++i )
    {
        glVertex2f( g_points[i].X, g_points[i].Y );
    }
    glEnd();
    glFinish();
    glutSwapBuffers();
}

这是结果:

点显示在预期的位置,只是它们的形状是错误的。

【问题讨论】:

  • 你没有提到你的目标平台是什么。一些 OpenGL 功能(如 GL_POINT_SMOOTH)并未得到广泛支持。如果您打算让您的应用程序在消费级视频卡上广泛运行,请确保在承诺使用外来扩展之前进行测试。即使它看起来有效,也要检查性能。它可能会让您进入软件模式。
  • 没有,但我确实提到我刚开始玩 opengl,很像一种爱好 ;)
  • 如果你只是想让你的代码在你自己的计算机上运行,​​那么无论如何,在你的计算机上做任何事情。但理解 OpenGL 的一个主要缺陷是规范的大部分内容在任何给定平台上都无法正常运行也是很有价值的。这些究竟是哪些部分是一个非常缺乏记录的秘密。软件模式回退往往会掩盖不受支持的功能。
  • 你可以随时尝试 glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);

标签: c opengl glut


【解决方案1】:

与之前所说的不同,只要您支持 OpenGL 1.4 或 GL_ARB_point_sprite 扩展,固定功能管道即使使用 GL_POINTS 原始类型也是可能的。请参阅此文档或您选择的 OpenGL 核心规范:http://www.opengl.org/registry/specs/ARB/point_sprite.txt

GL_ARB_point_sprite 将点转换为“四边形”,即平面形式的多边形。它转换为的确切原始类型没有由规范定义,尽管它并不重要。重要的是GL_COORD_REPLACE 在启用时会自动为表面生成纹理坐标,因此您可以使用球形 RGBA 纹理对它们进行纹理映射。

编辑:您(发帖人)似乎是对的。 抗锯齿点相对于它们的半径是四舍五入的。 (我从 2003 年开始使用 OpenGL,我不知道这一点。[/shame]) 因此,当您拥有multisample-able 视觉/像素格式时启用GL_POINT_SMOOTH,您会得到四舍五入的分数。尽管如此,多重采样可能很慢,所以我会同时实现。带纹理的四边形很便宜。

要请求使用 XLib 进行多重采样的视觉对象,请使用列表中的这两个属性来 glXChooseFBConfig():

GLX_SAMPLE_BUFFERS - 它的值应该是True。这是一个开/关切换。
GLX_SAMPLES - 样本数。

要使用 Win32 请求像素格式,请使用列表中的这两个属性来选择 ChoosePixelFormat() 或 wglChoosePixelFormatARB():

WGL_SAMPLE_BUFFERS_ARB 同上,一个切换开关。
WGL_SAMPLES_ARB 同上,样本数。

您似乎可以在标志 GLUT_MULTISAMPLEglutInitDisplayMode 中进行 OR 以在 GLUT 中进行多重采样,但您不能请求采样缓冲区的数量。

这是使用您的测试用例实现 alpha 混合四边形的方法。

void onInitialization( ) 
{
    glEnable( GL_POINT_SPRITE ); // GL_POINT_SPRITE_ARB if you're
                                 // using the functionality as an extension.

    glEnable( GL_POINT_SMOOTH );
    glEnable( GL_BLEND );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
    glPointSize( 6.0 );

    /* assuming you have setup a 32-bit RGBA texture with a legal name */
    glActiveTexture(GL_TEXTURE0);
    glEnable( GL_TEXTURE_2D );
    glTexEnv(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
    glTexEnv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glBindTexture(GL_TEXTURE_2D, texture_name);
}    

void onDisplay()
{
    glClearColor( 1.0f, 1.0f, 1.0f, 1.0f );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glBegin( GL_POINTS );
        glColor4f( 0.95f, 0.207, 0.031f, 1.0f );
    for ( int i = 0; i < g_numPoints; ++i )
    {
        glVertex2f( g_points[i].X, g_points[i].Y );
    }
    glEnd();
    glFinish();
    glutSwapBuffers();
}

使用逐片段 Alpha 混合 + 纹理的圆点图像:
(来源:mechcore.net
使用GL_POINT_SMOOTH 和多重采样得到的圆点图像:
(来源:mechcore.net
我制作的一个小样本展示了这两种技术。需要 libSDL 和 libGLEW 才能编译:

#include <iostream>
#include <exception>
#include <memory>
#include <SDL/SDL.h> 
#include <cmath>
#include <GL/glew.h>
#include <GL/glu.h>

#define ENABLE_TEXTURE
#define ENABLE_MULTISAMPLE

int Width = 800;
int Height = 600;

void Draw(void);
void Init(void);

inline float maxf(float a, float b)
{
    if(a < b)
        return b;
    return a;
}

inline float minf(float a, float b)
{
    if(a > b)
        return b;
    return a;
}

GLuint texture_name;

int main(void)
{
    try {
        SDL_Init(SDL_INIT_VIDEO);
        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
        #ifdef ENABLE_MULTISAMPLE
            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
        #endif
        SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
        SDL_SetVideoMode(Width, Height, 32, SDL_OPENGL);

        glewInit();
        Init();

        SDL_Event event;
        bool running = true;

        while(running){
            while(SDL_PollEvent(&event)){
                switch(event.type)
                {
                    case SDL_KEYDOWN:
                        if(event.key.keysym.sym == SDLK_ESCAPE)
                            running = false;
                    break;
                    case SDL_QUIT:
                        running = false;
                    break;
                }
            }
            Draw();
            SDL_GL_SwapBuffers();
        }
        SDL_Quit();
    }
    catch(std::bad_alloc& e)
    {
        std::cout << "Out of memory. " << e.what() << std::endl;
        exit(-1);
    }
    catch(std::exception& e)
    {
        std::cout << "Runtime exception: " << e.what() << std::endl;
        exit(-1);
    }
    catch(...)
    {
        std::cout << "Runtime exception of unknown type." << std::endl;
        exit(-1);
    }
    return 0;
}

void Init(void)
{
    const GLint texWidth = 256;
    const GLint texHeight = 256;
    const float texHalfWidth = 128.0f;
    const float texHalfHeight = 128.0f;
    printf("INIT: \n");

    unsigned char* pData = new unsigned char[texWidth*texHeight*4];
    for(int y=0; y<texHeight; ++y){
        for(int x=0; x<texWidth; ++x){
            int offs = (x + y*texWidth) * 4;
            float xoffs = ((float)x - texHalfWidth) / texHalfWidth;
            float yoffs = ((float)y - texHalfWidth) / texHalfHeight;
            float alpha = 1.0f - std::sqrt(xoffs*xoffs + yoffs*yoffs);
            if(alpha < 0.0f)
                alpha = 0.0f;
            pData[offs + 0] = 255; //r
            pData[offs + 1] = 0; //g
            pData[offs + 2] = 0; //b
            pData[offs + 3] = 255.0f * alpha; // * 
            //printf("alpha: %f\n", pData[x + y*texWidth + 3]);
        }
    }

    #ifdef ENABLE_TEXTURE
    glGenTextures(1, &texture_name);
    glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture_name);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pData);
    glEnable(GL_POINT_SPRITE);
    glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    #endif

    glPointSize(32.0f);

    glMatrixMode(GL_PROJECTION);
    glOrtho(0, Width, 0, Height, -1.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDisable(GL_DEPTH_TEST);

    #ifdef ENABLE_MULTISAMPLE
        glEnable(GL_POINT_SMOOTH);
    #endif

    GLenum e;
    do{
        e = glGetError();
        printf("%s\n",gluErrorString(e));
    } while(e != GL_NO_ERROR);

    delete [] pData;
}

void Draw(void)
{
    const int gridWidth = 1024;
    const int gridHeight = 1024;
    float t1, t2;

    t1 = t2 = (float)SDL_GetTicks() * 0.001f;
    t1 = fmod(t1, 10.0f) / 10.0f;
    t2 = fmod(t2, 4.0f) / 4.0f;
    float scale = 0.5f + (-sin(t2 * 2.0 * M_PI) + 1.0f) * 1.2f;
    //glColor4f(0.4f, 0.5f, 0.9f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();

    glTranslatef((Width>>1), (Height>>1), 0.0f);
    glScalef(scale,scale,scale);
    glRotatef(t1 * 360.0f, 0.0f, 0.0f, 1.0f);

    glBegin(GL_POINTS);
    for(int j=0; j<gridHeight; j+=64){
        for(int i=0; i<gridWidth; i+=64){ 
            glVertex2i(i-(gridWidth>>1),j-(gridHeight>>1));
        }
    }
    glEnd();
}

【讨论】:

  • 那么我在另一条评论中引用的参考文献呢?如果我没有误解,它表明 GL_POINT_SMOOTH 确实会产生圆形点。还是我?
  • 是的,我读过并尝试过。有趣,我不知道 :-)
  • 感谢您非常详细的回答。
  • 不客气。随时在 FreeNode IRC 网络上的 ##opengl 和 ##opengl3 上认识更多人。
  • 这可以在没有多重采样或没有点精灵的情况下使用固定管道 OpenGL。如果 OpenGL 实现确实符合规范。一些驱动程序不符合规范,并将点绘制为正方形。
【解决方案2】:

如果您选择固定功能管道,Mads 的回答提供了您需要的一切。但是,如果您的系统不提供ARB_point_sprite 扩展或实现损坏(某些 ATI 驱动程序),您也可以使用几何着色器解决这部分问题。 ARB_geometry_shader4 扩展允许您将一个点图元转换为两个三角形,这可以用作ARB_point_sprite 扩展创建的四边形。在 OpenGL 3.2 上,核心已经支持几何着色器,无需扩展。 OpenGL wiki 有two examples

【讨论】:

  • +1 用于提及几何着色器,如果支持的话,无论如何你都应该使用它(早在 2009 年,今天也是如此)。原因是并非所有 GPU 都支持保护带,您无法知道它们是否支持。如果不这样做,点和点精灵将在靠近剪辑矩形时弹出和弹出。当点精灵的中心点超出矩形时,点精灵的可见一半突然“消失”。另外,磅值通常限制在 64 像素左右。
【解决方案3】:

无法使用固定的 opengl 函数。点总是方形的:)

您必须绘制自己的圆(通过像蛋糕一样逐块构建)或绘制带有“圆”纹理的 GL_QUAD。

最好的问候, 安德烈

【讨论】:

  • 根据 opengl 参考:“如果启用抗锯齿,则点光栅化会为每个像素正方形生成一个片段,该片段与位于直径等于当前点大小的圆内的区域相交并以点的 x w y w 。每个片段的覆盖值是圆形区域与对应像素正方形相交的窗口坐标区域。"
  • 实际上是有可能的,但它的工作好坏(甚至是否)取决于 OpenGL 驱动程序。在我的测试中,它使用 nVidia 硬件/驱动程序给出圆点,但对于 ATI/AMD 硬件/驱动程序,它给出平方点。
猜你喜欢
  • 1970-01-01
  • 2018-10-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2018-10-16
  • 2012-05-29
  • 2016-09-11
  • 2019-06-21
相关资源
最近更新 更多