在第一章
《Three.js初体验》
中,大致介绍了在网页中渲染三维场景的简要流程。距离上一章已经过去了两周,如果你充满好奇心,并且有足够的执行力的话,相信你已经能读懂简单的JavaScript和HTML了,因此,这一章会在这个假设上做继续介绍。
既然是搭建场景,我们就来做一个大家都熟悉,但又具有挑战性的场景—城市。城市是一个复杂的系统,要解决一个复杂的问题,首先要把它进行抽象,将主要特征表达出来,然后次要特征也就可有可无了。
城市中有各种建筑物,并且有区域地标集中的特性,我们可以将这个“简要城市特征”进行几何化—在一个圆形区域内,随机放置方形柱,距离圆形区域圆心越近的方柱高度越高。听起来蛮复杂的,其实效果很简单:
这效果图指引我打开 [外卖广告位招租] ,然后点了一份 [薯条广告位招租],囧
也许你已经知道接下来我们要做什么了:
使用Three.js创建城市的边界实现随机生成建筑算法总结经验与方法
一、使用Three.js创建城市的边界
城市的边界,我们假定为圆形,所以我们可以创建一个高度很小的圆柱来作为城市的地面。但是首先,在创建物体前,我们需要创建场景和相机:
var scene, camera, renderer;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight);
camera.position.set(3,4,5);
camera.lookAt(new THREE.Vector3(0,0,0));
scene.add(camera);
上面这段代码,我们创建了一个场景并在场景中添加了一个相机,接下来,我们开始向场景中添加圆形区域,我们设置这个圆形区域的半径为3:
var regionR = 3;
var cylinderMesh = new THREE.CylinderGeometry(regionR, regionR, 0.05, 1024);
var material = new THREE.MeshNormalMaterial();
var cylinder = new THREE.Mesh( cylinderMesh, material );
cylinder.position.set(0,0,0);
scene.add( cylinder );
上面这段代码在场景中添加了一个圆盘,其实是高度极小的圆柱,如果你急切想看到效果的话,添加下面一段代码,就可以看到圆盘了:
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer.render(scene, camera);
这时候在浏览器中运行一下,可以看到如下效果:
在这里需要注意一下,在创建渲染器的时候,增加了{ antialias: true },这表示渲染时将具有抗锯齿效果,如果你运行的时候出现了任何问题,可以在
这里
检查代码哪里出了问题。
二、实现随机生成建筑算法
随机放置“薯条”似乎有点麻烦,我们如何保证”薯条”放在圆形区域内,并且能够控制它们的高度呢?
这时候需要引入极坐标系的知识,我们最常用的坐标系是笛卡尔坐标系(直角坐标系),在平面中确定(x, y)就能确定一个点的位置,给定x、y的值确定的区间值,就能确保这个点位于一个方形或矩形区域内。在极坐标系中,确定点到圆心的距离r,和一个角度θ就能确定这个点的位置,给定r一个区间值,就能确保这个点位于圆形或环形区域内。
我再画一张图,让你更容易理解一些:
有了这些知识,我们在一个圆形区域内生成随机点就很容易:
function pointsGenerator(amount) {
var polarPointsArr = [];
for (var i = 0; i < amount; i ++) {
var r = Math.random() * regionR;
var θ = (2 * Math.PI / 360) * (Math.random() * 360);
polarPointsArr.push({r: r, θ: θ});
}
return polarPointsArr;
}
var points = pointsGenerator(100);
我们定义了pointsGenerator函数来为我们生成极坐标随机点,并在最后使用这个函数获得了100个随机点,但是Three.js中的坐标系是笛卡尔坐标系,所以我们需要将这些极坐标点转换为笛卡尔坐标点,这个转换非常简单,只需把pointsGenerator改造一下:
function pointsGenerator(amount) {
var polarPointsArr = [];
for (var i = 0; i < amount; i ++) {
var r = Math.random() * regionR;
var θ = (2 * Math.PI / 360) * (Math.random() * 360);
polarPointsArr.push({r: r, θ: θ});
}
var cartesianPointsArr = [];
for (var j = 0; j < polarPointsArr.length; j ++) {
var r = polarPointsArr[j].r;
var θ = polarPointsArr[j].θ;
var x = r * Math.cos(θ);
var y = r * Math.sin(θ);
cartesianPointsArr.push({x: x, y: y});
}
return {polarPointsArr: polarPointsArr, cartesianPointsArr: cartesianPointsArr};
}
在改造的pointsGenerator中,我们也同时返回了极坐标点,因为我们在后面有可能会用到。
有了这些随机点,我们就可以在100次循环里创建方形柱,然后将它们放到这些随机生成的坐标上!我们仍然可以写一个函数,来实现刚才的想法:
function addRadomCubes(amount) {
var cubesArr = [];
var randomPosition = pointsGenerator(amount);
var polarPointsArr = randomPosition.polarPointsArr;
var cartesianPointsArr = randomPosition.cartesianPointsArr;
for (var i = 0; i < cartesianPointsArr.length; i ++) {
var position = cartesianPointsArr[i];
var r = polarPointsArr[i].r;
var cubeMesh = new THREE.BoxGeometry(0.2, regionR – r, 0.2);
var cube = new THREE.Mesh(cubeMesh, material);
cube.position.set(position.x, (regionR – r) / 2 + 0.025, position.y);
scene.add(cube);
cubesArr.push(cube);
}
return {cubesArr: cubesArr, randomPosition: randomPosition};
}
可以注意到,代码中,我用 (regionR – r) 作为“薯条”的高度。使用addRadomCubes函数,我们可以轻松地在场景中添加任意数量的随机“薯条”了!
var cubesArrInfo = addRadomCubes(100);
让我们看一下
效果
:
三、总结经验与方法
上面的“抽象城市”看起来还不错,我们已经实现了文章一开始抽象出来的“简要城市特征”。
在Three.js中创建场景如此简单,仅仅需要“创建”和“置入”这两步操作,场景的空间效果,完全取决于,你创建的是什么物体和物体置入的位置。在你想要搭建一个场景前,可以先花一些时间来思考,是否能够做一个有效的规划,然后其余的工作,就是填充和丰富场景的空间了。
我们的“抽象城市”还有很多可能性,可以添加更多规则,你可以自己想一些规则来丰富它。看看我做了什么,你是否能够实现呢?
上一章:《Three.js初体验》
下一章:《Three.js相机漫谈》
暂无评论内容