WebGL透视相机画布调整大小问题

时间:2014-06-11 21:56:58

标签: javascript matrix 3d webgl

编辑:已确认问题不是矩阵,而是画布。画布似乎调整大小,但它实际上是切断模型的一部分,直到我进行整个刷新。

现场预览:http://jsfiddle.net/B5B8k/6/

我将我的C ++底层引擎代码移植到Javascript,我设置了一个由6个顶点(2个三角形)组成的基本正方形,我试图渲染它。我现在发现问题与画布有关。画布正在调整大小到窗口的innerWidth和窗口的innerHeight。每当窗口被拉伸时,画布的一部分仍然是" gl.clear"颜色掩盖了我渲染的任何东西。

this.getPerspective = function(fov, aspect, zNear, zFar) {
    var yMax = zNear * Math.tan(fov * Math.PI / 360);
    var yMin = -yMax;
    var xMin = yMin * aspect;
    var xMax = yMax * aspect;
    return this.getFrustrum(xMin, xMax, yMin, yMax, zNear, zFar);
};
this.getFrustrum = function(left, right, bottom, top, zNear, zFar) {
    var X = 2*zNear/(right-left);
    var Y = 2*zNear/(top-bottom);
    var A = (right+left)/(right-left);
    var B = (top+bottom)/(top-bottom);
    var C = -(zFar + zNear)/(zFar - zNear);
    var D = -2*zFar*zNear/(zFar - zNear);

    return new Mat4(X,0,A,0, 0,Y,B,0, 0,0,C,D, 0,0,-1,0);
};

此功能需要"数字" :

  • fov(以度为单位)

  • 方面(窗口宽度/窗口高度)

  • z近剪裁平面

  • 和z远剪裁平面。

如果您需要查看更多代码,请询问。

1 个答案:

答案 0 :(得分:1)

简短的回答是你需要像

一样调用gl.viewport
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

您可以在resize中调用它,但可以说您应该在draw函数中调用它,因为将来如果使用帧缓冲对象,每次切换到帧缓冲区时都需要调用它与画布大小不同。

长版本是您永远不应该使用window.innerWidthwindow.innerHeight作为那些硬编码您的应用程序只能在一个完整的窗口中工作。而是使用canvas.clientWidthcanvas.clientHeight,因为它们适用于所有情况,例如画布仅覆盖页面的一部分,例如带有设置列的3d编辑器,或嵌入式,如文章中所示。 / p>

最重要的是,对于经常呈现与您相似的应用,最好是出于同样的原因调用每一帧的大小。

function draw() {
   resize();
   ...

用这样写的调整大小

        function resize() {
            var width = canvas.clientWidth;
            var height = canvas.clientHeight;
            if (canvas.clientWidth != width ||
                canvas.clientHeight != height) {
              canvas.width = canvas.clientWidth;
              canvas.height = canvas.clientHeight;
              perspective = new Mat4()
              perspective = perspective.getPerspective(60, canvas.clientWidth / canvas.clientHeight, 0.1, 100);
              perspective.mat[3][3] = 1;
              perspective = perspective.mul(new Mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -5, 0, 0, 0, 1));
           }
        }

原因是虽然resize事件在调整窗口大小时起作用,但如果画布由于其他原因而调整大小则无效。因此,在所有情况下调用resize每个帧都可以工作,因为仅在resize事件上调用resize只能在一个特定情况下工作。

您甚至可以在绘制函数中移动perspective.mat操作代码,因为如果将来您绘制到帧缓冲区(比如绘制阴影贴图),您无论如何都必须更改透视矩阵。尽管每帧尽可能少地工作当然是一个好主意,但在大型方案中,你只需要每帧一次或两次计算透视矩阵,而后来有更多对象,你可能会计算大量的东西在你的绘制循环中,所以计算透视矩阵所花费的时间相对来说是最小的成本。

最后,循环结束时gl.useProgram(0)WebGL无效,并在JavaScript控制台中产生大量错误。