Заполните три.JS сцена с сеткой

то, что я ищу

отображать сетку в три.сцена 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();

};

три настройки.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();
},

эти функции обеспечивают вектор для каждого угла экрана,

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);
},

Я создаю некоторую геометрию, которая будет представлять линию в самой верхней части экрана, и пытаюсь рисовать линии начиная сверху и спускаясь к нижней части экрана.

// 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 первая строка начинается с экрана, а ширина строк не заполняет ширину экрана.

4 ответов


таким образом, в вашем коде есть две ошибки.

во-первых, вы начинаете с строки MAX_Y а затем положить каждую строку на фиксированное расстояние ниже последней. Относительно незначительная ошибка заключается в том, что в getNWScreenVector и getNEScreenVector вы ставите вершины линии на высоту MAX_Y, затем в

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

вы добавляете перевод в строку MAX_Y - (c * this.gridBlockSize), что дает конечную позицию y MAX_Y + MAX_Y - (c * this.gridBlockSize), который является MAX_Y слишком много. Если это смысл вашей программы начните с линий, спускающихся от getNWScreenVector и getNEScreenVector, то вы должны изменить line.position.y линия просто

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

вы можете видеть, что линии теперь сосредоточены на jsFiddle, но они по-прежнему неправильно размера. Это потому, что вы не учитываете тот факт, что ваша сцена находится в перспективе. Все ваши линии имеют свои координаты z, установленные в 0, поэтому, когда вы устанавливаете this.camera.position.z = 1000, они 1000 блоков далеко от камеры. Нет никаких оснований предполагать, что что-то с той же шириной, что и ширина пикселя холста, будет иметь ту же ширину при рисовании в перспективе от 1000 единиц.

вместо этого мы можем рассчитать, насколько большими они должны быть. Я не буду вдаваться в полное объяснение перспективы здесь, но мы можем выяснить, насколько велика область, которую линии должны покрыть, чтобы покрыть экран. Вы указали вертикальный FOV 45 градусов в конструкторе камеры и расстояние 1000 между камерой и вашими линиями. Три.js в основном показывает решение, если вы пик в том, как он создает матрицу перспективы: makePerspective

во-первых, нам нужно вертикальное расстояние верхней половины экрана, так как 0 находится в центре экрана. Math.tan(45 / 2 * (Math.PI / 180)) (тангенс половины угла, преобразуется в радианы) дает вертикальное расстояние, деленное на расстояние от камеры, так что вы можете рассчитать высоту с

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

и два

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

горизонтальный FOV не то же самое, если холст не квадратный, но отношение ширины и высоты линии будет пропорционально отношению ширины и высоты экрана. Как три.js делает, вы можете просто умножить на соотношение сторон, которое вы также предоставили конструктору камеры, чтобы выяснить ширину:

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

это значения, которые следует использовать для размещения строк. Все вместе:

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);

наконец, вы хотите, чтобы распространить линии по полной площадь, поэтому вы должны установить

this.gridBlockSize = this.DISPLAY_HEIGHT / numHorizontalGridLines;

вы можете видеть, что работает здесь:http://jsfiddle.net/SeNDk/55/

есть еще один способ сделать это, хотя, который состоит в том, чтобы сохранить линии одинаковыми, но переместить камеру ближе к вашим линиям, так что ширина линий просто случается равным ширине пикселя холста на этом расстоянии. Формула является просто переработкой вышеизложенного для DISPLAY_HEIGHT, но вместо разрешать для высоты, мы разрешаем для расстояния когда высота равна высоте экрана:

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

вы можете увидеть это здесь:http://jsfiddle.net/SeNDk/53/

это гораздо меньшее изменение в вашем коде, но это означает, что положение камеры изменится в зависимости от того, насколько велик холст, что может повлиять на другой контент, который вам нужно точно разместить, поэтому выбор за вами.


начиная с ThreeJS r57, есть помощник gridhelper, с помощью которого вы можете легко нарисовать красивую сетку, как и любой другой геометрический объект.

GridHelper принимает 2 параметра. Первый-это размер сетки, а второй-размер шага между 2 строками

