超简单的Three.js 卡车跑跑demo

6,987 阅读2分钟

废话开篇:对于three.js的了解处于入门还未见门阶段,光看three.js www.webgl3d.cn/threejs/exa… 官网不如试着写下简单的demo。可以从官网里把全部的demo文件下载下来,然后根据自身的开发业务逻辑去分析对应的 demo 文件。

屏幕录制2021-10-13 下午3.01.16.gif

步骤一、如何利用从官网下载下来的examples

这里需要说明一下,three.js 加载3D模型需要开启服务,所以,将下载好的全部的文件放在了 ‘/Library/WebServer/Documents/’ 路径下(这里是Mac电脑),然后启动一下apache 服务,在终端 执行 sudo apachectl start ,这样,本地的服务就开启成功了。

这里看到本地服务已经开启了。

image.png

那么,怎么利用这些demo进行开发?比如,demo里面的卡车是obj格式的,那么,就需要在例子文件里找到如何加载obj格式的three.js的api。

找到 example 文件夹。 image.png

找到 webgl_loader_obj_mtl.html 文件

obj是3D模型文件格式,mtl是纹理渲染文件格式,二者是配套的。

image.png

好了,打开它,然后根据本地的网页例子展示来剖析里面的three.js api,这样是不是更容易上手一些?

步骤二、初始化相机、场景、渲染器

引入所需的js文件

//three.js
import * as THREE from '../build/three.module.js';

//下面的是不同模型类型加载功能模块
import { DDSLoader } from './jsm/loaders/DDSLoader.js';
import { MTLLoader } from './jsm/loaders/MTLLoader.js';
import { OBJLoader } from './jsm/loaders/OBJLoader.js';
import { TGALoader } from './jsm/loaders/TGALoader.js';

初始化相机、场景、渲染器

let camera, scene, renderer;

const container = document.createElement( 'div' );
document.body.appendChild( container );
//相机
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
// scene
scene = new THREE.Scene();
//renderer
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );

定时刷新方法

function animate() {
   requestAnimationFrame( animate );
      render();
}

步骤三、代码展示如何加载天空盒、公路、卡车

天空盒

天空盒就是建立一个正方体,然后进行纹理反面渲染,再将相机放在盒子里面,这个时候再给盒子加上沿z轴转动就形成了蓝天转换的动效。

//天空
let flieSize = 5;
const path = "textures/cube/skyboxsun25deg/";'
const format = '.jpg';
const urls = [
  path + 'px' + format,//右面
  path + 'nx' + format,//左面
  path + 'pz' + format,//后面 
  path + 'nz' + format,//前面 
  path + 'ny' + format,//地面
  path + 'py' + format,//顶面
];
				
const textureCube = new THREE.CubeTextureLoader().load( urls );
	var length = flieSize * 500;
	var materials = [];
	for (var i = 0; i < urls.length; ++i) {
	 materials.push(new THREE.MeshBasicMaterial({
	   map: THREE.ImageUtils.loadTexture(urls[i],
			 {}, function() {
				 renderer.render(scene, camera);
				        }),
				    overdraw: true
				    }));
				}
var geometry = new THREE.BoxBufferGeometry( length, length, length );
var material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
var cube = new THREE.Mesh( geometry, materials );
//纹理反面渲染
cube.geometry.scale( 1, 1, -1 );
//全局变量保存天空正方体
sky = cube;

天空盒旋转

function render() {
  //卡车移动
  if(truck != null && sky != null && camera != null){
     let speed = 1;
     truck.position.y += speed;
     camera.position.y += speed;
     sky.position.y += speed;
    }
  //天空旋转
  if(sky != null){
     sky.rotation.z += - Math.PI / 10500;
  }
  renderer.render( scene, camera );
}

公路

//加载图片纹理
const highwayTexture = new THREE.TextureLoader().load( 'textures/highway.jpg' );
//设置平铺重复
highwayTexture.wrapS = THREE.RepeatWrapping;
highwayTexture.wrapT = THREE.RepeatWrapping;
//设置重复次数(这里x轴重复1次,y轴重复500次)
highwayTexture.repeat.set(1, 500);

//创建盒子模型
const highwayGeometry = new THREE.BoxGeometry( 60, 150000, 1 );
const highwayMaterial = new THREE.MeshBasicMaterial( { map: highwayTexture } );
const highway = new THREE.Mesh( highwayGeometry, highwayMaterial );
highway.position.y = -100;
scene.add( highway );

卡车

卡车这里就需要加载obj格式的文件了,纹理需要加载mtl格式的文件。

new MTLLoader( manager )
  .setPath( 'models/obj/scania_obj/' )
  .load( 'scania.mtl', function ( materials ) {
    materials.preload();
    new OBJLoader( manager )
    .setMaterials( materials )
    .setPath( 'models/obj/scania_obj/' )
    .load( 'scania.obj', function ( object ) {
    //位移
    object.position.y = 90;
    object.position.z = 10;
    object.position.x = 12;
   
   //旋转						
   object.rotation.z = - Math.PI;
   //按比例缩小			
   object.scale.set(0.05,0.05,0.05);
   //全局保存truck对象
   truck = object;
   scene.add( object );
   }, onProgress, onError );
} );

MTLLoader 对象加载mtl格式的纹理,加载完成后进行obj格式模型的加载,最后的回调函数返回一个“精灵对象”。

当然,这里不想用mtl格式的纹理还可以先加载obj格式模型单独设置纹理。

//object obj格式模型对象
object.traverse( function ( child ) {
	if ( child.isMesh ) child.material.map = textureRabbit;
} );

加载 obj格式模型回调函数里面有两个“方法参数.

1、加载进度 2、加载出错

// model 加载模型进度回调
const onProgress = function ( xhr ) {
      if ( xhr.lengthComputable ) {
      const percentComplete = xhr.loaded / xhr.total * 100;
      //console.log( Math.round( percentComplete, 2 ) + '% downloaded');
      }
};
// model 加载模型出错回调
const onError = function () { };

卡车移动动画也是放在了屏幕渲染回调方法里面

function render() {
 //卡车移动 
 if(truck != null && sky != null && camera != null){
    let speed = 1;
    truck.position.y += speed;
    camera.position.y += speed;
    sky.position.y += speed;
   }
  //天空旋转
  if(sky != null){
	sky.rotation.z += - Math.PI / 10500;
  }
  renderer.render( scene, camera );
}

步骤四、设置相机旋,查看路况

屏幕录制2021-10-13 下午4.39.41.gif

其实就是监听键盘 ← ↑ → ↓,直接上代码


document.onkeydown = function whichButton(event)
{
  //console.log(event.keyCode)
  if(event.keyCode == 37){
    //左
    camera.rotateY(0.009);
   } else if(event.keyCode == 38){
    //上
    camera.rotateX(0.005);
   } else if(event.keyCode == 39){
    //右
    camera.rotateY(-0.009);
   } else if(event.keyCode == 40){
    //下
    camera.rotateX(-0.005);
   }
 }

好了,极其简单的 卡车跑跑 demo 就完成了,代码拙劣,大神无笑。