在保持透视的同时以像素分辨率使用WebGL画布

时间:2012-12-11 04:18:13

标签: html5 canvas html5-canvas webgl pixel

我以前在这里看过这类问题,但没有一个能给我一个完整的答案,所以我要发布我的代码,看看是否有人可以给我任何见解。基本上,这段代码有效,但我有一个神奇的z偏移量(高度* 1.205)[与宽度无关],我不知道为什么。

设置大小并创建对齐矢量:

gl.viewportWidth = gl.canvas.width;
gl.viewportHeight = gl.canvas.height;
gl.viewportHalfWidth = gl.viewportWidth / 2;
gl.viewportHalfHeight = gl.viewportHeight / 2;
gl.viewportAspect = gl.viewportWidth / gl.viewportHeight;
gl.viewportZoom = -gl.viewportHeight * 1.205;
if (gl.alignmentTest) {
    gl.alignmentVertices = (new Buffer(    // TL, TM, BM, BR, TR, BL, TL
       [    1.0,                        -1.0,                    0.0,
        gl.viewportHalfWidth - 0.5,     -1.0,                    0.0,
        gl.viewportHalfWidth - 0.5, -(gl.viewportHeight - 1.0),  0.0,
        gl.viewportWidth - 1.0,     -(gl.viewportHeight - 1.0),  0.0,
        gl.viewportWidth - 1.0,         -1.0,                    0.0,
            1.0,                    -(gl.viewportHeight - 1.0),  0.0,
            1.0,                        -1.0,                    0.0], 3)).post();
}

绘制对齐矢量:

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
mat4.perspective(45, gl.viewportAspect, 0.1, 1000.0, gl.perspective.current);
gl.perspective.post();    // ^ writes directly to current perspective buffer

gl.modelView.resetToIdentity();

gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

// Translate to pixel resolution
gl.modelView.push();
gl.modelView.translate([-gl.viewportHalfWidth, gl.viewportHalfHeight, gl.viewportZoom]);
gl.modelView.post();

// Alignment test?
if (gl.alignmentTest && gl.alignmentVertices) {
    gl.red.useAsAttribute(gl.shaderProg.vertexColour);
    gl.zero.vec2.useAsAttribute(gl.shaderProg.vertexTextureCoord);
    gl.alignmentVertices.drawAsPrimitives(gl.shaderProg.vertexPosition, gl.LINE_STRIP);
}

gl.disable(gl.BLEND);

有问题的行是:

gl.viewportZoom = -gl.viewportHeight * 1.205;

gl.modelView.translate([-gl.viewportHalfWidth, gl.viewportHalfHeight, gl.viewportZoom]);

此代码适用于任何大小。

以下是700x300的屏幕截图:http://oi49.tinypic.com/2my9ir7.jpg

另一个是400x600:http://oi46.tinypic.com/i2irh3.jpg

所以问题是:为什么这个神奇的比例因子1.205存在?或者我认为有必要做些什么呢?

1 个答案:

答案 0 :(得分:3)

我还没有计算出数学,但我猜这个因素来自你的透视投影矩阵的FOV和近平面。大多数试图获得1:1像素的应用程序也不使用透视投影。

如果您有理由使用透视,那么我建议不要使用基于FOV的透视计算,而是使用基于视锥体(glFrustum - 相似) - 实际推导投影视锥的效果要容易得多,你可以从第一原理中找出修正因子,而不是手工调整。


这实质上是一个三角问题。想象一下视锥体,或者更确切地说是视锥体(即没有被近处和远处的平面切断);它的尖端位于“摄像机位置”和四个倾斜的面。传统定义的FOV等于顶面和底面之间的角度(这就是为什么,如你所说,宽度没有影响)。但是,出于您的目的,您关心的参数不是角度,而是斜率(Z与X或Y移动的比率) - 这是您的任意数字的来源。

如果我们看一下the documentation for good ol' gluPerspective(我将假设使用与您的透视操作相同的数学运算),它会说:f = cotangent(fovy/2)。这正是我们关心的计算方式。 (如果尝试重现这一点,请注意数学库中的大多数trig函数都以弧度为单位,而不是度数。)

因子2出现是因为FOV通常被测量为全视角,但感兴趣的值是其中的一半 - “直线向前”和“仅在视图边缘”之间的角度。

如您所知,余切(或切线)函数的值可以解释为斜率。上面的斜率 f 可以准确地告诉您Z运动和X或Y运动之间的比率;例如,如果您将对象缩放某个值d,并沿Z轴移动f*d,则它似乎不会改变大小。这可能正是您viewportZoom已经在做的事情:

f / 2 = cot(45° / 2) / 2 ≈ 1.207106

非常接近你的1.205。如果事实确实如此,我还没有找出2因子的来源。

您需要做的就是计算f一次(或者可能是它的倒数tan(fovy/2),具体取决于哪个更方便)并使用它来计算“缩放”距离和投影基质


(顺便说一下,你的代码包含很多不是WebGL的东西,例如BufferpostdrawAsPrimitives等等。如果你提到了什么,这将会有所帮助你正在使用的图书馆。)