Заполните три.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>