Получение гладких, больших очков в OpenGL - PullRequest
27 голосов
/ 03 октября 2009

Я начал играть с 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();
}

Это результат: Result of the above code

Точки отображаются там, где и ожидалось, только их форма неправильна.

Ответы [ 3 ]

39 голосов
/ 03 октября 2009

В отличие от того, что было сказано ранее, это возможно с конвейером с фиксированными функциями, даже с примитивным типом GL_POINTS, если у вас есть поддержка OpenGL 1.4 или расширения GL_ARB_point_sprite. Обратитесь к этому документу или к базовой спецификации OpenGL по вашему выбору: http://www.opengl.org/registry/specs/ARB/point_sprite.txt

GL_ARB_point_sprite преобразует точки в "квадраты", то есть многоугольник в форме плоскости. Точный тип примитива, в который он преобразуется, не определяется спецификацией, хотя это и не важно. Важно то, что GL_COORD_REPLACE автоматически генерирует текстурные координаты для поверхности, когда она включена, поэтому вы можете наносить на карту их текстуру с помощью сферической RGBA-текстуры.

РЕДАКТИРОВАТЬ: кажется, что вы (постер) прав. Сглаженные точки округляются относительно их радиуса. (Я использую OpenGL с 2003 года, и я этого не знал. [/ Shame]) Поэтому, включив GL_POINT_SMOOTH, пока у вас есть multisample-able визуальный / пиксельный формат, вы получите округленные точки. Тем не менее, мультисэмплинг может быть медленным, поэтому я бы реализовал оба. Текстурированные четырехугольники дешевы.

Чтобы запросить визуал с мультисэмплингом с помощью XLib , используйте эти два атрибута в списке для glXChooseFBConfig ():

GLX_SAMPLE_BUFFERS - его значение должно быть True. Это тумблер включения / выключения.
GLX_SAMPLES - количество образцов.

Чтобы запросить формат пикселя с Win32 , используйте эти два атрибута в списке для ChoosePixelFormat () или wglChoosePixelFormatARB ():

WGL_SAMPLE_BUFFERS_ARB То же, что и выше, переключатель.
WGL_SAMPLES_ARB То же, что и выше, количество образцов.

Кажется, что вы можете ИЛИ во флаге GLUT_MULTISAMPLE до glutInitDisplayMode, чтобы получить мультисэмплинг в GLUT , но вы не можете запросить количество буферов семплов.

Вот как альфа-смешанные квады могут быть реализованы с помощью вашего тестового примера.

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();
}

Изображение округленных точек с использованием альфа-смешения для каждого фрагмента + текстуры: image
(source: mechcore.net )
Изображение округленных точек с использованием GL_POINT_SMOOTH и мультисэмплирования: image
(source: 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();
}
2 голосов
/ 03 октября 2009

Ответ Мадса дает все, что вам нужно, если вы идете по конвейеру с фиксированной функцией. Однако, если у вас есть система, которая не обеспечивает расширение ARB_point_sprite или не работает с ней (некоторые драйверы ATI), вы можете решить эту часть также с помощью геометрических шейдеров. ARB_geometry_shader4 Расширение позволяет преобразовать точечный примитив в два треугольника, которые можно использовать как четырехугольник, созданный расширением ARB_point_sprite. В OpenGL 3.2 геометрические шейдеры уже поддерживаются в ядре, расширение не требуется. В вики OpenGL есть два примера .

0 голосов
/ 03 октября 2009

Невозможно с фиксированной функцией opengl. Точки всегда квадратные:)

Вы должны нарисовать свой собственный круг (создавая его как торт, кусок за куском) или нарисовать GL_QUAD с текстурой "круг".

С наилучшими пожеланиями, Андрэ

...