3

使用 regl 我正在尝试实现一个非常简单的drawRect()函数,该函数最终将能够在屏幕空间中传递 x、y、宽度、高度(屏幕左上角的原点 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 在正交投影中使用了两次。这修复了一个空白屏幕,但是原点现在位于左下角而不是左上角。切换正射投影的参数会再次导致黑屏。

4

1 回答 1

3

投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。投影矩阵从视图空间转换到剪辑空间。通过除以w剪辑坐标的分量,剪辑空间中的坐标被转换为 (-1, -1, -1) 到 (1, 1, 1) 范围内的标准化设备坐标 (NDC)。

在正交投影中,视图空间中的坐标线性映射到剪辑空间坐标,剪辑空间坐标等于标准化设备坐标,因为w分量为 1(对于笛卡尔输入坐标)。

正投影

left、right、bottom、top、near 和 far 的值定义了一个框。盒子体积内的所有几何图形在视口上都是“可见的”。

最后,标准化的设备坐标线性映射到视口。

当您像这样设置正交投影时:

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

然后左下角(0, 0)和右上角(viewportWidth, viewportHeight)的矩形从(-1, -1)(1, 1)映射到 NDC 。

当您像这样设置视口时:

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为正值,则 y 轴变为负值,因为2/(top-bottom).

要解决的是您可以手动反转 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]
于 2018-08-25T20:23:45.117 回答