Подтвердить что ты не робот

Улучшенный 2D-чертеж Opengl

Я работаю над движком 2d. Он уже работает неплохо, но я продолжаю получать пиксельные ошибки.

Например, мое окно имеет размер 960x540 пикселей, я рисую линию от (0, 0) до (959, 0). Я ожидал бы, что каждый пиксель на линии сканирования 0 будет установлен в цвет, но нет: правый пиксель не нарисован. Та же проблема, когда я рисую вертикально на пиксель 539. Мне действительно нужно рисовать (960, 0) или (0, 540), чтобы нарисовать его.

Как я родился в эпоху пикселей, я убежден, что это не правильный результат. Когда мой экран был размером 320x200 пикселей, я мог рисовать от 0 до 319 и от 0 до 199, и мой экран был бы заполнен. Теперь я заканчиваю тем, что экран с правым/нижним пикселем не нарисован.

Это может быть связано с разными вещами: где я ожидаю, что примитив линии opengl будет отрисован от пикселя до пикселя включительно, что последний пиксель просто является исключительным? Это оно? моя проекционная матрица неверна? Я под ложным предположением, что, когда у меня есть 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;
    }
4b9b3361

Ответ 1

В 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, item "14.090 Как получить точную пикселизацию строк?". В нем говорится: "Спецификация OpenGL позволяет использовать широкий спектр оборудования для рендеринга линий, поэтому точная пикселизация может быть невозможна вообще".

Многие утверждают, что вы не должны использовать строки в OpenGL вообще. Их поведение основано на том, как работает старое SGI-оборудование, а не на том, что имеет смысл. (И линии с шириной > 1 почти невозможно использовать так, чтобы это выглядело хорошо!)

Ответ 2

Обратите внимание, что в пространстве координат OpenGL нет понятия целых чисел, все это float, а "центр" пикселя OpenGL действительно находится на 0,5,0,5 вместо его верхнего левого угла. Поэтому, если вам нужна линия шириной 1px от 0,0 до 10,10 включительно, вам действительно нужно провести линию от 0,5,0,5 до 10,5,10,5.

Это будет особенно заметно, если вы включите сглаживание, если у вас есть сглаживание, и вы пытаетесь сделать от 50,0 до 50 100, вы можете увидеть размытую 2px широкую линию, потому что линия упала между двумя пикселями.