第八章 手把手教你使用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.这是一个非常大的立方体了,我们把它添加到场景中。由于默认的立方体在外侧具有质地,我们设置flipSidedtrue。在我们的例子中,我们将在立方体的内部。

//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 官方文档

results matching ""

    No results matching ""