Ниже приведен код для рисования сетки на сцене, с размером = 100 и шагом = 10

var grid = new THREE.GridHelper(100, 10);
scene.add(grid);

в вашем случае вы можете избежать использования метода drawGrid и напрямую замените это на код выше двух строк, или вы можете добавить эти выше двух строк кода с помощью метода drawgrid.

живой пример доступен здесь по следующей ссылке

Пример Помощника Сетки


вы можете нарисовать сетку, как это.

// 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);

этот поток помог с изменением техники, применяемой к-кадру и с небольшим поворотом; динамически создавая и калибруя сетку как функцию ограничивающего прямоугольника для детей сцены.

Примечание: следующий метод должен быть вызван, чтобы получить координаты ограничивающего поля, которые изначально являются нулевыми, в отличие от ограничивающей сферы, которая предварительно вычисляется для вас:

'ограничивающего прямоугольника.геометрия.computeBoundingBox ();'

сайт CodePen для этого 'scene-ario' (каламбур) можно найти здесь:https://codepen.io/ubermario/pen/JvZMPg

    <!DOCTYPE html>
    <html lang="en" >

    <head>

      <meta charset="UTF-8">
      <link rel="shortcut icon" type="image/x-icon" href="https://static.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" />
      <link rel="mask-icon" type="" href="https://static.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" />
      <title>CodePen -  A-Frame Dynamically Sized GridHelper</title>     
    </head>

    <body translate="no" >

      <script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script>
      <a-scene>
        <a-entity>
          <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
          <a-box position="-1 0.5 -3" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box>
          <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
          <!-- replaced with gridhelper>
      <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane> -->
          <!-- Remove sky; it distorts the dimensions of just the sphere, box, cylinder objects
          <a-sky color="#ECECEC"></a-sky>
          -->
          <a-entity>
      </a-scene>




        <script >
          AFRAME.registerComponent("cmpGridHelper", {
      schema: {
        size: { default: 5, type: "int" },
        divisions: { default: 10, type: "int" },
        colorCenterLine: { default: "red", type: "color" },
        colorGrid: { default: "black", type: "color" },
        x: { default: 0, type: "number" },
        y: { default: 0, type: "number" },
        z: { default: 0, type: "number" }
      },
      init: function() {
        var entity = this.el.object3D,
          schema = this.data,
          grid = new THREE.GridHelper(
            schema.size,
            schema.divisions,
            schema.colorCenterLine,
            schema.colorGrid
          );
        grid.name = "gridHelper";
        entity.add(grid);
      },
      update: function(oldData) {
        var entity = this.el.object3D,
          grid = entity.getObjectByName("gridHelper"),
          schema = this.data;
        grid.position.set(schema.x, schema.y, schema.z);
      },
      remove: function() {
        var entity = this.el.object3D;
        entity.remove(entity.getObjectByName("gridHelper"));
      }
    });

    var sceneObj3d = document.querySelector("a-scene");

    if (sceneObj3d.hasLoaded) {
      fnAddDyanmicGridHelper();
    } else {
      sceneObj3d.addEventListener("loaded", fnAddDyanmicGridHelper);
    }

function fnAddDyanmicGridHelper() {
  //var helper = new THREE.BoundingBoxHelper(sceneObj3d.querySelector('a-entity').object3D, 0xff0000);
  var childObjects = sceneObj3d.querySelector('a-entity').object3D;
  var boundingBox = new THREE.BoxHelper(childObjects, 0xff0000);
  //Uncomment the next line to display the bounding box
  //childObjects.add(boundingBox);
  //Need the following method call to get the geometry.boundingBox coordinates in addition to the sphere coordinates
  boundingBox.geometry.computeBoundingBox();                            

  var x = boundingBox.geometry.boundingSphere.center.x;
  var z = boundingBox.geometry.boundingSphere.center.z;
  var size = boundingBox.geometry.boundingSphere.radius * 2;
  sceneObj3d.setAttribute("cmpGridHelper", {
    x: x,
    z: z,
    y: boundingBox.geometry.boundingBox.min.y,
    size: size,
    divisions: size
  });
}
        </script>
    </body>

    </html>