尝试使用带有 WebGL (regl) 的正交投影在屏幕 space 坐标中绘制形状
Trying to draw shapes in screen-space coordinates using an orthographic projection with WebGL (regl)
我正在尝试使用 regl 实现一个非常简单的 drawRect()
函数,最终能够在 screen 中传递 x、y、宽度、高度 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]
}
]);
目前它绘制了一个空白屏幕(我猜是由于投影矩阵剪裁或将所有内容移到了视口之外)。如果您从顶点着色器中删除投影或在投影属性中使用单位矩阵,则它会再次呈现蓝色正方形。
所以我的问题是:
- 我在这里做错了什么?为什么我的正交投影不起作用?
- 我以后要如何调试才能"see"得到坐标以及它们是怎么错的?
我已经阅读了一些 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 Isometric projection
Want an OpenGL 2D example (VC++, draw a rectangle)
http://songho.ca/opengl/gl_transform.html
编辑:修正了视口宽度在正射投影中被使用两次的问题。这修复了一个空白屏幕,但是原点现在是左下角而不是左上角。切换正射投影参数再次导致黑屏。
投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。投影矩阵从视图 space 变换到剪辑 space。剪辑 space 中的坐标通过除以 w
转换为 (-1, -1, -1) 到 (1, 1, 1) 范围内的标准化设备坐标 (NDC)剪辑坐标的分量。
在正交投影中,视图中的坐标 space 线性映射到剪辑 space 坐标,剪辑 space 坐标等于标准化设备坐标,因为 w
分量为1(对于笛卡尔输入坐标)。
left、right、bottom、top、near 和 far 的值定义了一个框。盒子体积内的所有几何体在视口上都是 "visible"。
最后,规范化的设备坐标被线性映射到视口。
当您像这样设置正交投影时:
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)[=100的视口矩形=].
这意味着,(0, 0, window.innerWidth, window.innerHeight) 中的视图坐标映射到视口矩形 (0, 0, window.innerWidth, window.innerHeight)。因此,您正在绘制 "window"(像素)坐标。
如果您有这样定义的几何体:
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
根据评论:
for mat4.ortho, top left origin should be mat4.ortho([], 0, viewportWidth, viewportHeight, 0, -1, 1)
but with that projection nothing appears on the screen again.
如果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]
我正在尝试使用 regl 实现一个非常简单的 drawRect()
函数,最终能够在 screen 中传递 x、y、宽度、高度 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]
}
]);
目前它绘制了一个空白屏幕(我猜是由于投影矩阵剪裁或将所有内容移到了视口之外)。如果您从顶点着色器中删除投影或在投影属性中使用单位矩阵,则它会再次呈现蓝色正方形。
所以我的问题是:
- 我在这里做错了什么?为什么我的正交投影不起作用?
- 我以后要如何调试才能"see"得到坐标以及它们是怎么错的?
我已经阅读了一些 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 Isometric projection Want an OpenGL 2D example (VC++, draw a rectangle) http://songho.ca/opengl/gl_transform.html
编辑:修正了视口宽度在正射投影中被使用两次的问题。这修复了一个空白屏幕,但是原点现在是左下角而不是左上角。切换正射投影参数再次导致黑屏。
投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。投影矩阵从视图 space 变换到剪辑 space。剪辑 space 中的坐标通过除以 w
转换为 (-1, -1, -1) 到 (1, 1, 1) 范围内的标准化设备坐标 (NDC)剪辑坐标的分量。
在正交投影中,视图中的坐标 space 线性映射到剪辑 space 坐标,剪辑 space 坐标等于标准化设备坐标,因为 w
分量为1(对于笛卡尔输入坐标)。
left、right、bottom、top、near 和 far 的值定义了一个框。盒子体积内的所有几何体在视口上都是 "visible"。
最后,规范化的设备坐标被线性映射到视口。
当您像这样设置正交投影时:
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)[=100的视口矩形=].
这意味着,(0, 0, window.innerWidth, window.innerHeight) 中的视图坐标映射到视口矩形 (0, 0, window.innerWidth, window.innerHeight)。因此,您正在绘制 "window"(像素)坐标。
如果您有这样定义的几何体:
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
根据评论:
for mat4.ortho, top left origin should be
mat4.ortho([], 0, viewportWidth, viewportHeight, 0, -1, 1)
but with that projection nothing appears on the screen again.
如果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]