第八章 手把手教你使用ThreeJS 构建3D汽车
构建天空
做为练手项目,我们一起构建一个场景,一辆在星空下大草坪上运行的汽车。这个项目是由tQuery的开发者Jerome在一个系列博客中使用的。
我们从模板构建工具入手,现在我们添加天空。最简单的添加天空的方法是在一个大的立方体上添加一张图片。这里的技巧在于,我们会把这个世界中的其它物品添加在立方体的其它地方。我们用如下代码加载多张图片到大立方体中:
//add skymap
//load sky images
var urls = [
"images/sky1.png",
"images/sky1.png",
"images/sky1.png",
"images/sky1.png",
"images/sky1.png",
"images/sky1.png",
];
var textureCube = THREE.ImageUtils.loadTextureCube(urls);
sky1.png
图片可以在示例代码中找到。
接下来,我们需要使用立方体着色器基于标准的格式绘制大立方体。请注意,我们设置tCube
质地到我们的质地中。
//setup the cube shader
var shader = THREE.ShaderUtils.lib["cube"];
var uniforms = THREE.UniformsUtils.clone(shader.uniforms);
uniforms['tCube'].texture = textureCube;
var material = new THREE.ShaderMaterial({
fragmentShader : shader.fragmentShader,
vertexShader : shader.vertexShader,
uniforms : uniforms
});
现在我们需要一个大立方体形状,设置大小为10000.这是一个非常大的立方体了,我们把它添加到场景中。由于默认的立方体在外侧具有质地,我们设置flipSided
为true
。在我们的例子中,我们将在立方体的内部。
//create a skybox
var size = 10000;
skyboxMesh = new THREE.Mesh(
new THREE.CubeGeometry(size,size,size),material);
//IMPORTANT!! draw on the inside instead of outside
skyboxMesh.flipSided = true; // you must have this or you won't see anything
scene.add(skyboxMesh);
接下来,我们添加太阳光,没有光,我们什么也看不见。
//create a skybox
var size = 10000;
skyboxMesh = new THREE.Mesh(
new THREE.CubeGeometry(size,size,size),material);
//IMPORTANT!! draw on the inside instead of outside
skyboxMesh.flipSided = true; // you must have this or you won't see anything
scene.add(skyboxMesh);
我们现在得到的效果如下:
添加平地
接下来,我们添加大地。我们首先先把绿地图片添加为质地。让它在x轴和y轴方向重复。重复值的大小需要和图片的大小一致,and usually should be a power of two (ex: 256)
var grassTex = THREE.ImageUtils.loadTexture('images/grass.png');
grassTex.wrapS = THREE.RepeatWrapping;
grassTex.wrapT = THREE.RepeatWrapping;
grassTex.repeat.x = 256;
grassTex.repeat.y = 256;
var groundMat = new THREE.MeshBasicMaterial({map:grassTex});
接下来就是要绘制几何形状了,这其实是空间中的一个大平台。将其大小设置为400*400,这和相机大小比起来非常大,但是和天空大小比起来却又相当小(10000).
var groundGeo = new THREE.PlaneGeometry(400,400);
接下来 我们把他们合并为一个mesh
,设置position.y
为-1.9.使其低于圆弧形,设置position.x
为90°,使得地面是水平的(平台默认是 垂直的)。如果你看不见效果,把doubleSided
设置为true
试试。平台默认只会绘制一面。
var ground = new THREE.Mesh(groundGeo,groundMat);
ground.position.y = -1.9; //lower it
ground.rotation.x = -Math.PI/2; //-90 degrees around the xaxis
//IMPORTANT, draw on both sides
ground.doubleSided = true;
scene.add(ground);
现在我们得到的效果如下:
添加汽车模型
我们需要加载额外的模型用以替换弧形为汽车,在本例中我们使用Troyano 创建的Bugatti Veyron 。可以从这里下载源代码.由于模型是二进制格式而不是JSON格式,我们需要使用THREE.BinaryLoader
加载它。
//load a car
//IMPORTANT: be sure to use ./ or it may not load the .bin correctly
new THREE.BinaryLoader().load('./VeyronNoUv_bin.js', function(geometry) {
var orange = new THREE.MeshLambertMaterial( { color: 0x995500, opacity: 1.0, transparent: false } );
var mesh = new THREE.Mesh( geometry, orange );
mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.05;
scene.add( mesh );
car = mesh;
});
注意这里的材质设置为了MeshLambertMaterial
而非我们之前使用的MeshNormalMaterial
。这将给汽车更好的基于光线的固态颜色(本例中是橘色)。这个mesh
和甜甜圈比起来大很多,我们需要把它缩小至5%,探后添加到场景中。
至此,效果如下:
键盘控制
一个孤零零的汽车停在这里没啥意思,我们让它动起来。cameraControl
对象用于移动相机。我们先去除它,然后在cametaControl
对象初始化时添加一个KeyboardState
对象。要使用此对象,需要先在文件中引入THREEx.KeyboardState.js
<script src="vendor/threex/THREEx.KeyboardState.js"></script>
// create a camera contol
//cameraControls = new THREEx.DragPanControls(camera)
keyboard = new THREEx.KeyboardState();
然后,修改render()
函数。keyboard
对象允许我们查询当前的键盘状态。为了让小车运动起来,使用下述代码用键盘代替cameraControls.update()
// update camera controls
//cameraControls.update();
if(keyboard.pressed("left")) {
car.rotation.y += 0.1;
}
if(keyboard.pressed("right")) {
car.rotation.y -= 0.1;
}
if(keyboard.pressed("up")) {
car.position.z -= 1.0;
}
if(keyboard.pressed("down")) {
car.position.z += 1.0;
}
现在我们的汽车可以通过键盘移动了,不过目前看起来并不真实,小车还是会飘。为了修复这个,我们需要用一个向量表征汽车当前的运动方向。添加一个角度变量并改变代码如下:
if(keyboard.pressed("left")) {
car.rotation.y += 0.1;
angle += 0.1;
}
if(keyboard.pressed("right")) {
car.rotation.y -= 0.1;
angle -= 0.1;
}
if(keyboard.pressed("up")) {
car.position.z -= Math.sin(-angle);
car.position.x -= Math.cos(-angle);
}
if(keyboard.pressed("down")) {
car.position.z += Math.sin(-angle);
car.position.x += Math.cos(-angle);
}
下一步
手把手的教学到此为止了,如果你想继续完善本例,以下是一些建议
- 使得相机跟随小车;
- 让小车闪烁起来,就如这个例子参考的原始例子那样;
- 让小车在到达事件边缘时停止;
- 把前一章学习的点状效果添加到这里的场景中。 你可以在这里看到最终的效果
更多的学习可以参看ThreeJS 官方文档