用网格填充three.js场景 [英] Fill three.js scene with a grid

查看:28
本文介绍了用网格填充three.js场景的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个 Three.js 场景中显示一个填充整个场景的网格.在这种情况下,场景是整个窗口.

Display a grid within a three.js scene that fills the entire scene. In this case, the scene is the whole window.

这个网格代表一个 3D 表面,可以使用 THREE.TrackballControls 用鼠标移动这个网格面向相机,所以最初它看起来像一个平面 (2D) 表面,直到轨迹球被用鼠标拉动.

This grid represents a surface in 3D and can be moved around with the mouse using THREE.TrackballControls This grid is facing the camera so initially it looks like a flat (2D) surface until the trackball is pulled around with the mouse.

网格线的宽度应等于渲染器的宽度.

The width of the grid lines should equal to the width of the renderer.

我已经为我目前所做的设置了一个有效的 jsFiddle.

I have setup a working jsFiddle for what I have done so far.

首先我找到场景的边界(所有这些都在 jsFiddle 中),

First I find the bounds of the scene (all of this is in the jsFiddle),

App = function(sceneContainerName) {
   this.sceneContainerName = sceneContainerName;

   this.SCREEN_WIDTH = window.innerWidth;
   this.SCREEN_HEIGHT = window.innerHeight;

   this.MAX_X = this.SCREEN_WIDTH / 2;
   this.MIN_X = 0 - (this.SCREEN_WIDTH / 2);
   this.MAX_Y = this.SCREEN_HEIGHT / 2;
   this.MIN_Y = 0 - (this.SCREEN_HEIGHT / 2);

   this.NUM_HORIZONTAL_LINES = 50;

   this.init();

};

设置三个.js

init: function() {
   // init scene
   this.scene = new THREE.Scene();

   // init camera
   // View Angle, Aspect, Near, Far
   this.camera = new THREE.PerspectiveCamera(45, this.SCREEN_WIDTH / this.SCREEN_HEIGHT, 1, 10000);
   // set camera position
   this.camera.position.z = 1000;
   this.camera.position.y = 0;

   // add the camera to the scene
   this.scene.add(this.camera);

   this.projector = new THREE.Projector();

   // init renderer
   this.renderer = new THREE.CanvasRenderer();
   // start the renderer
   this.renderer.setSize(this.SCREEN_WIDTH, this.SCREEN_HEIGHT);

   this.drawGrid(this.NUM_HORIZONTAL_LINES);

   this.trackball = new THREE.TrackballControls(this.camera, this.renderer.domElement);
   this.trackball.staticMoving = true;

   var me = this;

   this.trackball.addEventListener('change', function() {
       me.render();
   });

   // attach the render-supplied DOM element
   var container = document.getElementById(this.sceneContainerName);
   container.appendChild(this.renderer.domElement);

   this.animate();
},

这些函数为每个屏幕角落提供一个向量,

These functions provide a vector for each screen corner,

getNWScreenVector: function() {
    return new THREE.Vector3(this.MIN_X, this.MAX_Y, 0);
},

getNEScreenVector: function() {
    return new THREE.Vector3(this.MAX_X, this.MAX_Y, 0);
},

getSWScreenVector: function() {
    return new THREE.Vector3(this.MIN_X, this.MIN_Y, 0);
},

getSEScreenVector: function() {
    return new THREE.Vector3(this.MAX_X, this.MIN_Y, 0);
},

我创建了一些几何图形来表示屏幕最顶部的一条线,并尝试从屏幕顶部开始画线,一直到屏幕底部.

I create some geometry that will represent a line at the very top of the screen, and try to draw lines starting from the top and going down to the bottom of the screen.

// drawGrid will determine blocksize based on the 
// amount of horizontal gridlines to draw
drawGrid: function(numHorizontalGridLines) {

    // Determine the size of a grid block (square)
    this.gridBlockSize = this.SCREEN_HEIGHT / numHorizontalGridLines;

    var geometry = new THREE.Geometry();
    geometry.vertices.push(this.getNWScreenVector());
    geometry.vertices.push(this.getNEScreenVector());

    var material = new THREE.LineBasicMaterial({
        color: 0x000000,
        opacity: 0.2
    });

    for (var c = 0; c <= numHorizontalGridLines; c++) {
        var line = new THREE.Line(geometry, material);
        line.position.y = this.MAX_Y - (c * this.gridBlockSize);
        this.scene.add(line);
    }
}

问题

此方法不起作用,在 jsFiddle 中,第一行从屏幕开始,并且行的宽度未填充屏幕宽度.

The problem

This method does not work, in the jsFiddle the first line starts off the screen and the width of the lines do not fill the screen width.

推荐答案

因此您的代码中有两个错误.

So there are two bugs in your code.

首先,您从 MAX_Y 处的那条线开始,然后将每条线放在最后一条线下方固定距离处.相对较小的错误是在 getNWScreenVectorgetNEScreenVector 中,您将线的顶点放在 MAX_Y 的高度,然后在

First, you're starting with the line at MAX_Y and then putting each line a fixed distance below the last. The relatively minor bug is that in getNWScreenVector and getNEScreenVector you're putting the line's vertices at a height of MAX_Y, then in

line.position.y = this.MAX_Y - (c * this.gridBlockSize);

您正在向 MAX_Y - (c * this.gridBlockSize) 行添加翻译,它给出 MAX_Y + MAX_Y - (c * this.gridBlockSize) 的最终 y 位置),这是一个 MAX_Y 太多了.如果您的程序以从 getNWScreenVectorgetNEScreenVector 降序的行开始,那么您应该将 line.position.y 行更改为只是

