2012-04-06 16 views
27

Estoy trabajando en un motor 2d. Ya funciona bastante bien, pero sigo recibiendo errores de píxeles.Opengl pixel perfect 2D drawing

Por ejemplo, mi ventana es de 960x540 píxeles, trazo una línea de (0, 0) a (959, 0). Esperaría que cada píxel en la línea de escaneo 0 se establezca en un color, pero no: el píxel que está más a la derecha no se dibuja. El mismo problema cuando dibujo verticalmente al píxel 539. Realmente necesito dibujar a (960, 0) o (0, 540) para dibujarlo.

Como nací en la era de los píxeles, estoy convencido de que este no es el resultado correcto. Cuando mi pantalla tenía 320x200 píxeles, podía dibujar de 0 a 319 y de 0 a 199, y mi pantalla estaría llena. Ahora termino con una pantalla con un pixel derecho/inferior no dibujado.

Esto puede deberse a cosas diferentes: donde espero que la primitiva de línea OpenGL se extraiga de un píxel a un píxel incluido, ¿ese último píxel es realmente exclusivo? ¿Es asi? mi matriz de proyección es incorrecta? Estoy bajo la falsa suposición de que cuando tengo un backbuffer de 960x540, ¿eso realmente tiene un píxel más? ¿Algo más?

¿Alguien puede ayudarme? He estado investigando este problema desde hace mucho tiempo, y cada vez que pensaba que estaba bien, después de un tiempo vi que en realidad no era así.

Aquí está algo de mi código, traté de quitarlo todo lo que pude. Cuando llamo a mi función de línea, cada coordenada se agrega con 0.375, 0.375 para corregirla en los adaptadores ATI y NVIDIA.

int width = resX(); 
int height = resY(); 

for (int i = 0; i < height; i += 2) 
    rm->line(0, i, width - 1, i, vec4f(1, 0, 0, 1)); 
for (int i = 1; i < height; i += 2) 
    rm->line(0, i, width - 1, i, vec4f(0, 1, 0, 1)); 

// when I do this, one pixel to the right remains undrawn 

void rendermachine::line(int x1, int y1, int x2, int y2, const vec4f &color) 
{ 
    ... some code to decide what std::vector the coordinates should be pushed into 
    // m_z is a z-coordinate, I use z-buffering to preserve correct drawing orders 
    // vec2f(0, 0) is a texture-coordinate, the line is drawn without texturing 
    target->push_back(vertex(vec3f((float)x1 + 0.375f, (float)y1 + 0.375f, m_z), color, vec2f(0, 0))); 
    target->push_back(vertex(vec3f((float)x2 + 0.375f, (float)y2 + 0.375f, m_z), color, vec2f(0, 0))); 
} 

void rendermachine::update(...) 
{ 
    ... render target object is queried for width and height, in my test it is just the back buffer so the window client resolution is returned 
    mat4f mP; 
    mP.setOrthographic(0, (float)width, (float)height, 0, 0, 8000000); 

    ... all vertices are copied to video memory 

    ... drawing 
    if (there are lines to draw) 
     glDrawArrays(GL_LINES, (int)offset, (int)lines.size()); 

    ... 
} 

// And the (very simple) shader to draw these lines 

// Vertex shader 
    #version 120 
    attribute vec3 aVertexPosition; 
    attribute vec4 aVertexColor; 
    uniform mat4 mP; 
    varying vec4 vColor; 
    void main(void) { 
     gl_Position = mP * vec4(aVertexPosition, 1.0); 
     vColor = aVertexColor; 
    } 

// Fragment shader 
    #version 120 
    #ifdef GL_ES 
    precision highp float; 
    #endif 
    varying vec4 vColor; 
    void main(void) { 
     gl_FragColor = vColor.rgb; 
    } 
+0

Estoy empezando a pensar que es el punto "exclusivo" del que estoy hablando. Si usa QT o C++ Builder/Delphi o incluso API de Windows para dibujar líneas, los píxeles se dibujan excluyendo el punto final. Entonces, ¿es posible que lo mismo se haga en OpenGL, y probablemente también al dibujar triángulos? Cuando dibuja un rectángulo con QT, constructor, ..., también obtiene la esquina inferior derecha excluida ... – scippie

