Попытка рисовать фигуры в координатах экранного пространства с использованием ортографической проекции с помощью WebGL (regl) - PullRequest
0 голосов
/ 25 августа 2018

Используя regl, я пытаюсь реализовать очень простую drawRect() функцию, которая в конечном итоге сможет передавать x, y, width, height в screen space (с началом координат 0,0 сверху левый угол экрана)

Вот что у меня есть:

https://codesandbox.io/s/nn9qvxom4l

const regl = require('regl')();
const mat4 = require('gl-mat4');

var drawRect = regl({
    frag: `
      precision mediump float;
      uniform vec4 color;
      void main() {
        gl_FragColor = color;
      }`,

    vert: `
      precision mediump float;
      attribute vec2 position;
      uniform vec2 offset;
      uniform vec2 scale;
      uniform float viewportWidth;
      uniform float viewportHeight;
      uniform mat4 projection;
      void main() {
        // windows ratio scaling factor.
        float r = (viewportWidth) / (viewportHeight);
        gl_Position = projection * vec4(position.xy * scale, 0, 1);
      }`,

    attributes: {
        position: [
            [-1, -1], //tri 1 bottom left
            [1, 1],
            [-1, 1],
            [-1, -1],
            [1, -1],
            [1, 1]
        ]
    },

    uniforms: {
        offset: regl.prop('offset'),
        scale: regl.prop('scale'),
        color: regl.prop('color'),
        viewportWidth: regl.context('viewportWidth'),
        viewportHeight: regl.context('viewportHeight'),
        projection: ({ viewportWidth, viewportHeight }) => {
            //glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);
            //ortho(out:mat4, left, right, bottom, top, near, far)
            // this makes everything blank
            var m = mat4.ortho(
                [],
                0,
                viewportWidth,
                0,
                viewportHeight,
                0.1,
                100.0
            );
            console.log(m);
            return m;

        }
    },

    depth: {
        enable: false
    },

    cull: {
        enable: true
    },

    count: 6,

    viewport: {
        x: 0,
        y: 0,
        width: window.innerWidth,
        height: window.innerHeight
    }
});

regl.clear({
    color: [0, 0, 0, 255],
    depth: 1
});

// all these coordinates are in clip-space* right now (* I think)
// but I'd like to be able to pass in screen-space coordinates
drawRect([
    {
        offset: [0, 0],
        scale: [0.002, 0.002],
        color: [1, 0.2, 0.2, 1]
    },
    {
        offset: [0, -0.2],
        scale: [0.2, 0.05],
        color: [1, 0.2, 1, 1]
    },
    {
        offset: [0, 0],
        scale: [0.5, 0.5],
        color: [0.2, 0.2, 1, 1]
    }
]);

В настоящее время он рисует пустой экран (наверное, из-за отсечения матрицы проекции или перемещения всего за пределы области просмотра). Если вы удаляете проекцию из вершинного шейдера или используете идентификационную матрицу в атрибуте проекции, тогда он снова визуализирует синий квадрат.

Итак, мои вопросы:

  1. Что я здесь не так делаю? Почему моя орфографическая проекция не работает?
  2. Как мне отладить это в будущем, чтобы я мог "видеть" результирующие координаты и как они ошибочны?

Я прочитал некоторые вопросы SO, книги и блоги OpenGL, но я застрял здесь, потому что, похоже, это должно сработать.

Связанные ресурсы:

https://unspecified.wordpress.com/2012/06/21/calculating-the-gluperspective-matrix-and-other-opengl-matrix-maths/ https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_model_view_projection https://webglfundamentals.org/webgl/lessons/webgl-2d-matrices.html WebGL Изометрическая проекция Хотите пример OpenGL 2D (VC ++, нарисуйте прямоугольник) http://songho.ca/opengl/gl_transform.html

Редактировать: фиксированный viewportWidth, используемый дважды в ортопроекции. Это исправляет пустой экран, однако начало координат теперь слева внизу, а не вверху слева. Переключение параметров орто-проекции снова вызывает пустой экран.

1 Ответ

0 голосов
/ 25 августа 2018

