三个 JS:如何在单独的 DIV 中呈现不同的模型?
THREEJS: How to render different models in seperate DIVs?
(我是 Three.js 的新手)
描述
我正在为多个 stl 模型开发 3D 查看器。每个模型都应该在单独的 div 中呈现,因此您会得到一个包含不同模型的网格。
文件名存储在 json 文件中。我使用 for 循环遍历 json 并将文件名存储在 "var filename" 中。我使用该变量来完成模型的路径。
详情
var filename = data[i].Filename;
通过 for 循环,我可以从 json 文件中获取所有文件名。
loader.load('model/' + filename, function(geometry) {...
然后我添加文件名来完成模型路径。
modelContainer = document.createElement('div');
modelContainer.className = modelClass;
document.getElementById(modelId).appendChild(modelContainer);
在具有给定 ID 的 div 中创建了具有 class 名称的 div。并且模型应该渲染成 div 内的 canvas(div 和 class)。
问题
See image
现在发生的是代码将所有模型(本例中为 3 个模型)放入最后一个 div。用于模型 1 和 2 的前两个 div 是黑色的。
经过大量试验和分析,我仍未找到解决方案。 也许我必须将所有场景存储在一个数组中,并在渲染函数中循环所有场景?
完成 Html 和 Javascript
您可以在此处找到完整的项目代码(不含 .stl 模型):
https://codepen.io/indoguy/project/editor/XqNKGQ
<div class="container">
<div class="row" id="modelGrid">
<!-- The models will be uploaded within this grid -->
</div>
</div>
<script src="js/three.js"></script>
<script src="js/STLLoader.js"></script>
<script src="js/WebGL.js"></script>
<script>
var modelDiv = document.getElementById('modelGrid');
var ourRequest = new XMLHttpRequest();
// In this code snippit I will read the model filenames out of the json.
// Add URL where the json is stored
ourRequest.open('GET', 'http://localhost:8888/3D%20Modeling/3D%20Previewer/3D%20Previewer%20Library/models.json');
ourRequest.onload = function() {
var queueData = JSON.parse(ourRequest.responseText);
renderHTML(queueData);
};
ourRequest.send();
function renderHTML(data) {
// 3D Model Process
if (WEBGL.isWebGLAvailable() === false) {
document.body.appendChild(WEBGL.getWebGLErrorMessage());
}
// Editable
var divWidth = 300;
var divHeight = 300;
var modelId = 'modelGrid';
var modelClass = 'col-sm';
// Don't change
var modelContainer;
var camera, cameraTarget, scene, renderer;
var cameraFov = 45;
init(data);
animate();
// init
function init(data) {
// For loop for every model in the json file
for (i = 0; i < data.length; i++) {
var filename = data[i].Filename;
// Scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffe5e5);
// Camera
camera = new THREE.PerspectiveCamera(cameraFov, divWidth / divHeight, 1, 15);
cameraTarget = new THREE.Vector3(0, -0.25, 0);
// Creates a div with the name of the value of var className, inside modelGrid id
modelContainer = document.createElement('div');
modelContainer.className = modelClass; // Add class to the div
document.getElementById(modelId).appendChild(modelContainer);
// STL Loader
var loader = new THREE.STLLoader();
loader.load('model/' + filename, function(geometry) {
var material = new THREE.MeshStandardMaterial({
color: 0xff1919,
roughness: 0.5,
metalness: 0
});
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, -0.5, 0); // Position with the Y value
mesh.rotation.set(0, -Math.PI / 2, 0);
mesh.scale.set(0.15, 0.15, 0.15);
scene.add(mesh); // add model to the scene
});
// Lights
scene.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444));
var light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.set(1, 1, 1);
scene.add(light);
// renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0xffffff, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(divWidth, divHeight);
// renderer.setSize(window.innerWidth, window.innerHeight);
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.shadowMap.enabled = true;
modelContainer.appendChild(renderer.domElement);
// window.addEventListener('resize', onWindowResize, false);
}
}
// animate
function animate() {
render();
requestAnimationFrame(animate);
}
// render
function render() {
renderer.setClearColor(0xffffff);
var timer = Date.now() * 0.0005;
camera.position.x = Math.cos(timer) * 3;
camera.position.z = Math.sin(timer) * 3;
camera.lookAt(cameraTarget);
renderer.render(scene, camera);
}
};
</script>
问题是您只保存和渲染单个 three.js 渲染器。在您的 for 循环中,这一行覆盖了全局 renderer
变量,该变量在您的 render()
函数中被引用:
renderer = new THREE.WebGLRenderer({
antialias: true
});
有几种方法可以解决这个问题,包括将渲染器和相机添加到数组中并在单个渲染函数中迭代它们或为每个渲染器创建一个新的 animate()
循环等。这是一个粗略的第二个选项的例子:
// For loop for every model in the json file
for (i = 0; i < data.length; i++) {
var filename = data[i].Filename;
// EDIT Fix3: These variables must be defined inside of the for loop
// scope otherwise they will be shared by all loop iterations
var modelContainer;
var camera, cameraTarget, scene;
// ...
// Fix 1: Add var declaration so the variable is scoped to this
// block and variables are not defined globally and overwritten on
// subsequent iterations. This is needed for all the variables
// in this block (scene, camera, etc)
// renderer
var renderer = new THREE.WebGLRenderer({
antialias: true
});
// ...
// Fix 2: Create an animation loop for each renderer instead
// of a single global one
// animate
function animate() {
render();
requestAnimationFrame(animate);
}
// render
function render() {
renderer.setClearColor(0xffffff);
var timer = Date.now() * 0.0005;
camera.position.x = Math.cos(timer) * 3;
camera.position.z = Math.sin(timer) * 3;
camera.lookAt(cameraTarget);
renderer.render(scene, camera);
}
animate();
}
肯定有更简洁的方法,但希望这能帮助您入门!让我知道是否可行,或者您需要澄清。
编辑:我刚刚注意到您的图像显示所有三个模型都显示在彼此之上。这又是因为 scene
变量是全局声明的,并且在触发异步 load
函数时它被设置为最终场景对象。
(我是 Three.js 的新手)
描述
我正在为多个 stl 模型开发 3D 查看器。每个模型都应该在单独的 div 中呈现,因此您会得到一个包含不同模型的网格。 文件名存储在 json 文件中。我使用 for 循环遍历 json 并将文件名存储在 "var filename" 中。我使用该变量来完成模型的路径。
详情
var filename = data[i].Filename;
通过 for 循环,我可以从 json 文件中获取所有文件名。
loader.load('model/' + filename, function(geometry) {...
然后我添加文件名来完成模型路径。
modelContainer = document.createElement('div');
modelContainer.className = modelClass;
document.getElementById(modelId).appendChild(modelContainer);
在具有给定 ID 的 div 中创建了具有 class 名称的 div。并且模型应该渲染成 div 内的 canvas(div 和 class)。
问题
See image 现在发生的是代码将所有模型(本例中为 3 个模型)放入最后一个 div。用于模型 1 和 2 的前两个 div 是黑色的。
经过大量试验和分析,我仍未找到解决方案。 也许我必须将所有场景存储在一个数组中,并在渲染函数中循环所有场景?
完成 Html 和 Javascript
您可以在此处找到完整的项目代码(不含 .stl 模型): https://codepen.io/indoguy/project/editor/XqNKGQ
<div class="container">
<div class="row" id="modelGrid">
<!-- The models will be uploaded within this grid -->
</div>
</div>
<script src="js/three.js"></script>
<script src="js/STLLoader.js"></script>
<script src="js/WebGL.js"></script>
<script>
var modelDiv = document.getElementById('modelGrid');
var ourRequest = new XMLHttpRequest();
// In this code snippit I will read the model filenames out of the json.
// Add URL where the json is stored
ourRequest.open('GET', 'http://localhost:8888/3D%20Modeling/3D%20Previewer/3D%20Previewer%20Library/models.json');
ourRequest.onload = function() {
var queueData = JSON.parse(ourRequest.responseText);
renderHTML(queueData);
};
ourRequest.send();
function renderHTML(data) {
// 3D Model Process
if (WEBGL.isWebGLAvailable() === false) {
document.body.appendChild(WEBGL.getWebGLErrorMessage());
}
// Editable
var divWidth = 300;
var divHeight = 300;
var modelId = 'modelGrid';
var modelClass = 'col-sm';
// Don't change
var modelContainer;
var camera, cameraTarget, scene, renderer;
var cameraFov = 45;
init(data);
animate();
// init
function init(data) {
// For loop for every model in the json file
for (i = 0; i < data.length; i++) {
var filename = data[i].Filename;
// Scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffe5e5);
// Camera
camera = new THREE.PerspectiveCamera(cameraFov, divWidth / divHeight, 1, 15);
cameraTarget = new THREE.Vector3(0, -0.25, 0);
// Creates a div with the name of the value of var className, inside modelGrid id
modelContainer = document.createElement('div');
modelContainer.className = modelClass; // Add class to the div
document.getElementById(modelId).appendChild(modelContainer);
// STL Loader
var loader = new THREE.STLLoader();
loader.load('model/' + filename, function(geometry) {
var material = new THREE.MeshStandardMaterial({
color: 0xff1919,
roughness: 0.5,
metalness: 0
});
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, -0.5, 0); // Position with the Y value
mesh.rotation.set(0, -Math.PI / 2, 0);
mesh.scale.set(0.15, 0.15, 0.15);
scene.add(mesh); // add model to the scene
});
// Lights
scene.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444));
var light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.set(1, 1, 1);
scene.add(light);
// renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0xffffff, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(divWidth, divHeight);
// renderer.setSize(window.innerWidth, window.innerHeight);
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.shadowMap.enabled = true;
modelContainer.appendChild(renderer.domElement);
// window.addEventListener('resize', onWindowResize, false);
}
}
// animate
function animate() {
render();
requestAnimationFrame(animate);
}
// render
function render() {
renderer.setClearColor(0xffffff);
var timer = Date.now() * 0.0005;
camera.position.x = Math.cos(timer) * 3;
camera.position.z = Math.sin(timer) * 3;
camera.lookAt(cameraTarget);
renderer.render(scene, camera);
}
};
</script>
问题是您只保存和渲染单个 three.js 渲染器。在您的 for 循环中,这一行覆盖了全局 renderer
变量,该变量在您的 render()
函数中被引用:
renderer = new THREE.WebGLRenderer({
antialias: true
});
有几种方法可以解决这个问题,包括将渲染器和相机添加到数组中并在单个渲染函数中迭代它们或为每个渲染器创建一个新的 animate()
循环等。这是一个粗略的第二个选项的例子:
// For loop for every model in the json file
for (i = 0; i < data.length; i++) {
var filename = data[i].Filename;
// EDIT Fix3: These variables must be defined inside of the for loop
// scope otherwise they will be shared by all loop iterations
var modelContainer;
var camera, cameraTarget, scene;
// ...
// Fix 1: Add var declaration so the variable is scoped to this
// block and variables are not defined globally and overwritten on
// subsequent iterations. This is needed for all the variables
// in this block (scene, camera, etc)
// renderer
var renderer = new THREE.WebGLRenderer({
antialias: true
});
// ...
// Fix 2: Create an animation loop for each renderer instead
// of a single global one
// animate
function animate() {
render();
requestAnimationFrame(animate);
}
// render
function render() {
renderer.setClearColor(0xffffff);
var timer = Date.now() * 0.0005;
camera.position.x = Math.cos(timer) * 3;
camera.position.z = Math.sin(timer) * 3;
camera.lookAt(cameraTarget);
renderer.render(scene, camera);
}
animate();
}
肯定有更简洁的方法,但希望这能帮助您入门!让我知道是否可行,或者您需要澄清。
编辑:我刚刚注意到您的图像显示所有三个模型都显示在彼此之上。这又是因为 scene
变量是全局声明的,并且在触发异步 load
函数时它被设置为最终场景对象。