three.js如何创建一个天空盒

three.js如何创建一个天空盒

天空盒通常用于视频游戏中,以创建遥远的三维背景的错觉。天空盒本质上是一个立方体,立方体的每一侧都有纹理。然后将播放器或摄像机放置在立方体中,以便所有六个纹理都围绕它们,使它们产生它们处于更大环境中的错觉。

Three.js设置

首先,我们将调用来初始化 Three.js。我们将使用一个透视摄像机,其位置缩小得很远,这样我们就可以在跳入之前看到盒子。我们将使用一个 PerspectiveCamera,其位置被缩小了很多,这样我们可以在跳入之前看到该框。我们还将使用 并将其附加到页面主体。最后,该函数将使用我们添加的任何更新处理重新渲染场景.

let scene, camera, renderer, skyboxGeo, skybox;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(
55,
window.innerWidth / window.innerHeight,
45,
30000
);
camera.position.set(1200, -250, 20000);

renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.domElement.id = “canvas”;
document.body.appendChild(renderer.domElement);
animate();
}
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}

init();

导入核心 Three.js 库。

<script src=”https://threejs.org/build/three.min.js”></script>

将主体高度设置为视口高度,并为主体添加灰色背景,以便我们可以看到立方体。

body {
margin: 0;
height: 100vh;
background: #bdc3c7;
}

由于我们还没有添加任何对象,我们现在只会看到灰色背景。

添加 Three.js 盒。 我们可以添加一个带有 、 和设置为 的盒。然后使用对其应用纹理,在这种情况下,它将默认为纯纹理。最后,在函数内调用函数之前将对象添加到场景中。

function init() {

skyboxGeo = new THREE.BoxGeometry(10000, 10000, 10000);
skybox = new THREE.Mesh(skyboxGeo);
scene.add(skybox);

animate();

尽管它是一个立方体,但它看起来像一个正方形,因为我们直接观察它。为了验证它是一个立方体,我们可以在函数中添加一个旋转动画:animate

function animate() {
skybox.rotation.x += 0.005;
skybox.rotation.y += 0.005;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}

天空盒网格材质

您可以在 opengameart.org 上找到免费的天空盒图像,或者您可以在 Google 上搜索“免费天空盒图像”。应该有六个图像对应于无缝啮合在一起的立方体的每一面。例如,对于 React Native Infinity,六个空间图像对应于不同的边,如下所示。

每张图片要根据对应的边来命名,比如是前图,是右图,是底图。我们需要按照三个步骤将这些图像添加到我们的立方体中:.purplenebula_ft.pngpurplenebula_rt.pngpurplenebula_dn.png

将每个图像加载为纹理

将每个纹理映射到材质数组

将 Material 数组添加到 Skybox 立方体

1.加载图像作为纹理

可以使用函数在 Three.js 中加载纹理。该方法将图像的路径作为参数。我们可以通过创建六个函数来加载每个图像,如下所示:TextureLoader().load()load()TextureLoader()

const ft = new THREE.TextureLoader().load(“purplenebula_ft.jpg”);
const bk = new THREE.TextureLoader().load(“purplenebula_bk.jpg”);
const up = new THREE.TextureLoader().load(“purplenebula_up.jpg”);
const dn = new THREE.TextureLoader().load(“purplenebula_dn.jpg”);
const rt = new THREE.TextureLoader().load(“purplenebula_rt.jpg”);
const lf = new THREE.TextureLoader().load(“purplenebula_lf.jpg”);

但是最好创建一个可重用的函数,为我们循环遍历所有图像。创建一个函数,该函数将从文件图像名称 .createPathStrings()filename 创建路径字符串数组

function createPathStrings(filename) {
const basePath = “./static/skybox/”;
const baseFilename = basePath + filename;
const fileType = “.png”;
const sides = [“ft”, “bk”, “up”, “dn”, “rt”, “lf”];
const pathStings = sides.map(side => {
return baseFilename + “_” + side + fileType;
});

return pathStings;
}

这应该创建一个字符串数组来表示每个图像的路径:

[‘./static/skybox/purplenebula_ft.jpg’, ‘./static/skybox/purplenebula_bk.jpg’, …]

接下来,通过在上面的数组上映射来加载每个纹理。让我们创建另一个函数 ,以生成一个新的加载纹理数组。我们还将参数传入函数.TextureLoader().load()createMaterialArray()filenamecreatePathStrings

let skyboxImage = “purplenebula”;
function createMaterialArray(filename) {
const skyboxImagepaths = createPathStrings(filename);
const materialArray = skyboxImagepaths.map(image => {
let texture = new THREE.TextureLoader().load(image);

return texture;
});
return materialArray;
}

2.将每个Texture映射到一个Mesh数组 Three.js 方法将允许我们将上面的纹理映射到 Three.js 材质。我们可以简单地修改函数以返回 Three.js 材质而不是加载的纹理,而不是创建另一个函数来执行此操作。MeshBasicMaterial()createMaterialArray()

function createMaterialArray(filename) {
const skyboxImagepaths = createPathStrings(filename);
const materialArray = skyboxImagepaths.map(image => {
let texture = new THREE.TextureLoader().load(image);

return new THREE.MeshBasicMaterial({ map: texture, side: THREE.BackSide }); // <—
});
return materialArray;
}

3.在Skybox立方体中添加Mesh阵列 我们终于准备好将我们的网格数组添加到我们上面创建的立方体中。首先,使用基本文件名创建一个变量 。将该变量传递到 以生成我们的网格数组。最后,将该数组传递给函数的第二个参数。skyboxImagecreateMaterialArraynew Three.Mesh()

const skyboxImage = ‘purplenebula’;

function init() {

const materialArray = createMaterialArray(skyboxImage);
skyboxGeo = new THREE.BoxGeometry(10000, 10000, 10000);
skybox = new THREE.Mesh(skyboxGeo, materialArray);
scene.add(skybox);

animate();
}

我们的立方体现在应该有网格阵列

将相机放在立方体中 我们可以将 的位置从 更改为将相机放在立方体中。camera z 20000 2000

function init()

camera.position.set(1200, -250, 2000);

}