Матрица проекции описывает отображение от трехмерных точек сцены к двухмерным точкам области просмотра. Матрица проекции преобразуется из пространства просмотра в пространство клипа. Координаты в пространстве клипа преобразуются в нормализованные координаты устройства (NDC) в диапазоне (-1, -1, -1) в (1, 1, 1) путем деления на компоненту w координат клипа.

В ортогональной проекции координаты в пространстве вида линейно отображаются на координаты пространства клипа, а координаты пространства клипа равны нормализованным координатам устройства, поскольку компонент w равен 1 (для декартовой входной координаты).

orthographic projection

Значения слева, справа, снизу, сверху, близко и далеко определяют поле. Вся геометрия, которая находится внутри объема окна, «видна» в окне просмотра.

Наконец, нормализованные координаты устройства линейно отображаются в окне просмотра.

Когда вы устанавливаете ортографическую проекцию следующим образом:

var m = mat4.ortho([], 0, viewportWidth, 0, viewportHeight, 0.1, 100.0);

, тогда прямоугольник с нижним левым (0, 0) и верхним правым (viewportWidth, viewportHeight) отображается в NDC из (- 1, -1 ) до (1, 1) .

Когда вы устанавливаете видовой экран следующим образом:

viewport: {x: 0, y: 0, width: window.innerWidth, height: window.innerHeight}

, тогда NDC отображаются в прямоугольник области просмотра с левым нижним (0, 0) и верхним правым (viewportWidth, viewportHeight) .

Это означает, что координаты вида в (0, 0, window.innerWidth, window.innerHeight) отображаются в прямоугольник области просмотра (0, 0, window.innerWidth, window. innerHeight) . Итак, вы рисуете в «оконных» (пиксельных) координатах.

Если у вас есть такая геометрия:

position: [[-1, -1], [1, 1], [-1, 1], [-1, -1], [1, -1], [1, 1] ]

{ offset: [0, 0], scale: [0.5, 0.5], color: [0.2, 0.2, 1, 1] }

тогда размер этой геометрии составляет 1 * 1 пиксель в области просмотра - прямоугольник от (- 1, -1) до (1, 1) , с шкала (0,5, 0,5) .

Увеличьте масштаб (например, scale: [100, 100]), чтобы решить вашу проблему.


Кроме того, геометрия должна находиться между ближней и дальней плоскостями, чтобы быть внутри объема обзора.

В вашем случае координата z геометрии равна 0, как указано в вершинном шейдере:

gl_Position = projection * vec4(position.xy * scale, 0, 1);

Но ближняя плоскость равна 0,1, а дальняя - 100,0, как указано в ортографической проекции:

var m = mat4.ortho([], 0, viewportWidth, 0, viewportHeight, 0.1, 100.0);

Это приводит к тому, что геометрия ограничена ближней плоскостью, поскольку координата z меньше, чем ближняя плоскость.

Поменяйте ближний самолет, чтобы решить проблему:

var m = mat4.ortho([], 0, viewportWidth, 0, viewportHeight, -100.0, 100.0);

Матрица ортографической проекции выглядит следующим образом:

r = right, l = left, b = bottom, t = top, n = near, f = far 

2/(r-l)         0               0               0
0               2/(t-b)         0               0
0               0               -2/(f-n)        0
-(r+l)/(r-l)    -(t+b)/(t-b)    -(f+n)/(f-n)    1

В соответствии с комментарием:

для mat4.ortho, левый верхний источник должен быть mat4.ortho([], 0, viewportWidth, viewportHeight, 0, -1, 1), но с такой проекцией на экране снова ничего не появляется.

Если top равно 0, а bottom - положительное значение, то из-за 2/(top-bottom) ось y становится отрицательной.

Чтобы решить, вы можете инвертировать ось Y вручную:

var m = mat4.ortho([], 0, viewportWidth, -viewportHeight, 0, -1, 1);

В этом случае вам нужно инвертировать смещения, потому что в верхнем правом углу это (0, 0) , а в левом нижнем углу - (viewportWidth, -viewportHeight) .

, например

offset: [250, -250]

Если бы вы использовали

var m = mat4.ortho([], 0, viewportWidth, viewportHeight, 0, -1, 1);

тогда вам нужно инвертировать шкалы y.

, например

scale: [100, -100]
...