用网格填充three.js场景

cs_*_*ndt 9 javascript 3d three.js

我在找什么

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

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

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

我做了什么

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

首先,我找到场景的边界(所有这些都在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();
Run Code Online (Sandbox Code Playgroud)

};

设置three.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();
},
Run Code Online (Sandbox Code Playgroud)

这些函数为每个屏幕角提供矢量,

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);
},
Run Code Online (Sandbox Code Playgroud)

我创建了一些几何图形,它将代表屏幕顶部的一条线,并尝试从顶部开始绘制线条并向下到屏幕底部.

// 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

问题

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

ara*_*ara 23

从ThreeJS r57开始,有一个名为GridHelper的帮助器,使用它可以轻松地绘制一个漂亮的网格,就像任何其他几何对象一样.

GridHelper有2个参数.第一个是网格的大小,第二个是两条线之间的步长

下面是在场景上绘制网格的代码,大小= 100,步长= 10

var grid = new THREE.GridHelper(100, 10);
scene.add(grid);
Run Code Online (Sandbox Code Playgroud)

在你的情况下,你可以避免使用一个名为drawGrid的方法,并直接用上面两行代码替换它,或者你可以在drawgrid方法中添加上面两行代码.

以下链接提供了一个实例

网格助手示例


gee*_*dew 11

你可以像这样画一个网格.

// each square
var planeW = 50; // pixels
var planeH = 50; // pixels 
var numW = 50; // how many wide (50*50 = 2500 pixels wide)
var numH = 50; // how many tall (50*50 = 2500 pixels tall)
var plane = new THREE.Mesh(
    new THREE.PlaneGeometry( planeW*numW, planeH*numH, planeW, planeH ),
    new THREE.MeshBasicMaterial( {
        color: 0x000000,
        wireframe: true
    } )
);

scene.add(plane);
Run Code Online (Sandbox Code Playgroud)

  • 请注意,现在绘制对角线 (2认同)
  • 我不相信这会再按预期工作。 (2认同)

Bre*_*nny 10

所以你的代码中有两个错误.

首先,您从第一行开始,MAX_Y然后将每行放在最后一行之下.相对较小的错误就在于getNWScreenVector,getNEScreenVector你将线的顶点放在高度上MAX_Y,然后放入

line.position.y = this.MAX_Y - (c * this.gridBlockSize);
Run Code Online (Sandbox Code Playgroud)

你正在为这一行添加一个翻译MAX_Y - (c * this.gridBlockSize),它给出了最终的y位置MAX_Y + MAX_Y - (c * this.gridBlockSize),这个位置MAX_Y太多了.如果是有意义的程序开始与线从下降getNWScreenVectorgetNEScreenVector,那么你应该改变line.position.y路线,只是

line.position.y = -c * this.gridBlockSize;
Run Code Online (Sandbox Code Playgroud)

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

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

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

halfDisplayHeight = 1000 * Math.tan(45 / 2 * (Math.PI / 180));
Run Code Online (Sandbox Code Playgroud)

或加倍

this.DISPLAY_HEIGHT = 2 * 1000 * Math.tan(45 / 2 * (Math.PI / 180));
Run Code Online (Sandbox Code Playgroud)

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

this.DISPLAY_WIDTH = this.DISPLAY_HEIGHT * (this.SCREEN_WIDTH / this.SCREEN_HEIGHT);
Run Code Online (Sandbox Code Playgroud)

这些是您用于放置线条的值.全部一起:

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);
Run Code Online (Sandbox Code Playgroud)

最后,你想要在整个区域上分布线条,所以你应该设置

this.gridBlockSize = this.DISPLAY_HEIGHT / numHorizontalGridLines;
Run Code Online (Sandbox Code Playgroud)

你可以看到在这里工作:http://jsfiddle.net/SeNDk/55/

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

this.camera.position.z = this.SCREEN_HEIGHT / (2 * Math.tan(45 / 2 * (Math.PI / 180)));
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到:http://jsfiddle.net/SeNDk/53/

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