Opengl pixel perfect 2D рисунок
Я работаю над 2d-движком. Это уже работает довольно хорошо, но я продолжаю получать пиксельные ошибки.
Например, мое окно имеет размер 960x540 пикселей, я провожу линию от (0, 0) до (959, 0). Я ожидал бы, что каждый пиксель на scan-line 0 будет установлен в цвет, но нет: самый правый пиксель не рисуется. Та же проблема, когда я рисую вертикально до пикселя 539. Мне действительно нужно нарисовать (960, 0) или (0, 540), чтобы он был нарисован.
Поскольку я родился в пиксель-эру, я убежден, что это не так. правильный результат. Когда мой экран был размером 320х200 пикселей, я мог рисовать от 0 до 319 и от 0 до 199, и мой экран был бы полон. Теперь я получаю экран с правым / нижним пикселем, который не нарисован.
Это может быть связано с разными вещами:
там, где я ожидаю, что примитив OpenGL line рисуется ОТ ПИКСЕЛЯ ДО пикселя включительно, этот последний пиксель на самом деле является эксклюзивным? И это все?
моя проекционная матрица неверна?
Я нахожусь под ложным предположением, что когда у меня есть backbuffer 960x540, то есть на самом деле есть еще один пиксель?
Что-то еще?
Может кто-то пожалуйста, помогите мне? Я уже давно изучаю эту проблему, и каждый раз, когда я думал, что все в порядке, через некоторое время я видел, что на самом деле это не так.
Вот некоторые из моих кодов, я постарался разобрать их как можно больше. Когда я вызываю свою линейную функцию, каждая координата добавляется с 0.375, 0.375, чтобы сделать ее правильной как на адаптерах ATI, так и на адаптерах 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;
}
2 ответов:
В OpenGL строки растеризуются с помощью правила "Алмазный выход". Это почти то же самое, что сказать, что конечная координата является исключительной, но не совсем...
Вот что говорит спецификация OpenGL: http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html
Также взгляните на FAQ OpenGL, http://www.opengl.org/archives/resources/faq/technical/rasterization.htm , пункт " 14.090 как получить точную пикселизацию линий?". В нем говорится: "спецификация OpenGL позволяет использовать широкий спектр оборудования для рендеринга линий, поэтому точная пикселизация может быть вообще невозможна."
Многие будут утверждать, что вы вообще не должны использовать строки в OpenGL. Их поведение основано на том, как работало древнее оборудование SGI, а не на том, что имеет смысл. (А линии с шириной >1 практически невозможно использовать так, чтобы это выглядело хорошо!)
Обратите внимание, что координатное пространство OpenGL не имеет понятия целых чисел,все является плавающей точкой, и "центр" пикселя OpenGL действительно находится в 0.5, 0.5 вместо его левого верхнего угла. Поэтому, если вы хотите получить линию шириной 1px от 0,0 до 10,10 включительно, вам действительно нужно было нарисовать линию от 0,5, 0,5 до 10,5, 10,5.
Это будет особенно заметно, если вы включите сглаживание, если у вас есть сглаживание и вы пытаетесь нарисовать от 50,0 до 50,100, вы можете увидеть размытую 2px широкую линию, потому что линия упала между двумя пикселями.
Comments