+0

La siguiente prueba muestra que estoy de acuerdo con lo que dije antes: cuando uso GL_POINT para llenar los píxeles en todas las esquinas de la ventana, estas coordenadas son las que espero sean: (0,0), (ancho-1, altura) -1). Esto me muestra que mi forma de dibujar es correcta y que la línea (y posiblemente el triángulo) simplemente no incluirá el punto final. ¿Puede alguien estar de acuerdo? – scippie

+0

relacionado http://stackoverflow.com/questions/5467218/opengl-2d-hud-over-3d –

Respuesta

15

En OpenGL, las líneas se rasterizan usando la regla "Diamond Exit". Esto es casi lo mismo que decir que la coordenada final es exclusiva, pero no del todo ...

Esto es lo que la especificación OpenGL tiene que decir: http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html

también echar un vistazo a la AYUDA OpenGL , http://www.opengl.org/archives/resources/faq/technical/rasterization.htm, elemento "14.090 ¿Cómo obtengo una pixelización exacta de líneas?". Dice: "La especificación OpenGL permite una amplia gama de hardware de renderizado de línea, por lo que la pixelización exacta puede no ser posible en absoluto".

Muchos argumentarán que no debe utilizar líneas en OpenGL en absoluto. Su comportamiento se basa en cómo funcionó el antiguo hardware de SGI, no en lo que tiene sentido. (¡Y las líneas con anchos> 1 son casi imposibles de usar de una manera que se ve bien!)

+0

¡Gracias, esta respuesta es muy útil! Las líneas son para fines de prueba, no para la frescura del producto final. Así que estoy muy contento con GL_LINES en este punto. ¿Puedo suponer que los triángulos tienen el mismo resultado? Quiero decir, si dibujo un rectángulo relleno dibujando dos triángulos, ¿puedo esperar el mismo resultado? – scippie

+3

Lo sentimos: 14.120: Las primitivas y las primitivas de línea rellenas siguen reglas diferentes para la rasterización. – scippie

+0

> "¡Y las líneas con anchos> 1 son casi imposibles de usar de una manera que se ve bien!" Se pueden dibujar como rectángulos finos y luego hacer magia en el sombreador de fragmentos para que se vean como líneas con casi todas las propiedades visuales. Pero esto puede ser costoso. –

6

Tenga en cuenta que OpenGL espacio de coordenadas no tiene noción de los números enteros, todo es un flotador y el "centro" de un píxel OpenGL está realmente en el 0.5,0.5 en lugar de su esquina superior izquierda. Por lo tanto, si desea una línea de 1px de 0,0 a 10,10 inclusive, realmente debe trazar una línea de 0.5,0.5 a 10.5,10.5.

Esto será especialmente evidente si activa el suavizado, si tiene anti-aliasing e intenta dibujar de 50,0 a 50,100, puede ver una línea borrosa de 2px de ancho, ya que la línea cayó entre dos pixeles

+1

Entiendo completamente de lo que estás hablando pero eso no es un problema aquí. Al usar la matriz de proyección correcta y agregar 0.375 a cada píxel, tanto ATI como nvidia redondearán los números para que las coordenadas sean píxeles exactos. ¿De qué otro modo, por ejemplo, Mac os dibujaría ventanas perfectas de píxeles si no fueran perfectas para píxeles? – scippie

+0

@scippie: en realidad debería agregar 0.5. 0.5 es el medio del pixel. 0.375 no es. – SigTerm

+4

@SigTerm: esto no es cierto, debe probarlo tanto en hardware ATI como en NVidia y la pesadilla comienza. ATI y NVidia tienen una ronda diferente. Al usar 0.375, el redondeo es el mismo en ambos y los resultados son perfectos (aunque puede no ser correcto en teoría) – scippie

Cuestiones relacionadas