you're adding a translation to the line of MAX_Y - (c * this.gridBlockSize), which gives a final y position of MAX_Y + MAX_Y - (c * this.gridBlockSize), which is one MAX_Y too many. If it makes sense to your program to start with lines descending from getNWScreenVector and getNEScreenVector, then you should change the line.position.y line to just

line.position.y = -c * this.gridBlockSize;

您可以看到线条现在以 jsFiddle 为中心,但它们的大小仍然不正确.这是因为您没有考虑到场景是透视的这一事实.您的线条都将其 z 坐标设置为 0,因此当您设置 this.camera.position.z = 1000 时,它们距离相机 1000 个单位.没有理由假设宽度与画布像素宽度相同的东西在 1000 个单位以外的透视图中绘制时具有相同的宽度.

You can see that the lines are now centered on jsFiddle, but they're still sized incorrectly. This is because you aren't accounting for the fact that your scene is in perspective. Your lines all have their z coordinates set to 0, so when you set this.camera.position.z = 1000, they are 1000 units away from the camera. There is no reason to assume that something with the same width as the pixel width of the canvas will have the same width when drawn in perspective from 1000 units away.

相反,我们可以计算它们需要多大.我不会在这里详细解释透视图,但我们可以计算出线条需要覆盖多大的区域才能覆盖屏幕.您已在相机构造函数中指定了 45 度的垂直 FOV,相机与线条之间的距离为 1000.如果您深入了解它如何创建透视矩阵,three.js 基本上会显示解决方案:makePerspective

Instead, we can calculate how big they need to be. I won't go in to a full explanation of perspective here, but we can figure out how big an area the lines need to cover to cover the screen. You've specified a vertical FOV of 45 degrees in your camera constructor and a distance of 1000 between the camera and your lines. Three.js basically shows the solution if you peak into how it creates the perspective matrix: makePerspective

首先,我们需要屏幕上半部分的垂直距离,因为 0 位于屏幕中心.Math.tan(45/2 * (Math.PI/180))(角度一半的切线,转换为弧度)给出垂直距离除以离相机的距离,所以你可以用

First, we need the vertical distance of the upper half of the screen, since 0 is at the center of the screen. Math.tan(45 / 2 * (Math.PI / 180)) (tangent of half the angle, converted to radians) gives the vertical distance divided by the distance away from the camera, so you can calculate the height with

halfDisplayHeight = 1000 * Math.tan(45 / 2 * (Math.PI / 180));

或加倍

this.DISPLAY_HEIGHT = 2 * 1000 * Math.tan(45 / 2 * (Math.PI / 180));

水平FOV除非画布是方形的,否则线区域的宽高比会和屏幕的宽高比成正比.像three.js一样,你可以乘以你也提供给相机构造函数的纵横比来计算宽度:

The horizontal FOV is not the same unless the canvas is square, but the width and height ratio of the line area will be proportional to the width and height ratio of the screen. Like three.js does, you can just multiply by the aspect ratio you also provided to the camera constructor to figure out the width:

this.DISPLAY_WIDTH = this.DISPLAY_HEIGHT * (this.SCREEN_WIDTH / this.SCREEN_HEIGHT);

这些是您应该用于放置线条的值.一起来:

These are the values that you should use for placing your lines. All together:

this.DISPLAY_HEIGHT = 2 * 1000 * Math.tan(45 / 2 * (Math.PI / 180));
this.DISPLAY_WIDTH = this.DISPLAY_HEIGHT * (this.SCREEN_WIDTH / this.SCREEN_HEIGHT);
this.MAX_X = this.DISPLAY_WIDTH / 2;
this.MIN_X = 0 - (this.DISPLAY_WIDTH / 2);
this.MAX_Y = this.DISPLAY_HEIGHT / 2;
this.MIN_Y = 0 - (this.DISPLAY_HEIGHT / 2);

最后,您需要将线条分布在整个区域,因此您应该设置

Finally, you'll want to spread the lines over the full area, so you should set

this.gridBlockSize = this.DISPLAY_HEIGHT / numHorizontalGridLines;

你可以在这里看到它的工作:http://jsfiddle.net/pa46hxwo/

You can see that working here: http://jsfiddle.net/pa46hxwo/

不过,还有另一种方法可以做到这一点,即保持线条不变,但将相机移近线条,使线条的宽度恰好等于该距离处画布的像素宽度.该公式只是对上述 DISPLAY_HEIGHT 的修改,但不是求解高度,而是求解高度等于屏幕高度时的距离:

There is another way to do this, though, which is to keep the lines the same, but move the camera closer to your lines so that the width of the lines just happens to equal the pixel width of the canvas at that distance. The formula is just a reworking of the above for DISPLAY_HEIGHT, but instead of solving for height, we solve for a distance when the height equals the screen height:

this.camera.position.z = this.SCREEN_HEIGHT / (2 * Math.tan(45 / 2 * (Math.PI / 180)));

你可以在这里看到:http://jsfiddle.net/0jtykgrL/

这是对您的代码的一个小得多的更改,但这意味着相机位置将根据画布的大小而变化,这可能会影响您需要精确放置的其他内容,因此选择权在您手中.

It's a much smaller change to your code, but it means that the camera position will change depending on how big the canvas is, which could affect other content which you need to place precisely, so the choice is yours.

这篇关于用网格填充three.js场景的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