c++ - OpenGL Scale Single Pixel Line
sdl (2)
Me gustaría hacer un juego que sea internamente 320x240, pero que se muestre en la pantalla con múltiplos enteros de este (640x480, 960,720, etc.). Voy por gráficos retro de píxeles 2D.
He logrado esto configurando la resolución interna a través de glOrtho ():
glOrtho(0, 320, 240, 0, 0, 1);
Y luego escalo la resolución de salida en un factor de 3, así:
glViewport(0,0,960,720);
window = SDL_CreateWindow("Title", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 960, 720, SDL_WINDOW_OPENGL);
Dibujo rectángulos como este:
glBegin(GL_LINE_LOOP);
glVertex2f(rect_x, rect_y);
glVertex2f(rect_x + rect_w, rect_y);
glVertex2f(rect_x + dst_w, dst_y + dst_h);
glVertex2f(rect_x, rect_y + rect_h);
glEnd();
Funciona perfectamente a 320x240 (sin escala):
Cuando escalo hasta 960x720, ¡la representación de píxeles funciona bien! Sin embargo, parece que GL_Line_Loop no se dibuja en un lienzo de 320x240 y se amplía, sino que se dibuja en el lienzo final de 960x720. El resultado son líneas de 1 px en un mundo de 3 px :(
¿Cómo dibujo líneas al lienzo glOrtho 320x240, en lugar del lienzo de salida 960x720?
Como mencioné en mi comentario, los
controladores Intel OpenGL
tienen problemas con el renderizado directo a la textura y no conozco ninguna solución alternativa que esté funcionando.
En tal caso, la única forma de evitar esto es usar
glReadPixels
para copiar el contenido de la pantalla en
la
memoria de la
CPU
y luego volver a copiarlo en la
GPU
como textura.
De grueso que es mucho más lento que el renderizado directo a textura.
Así que aquí está el trato:
-
establecer vista de baja resolución
no cambie la resolución de su ventana solo los valores de
glViewport
. Luego renderice su escena en baja resolución (en solo una fracción del espacio de la pantalla) -
copia la pantalla renderizada en textura
- establecer vista de resolución objetivo
-
renderizar la textura
no olvides usar el filtro
GL_NEAREST
. ¡Lo más importante es que intercambie los buffers solo después de esto, no antes! De lo contrario, habría parpadeo.
Aquí la fuente de C ++ para esto:
void gl_draw()
{
// render resolution and multiplier
const int xs=320,ys=200,m=2;
// [low res render pass]
glViewport(0,0,xs,ys);
glClearColor(0.0,0.0,0.0,1.0);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
// 50 random lines
RandSeed=0x12345678;
glColor3f(1.0,1.0,1.0);
glBegin(GL_LINES);
for (int i=0;i<100;i++)
glVertex2f(2.0*Random()-1.0,2.0*Random()-1.0);
glEnd();
// [multiply resiolution render pass]
static bool _init=true;
GLuint txrid=0; // texture id
BYTE map[xs*ys*3]; // RGB
// init texture
if (_init) // you should also delte the texture on exit of app ...
{
// create texture
_init=false;
glGenTextures(1,&txrid);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txrid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST); // must be nearest !!!
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_COPY);
glDisable(GL_TEXTURE_2D);
}
// copy low res screen to CPU memory
glReadPixels(0,0,xs,ys,GL_RGB,GL_UNSIGNED_BYTE,map);
// and then to GPU texture
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txrid);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, xs, ys, 0, GL_RGB, GL_UNSIGNED_BYTE, map);
// set multiplied resolution view
glViewport(0,0,m*xs,m*ys);
glClear(GL_COLOR_BUFFER_BIT);
// render low res screen as texture
glBegin(GL_QUADS);
glTexCoord2f(0.0,0.0); glVertex2f(-1.0,-1.0);
glTexCoord2f(0.0,1.0); glVertex2f(-1.0,+1.0);
glTexCoord2f(1.0,1.0); glVertex2f(+1.0,+1.0);
glTexCoord2f(1.0,0.0); glVertex2f(+1.0,-1.0);
glEnd();
glDisable(GL_TEXTURE_2D);
glFlush();
SwapBuffers(hdc); // swap buffers only here !!!
}
Y vista previa:
Probé esto en algunos gráficos Intel HD (Dios sabe qué versión) tengo a mi disposición y funciona (mientras que los enfoques estándar de renderizado a textura no lo son).
No hay un "lienzo glOrtho de 320x240". Solo existe la resolución real de la ventana: 960x720.
Todo lo que está haciendo es ampliar las coordenadas de las primitivas que renderiza. Entonces, su código dice que renderice una línea desde, por ejemplo, (20, 20) a (40, 40). Y OpenGL (eventualmente) escala esas coordenadas en 3 en cada dimensión: (60, 60) y (120x120).
Pero eso solo se trata de los puntos finales . Lo que sucede en el medio todavía se basa en el hecho de que está renderizando con la resolución real de la ventana.
Incluso si empleó
glLineWidth
para cambiar el ancho de sus líneas, eso solo solucionaría los anchos de línea.
No solucionaría el hecho de que la rasterización de líneas se basa en la resolución real a la que está renderizando.
Por lo tanto, las líneas diagonales no tendrán la apariencia pixelada que probablemente desee.
La única forma de hacerlo correctamente es, bueno, hacerlo correctamente. Renderice a una imagen que sea real 320x240, luego dibuje a la resolución real de la ventana.
Tendrás que crear una textura de ese tamaño, luego adjuntarla a un objeto framebuffer . Enlace el FBO para renderizar y renderizar a él (con la ventana gráfica configurada al tamaño de la imagen). Luego, desenlaza el FBO y dibuja esa textura en la ventana (con la ventana gráfica configurada con la resolución de la ventana).