轨道控制 虽然上面的方法可以让我们进入立方体,但如果可以用鼠标控制相机并环顾四周会更好。 Three.js 的 Orbit Controls 包允许我们添加标签 import:script

<script src=”https://threejs.org/build/three.min.js”></script>
<script src=”https://threejs.org/examples/js/controls/OrbitControls.js”></script>

首先,在顶部添加另一个名为初始化的变量。然后在传入 and 时将该变量分配给方法。通过设置为启用控件。最后,设置 a 和 以便用户无法缩放到立方体之外。controls OrbitControls camera domElement controls.enabled true minDistance maxDistance

let scene, camera, renderer, skyboxGeo, skybox, controls;

function init() {

controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enabled = true;
controls.minDistance = 700;
controls.maxDistance = 1500;


animate();
}

接下来,去掉()函数中的旋转,添加animate()controls.update()();

function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}

您现在应该能够单击并拖动环境来查看您想要的任何部分。如果你想让环境再次旋转,就像你在空间旋转,你可以使用属性:autoRotate

function init() {

controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enabled = true;
controls.minDistance = 700;
controls.maxDistance = 1500;
controls.autoRotate = true;
controls.autoRotateSpeed = 1.0;

animate();
}

调整窗口大小 如果您在初始化后调整浏览器窗口的大小,画布将不会调整大小以适应新的窗口大小。要解决此问题,请创建一个函数以将和大小重新定义为 :camera.aspect renderer window 的高度和宽度

function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;

camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}

然后在事件上添加一个事件监听器并传递这个新函数。将此事件侦听器添加到 call.windowresizeinit() animate() 正上方的函数中

画布现在将随窗口调整大小。

结论 天空盒是一种快速创建 3d 环境幻觉的巧妙方法。虽然通常用于视频游戏,但您可能有一些创造性的方法可以在 Web 项目中实现它们。

0 0 投票数
文章评分
订阅评论
提醒
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x