Obtendo grandes pontos suaves no OpenGL
Pergunta
Comecei a brincar com OpenGL e GLUT.Eu gostaria de desenhar alguns pontos, mas o problema é que eles são quadrados e eu gostaria que fossem pontos redondos (círculos preenchidos).
Isto é o que eu faço:
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();
}
Este é o resultado:
Os pontos aparecem onde esperado, apenas a sua forma está errada.
Solução
Ao contrário do que foi dito anteriormente, isso é possível com o pipeline de função fixa, mesmo com o GL_POINTS
Tipo primitivo, desde que você tenha suporte para o OpenGL 1.4 ou o GL_ARB_point_sprite
extensão. Consulte este documento ou a especificação do OpenGL Core de sua escolha: http://www.opengl.org/registry/specs/arb/point_sprite.txt
GL_ARB_point_sprite
Converte pontos em "Quads", ou seja, um polígono com a forma de um avião. O tipo primitivo exato para o qual é convertido não é definido pela especificação, embora não seja importante. O que é importante é que GL_COORD_REPLACE
As coordenadas de textura de gerações automáticas para a superfície quando ativadas, para que você possa mapear a textura com uma textura RGBA em forma de esfera.
EDIT: Parece que você (o pôster) está certo. Pontos anti-alias são arredondados em relação ao raio. (Eu uso o OpenGL desde 2003 e não sabia disso. [/Vergonha]) Então, permitindo GL_POINT_SMOOTH
Enquanto você tem um multisample-able
Visual/Pixelformat, você recebe pontos arredondados. Ainda assim, a múltipla amortecimento pode ser lenta, então eu implementaria os dois. Quads texturizados são baratos.
Para solicitar um visual com multisampling com XLIB, use esses dois atributos na lista para glxChoosefbconfig ():
GLX_SAMPLE_BUFFERS
- seu valor deve ser True
. Esta é uma alternância ligada/desliga.
GLX_SAMPLES
- O número de amostras.
Para solicitar um pixelformat com Win32, use esses dois atributos na lista para escolherpixelformat () ou wglChoosepixelformatarb ():
WGL_SAMPLE_BUFFERS_ARB
O mesmo que acima, uma alternância.
WGL_SAMPLES_ARB
O mesmo que acima, o número de amostras.
Parece que você pode ou na bandeira GLUT_MULTISAMPLE
para glutInitDisplayMode
Para obter multisampling no GLUT, mas você não pode solicitar o número de buffers de amostra.
Aqui está como os quadríceps misturados alfa podem ser implementados usando seu caso de teste.
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();
}
Imagem de pontos arredondados usando a mistura alfa por fragmentação + texturas:
(fonte: Mechcore.net)
Imagem de pontos arredondados usando GL_POINT_SMOOTH
e multissampling:
(fonte: Mechcore.net)
Uma pequena amostra que fiz, que mostra as duas técnicas. Requer libsdl e libglew para compilar:
#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();
}
Outras dicas
A resposta de Mads fornece tudo que você precisa se optar pelo pipeline de função fixa.No entanto, se você tiver um sistema que não fornece o ARB_point_sprite
extensão ou com uma implementação quebrada (alguns drivers ATI), você pode resolver esta parte também com shaders de geometria.O ARB_geometry_shader4
extensão permite converter um ponto primitivo em dois triângulos, que podem ser usados como o quadrante criado pelo ARB_point_sprite
extensão.No OpenGL 3.2, shaders de geometria já são suportados no núcleo, sem necessidade de extensão.O wiki OpenGL tem dois exemplos.
Não é possível com uma função fixa do OpenGL. Os pontos são sempre quadrados :)
Você precisa desenhar seu próprio círculo (construindo -o como um bolo, peça por peça) ou desenhe um Gl_quad com uma textura "círculo".
Atenciosamente, Andre