Three 之 three.js (webgl)基础 第二个入门案例之汽车模型加载和简单模型展示
目录
Three 之 three.js (webgl)基础 第二个入门案例之汽车模型加载和简单模型展示
八、案例代码下载:第二个入门案例之汽车模型加载和简单模型展示
一、简单介绍
Three js 开发的一些知识整理,方便后期遇到类似的问题,能够及时查阅使用。
本节介绍, three.js (webgl)入门的第二个代码程序,实现一个汽车模型的加载,以及汽车模型的简单展示,简单的模型汽车行驶,并可以修改汽车模型的颜色等,如果有不足之处,欢迎指出,或者你有更好的方法,欢迎留言。
二、实现原理
1、场景构建三要素,scene、camera 和 renderer
2、模型加载器,这里使用的是 .glb 模型,所以使用的是GTLFLoader() 加载器,把汽车模型加载出来
3、材质 Material 的使用,这里通过材质赋给汽车模型,然后通过修改汽车的颜色,从而实现,汽车模型颜色的修改
4、汽车的模拟行驶,是通过,汽车车轮的旋转(汽车是原地旋转) 和 地面网线的移动实现
5、汽车多角度的观察,以及放大缩小,基本上都是通过 OrbitController 控制 Camera 位置远近灯变化实现的
三、GLTF加载器(GLTFLoader)
glTF(gl传输格式)是一种开放格式的规范 (open format specification), 用于更高效地传输、加载3D内容。该类文件以JSON(.glft)格式或二进制(.glb)格式提供, 外部文件存储贴图(.jpg、.png)和额外的二进制数据(.bin)。一个glTF组件可传输一个或多个场景, 包括网格、材质、贴图、蒙皮、骨架、变形目标、动画、灯光以及摄像机。
1、new GLTFLoader( manager : LoadingManager )
manager — 该加载器将要使用的 loadingManager 。默认为 THREE.DefaultLoadingManager。
创建一个新的GLTFLoader。
2、属性
共有属性请参见其基类Loader。
3、方法
共有方法请参见其基类Loader。
1).load ( url : String, onLoad : Function, onProgress : Function, onError : Function ) : undefined
url — 包含有.gltf/.glb文件路径/URL的字符串。
onLoad — 加载成功完成后将会被调用的函数。该函数接收parse所返回的已加载的JSON响应。
onProgress — (可选)加载正在进行过程中会被调用的函数。其参数将会是XMLHttpRequest实例,包含有总字节数.total与已加载的字节数.loaded。
onError — (可选)若在加载过程发生错误,将被调用的函数。该函数接收error来作为参数。开始从url加载,并使用解析过的响应内容调用回调函数。
2).setDRACOLoader ( dracoLoader : DRACOLoader ) : this
dracoLoader — THREE.DRACOLoader的实例,用于解码使用KHR_draco_mesh_compression扩展压缩过的文件。
请参阅readme来了解Draco及其解码器的详细信息。
3).parse ( data : ArrayBuffer, path : String, onLoad : Function, onError : Function ) : undefined
data — 需要解析的glTF文件,值为一个ArrayBuffer或JSON字符串。
path — 用于找到后续glTF资源(如纹理和.bin数据文件)的基础路径。
onLoad — 解析成功完成后将会被调用的函数。
onError — (可选)若在解析过程发生错误,将被调用的函数。该函数接收error来作为参数。解析基于glTF的ArrayBuffer或JSON字符串,并在完成后触发onLoad回调。onLoad的参数将是一个包含有已加载部分的Object:.scene、 .scenes、 .cameras、 .animations 和 .asset。
4、这里用到 DRACOLoader 做简单介绍
Draco是一个用于压缩和解压3D网格和点云的开源库。压缩的几何形状可以显著地更小,但要以客户端设备上额外的解码时间为代价。
独立的Draco文件有一个.drc扩展名,并包含顶点位置、法线、颜色和其他属性。Draco文件不包含材质,纹理,动画,或节点层次结构-要使用这些功能,嵌入Draco几何在glTF文件。一个普通的glTF文件可以使用glTF- pipeline转换为一个draco压缩的glTF文件。当Draco和glTF一起使用时,一个DRACOLoader的实例将被gltloader内部使用。
四、材质(Material)
材质的抽象基类。
材质描述了对象objects的外观。它们的定义方式与渲染器无关, 因此,如果您决定使用不同的渲染器,不必重写材质。
所有其他材质类型都继承了以下属性和方法(尽管它们可能具有不同的默认值)。
1、构造函数(Constructor) Material()
该方法创建一个通用材质。
2、属性(Properties)
1).alphaTest : Float
设置运行alphaTest时要使用的alpha值。如果不透明度低于此值,则不会渲染材质。默认值为0。
2).alphaToCoverage : Float
启用alpha to coverage. 只能在开启了MSAA的渲染环境中使用 (当渲染器创建的时候antialias 属性要true才能使用). 默认为 false.
3).blendDst : Integer
混合目标。默认值为OneMinusSrcAlphaFactor。 目标因子所有可能的取值请参阅constants。 必须将材质的blending设置为CustomBlending才能生效。
4).blendDstAlpha : Integer
.blendDst的透明度。 默认值为 null.
3、方法(Methods)
EventDispatcher 方法在此类中可用。
1).clone ( ) : Material
返回与此材质具有相同参数的新材质。
2).copy ( material : material ) : this
将被传入材质中的参数复制到此材质中。
3).dispose () : undefined
这里只做简单介绍,更多参见 :three.js docs
不同材质的效果,可参见博文:Three 之 three.js (webgl)涉及的各种材质简单说明(常用材质配有效果图)_
五、轨道控制器(OrbitControls)
Orbit controls(轨道控制器)可以使得相机围绕目标进行轨道运动。
要使用这一功能,就像在/examples(示例)目录中的所有文件一样, 您必须在HTML中包含这个文件。
1、OrbitControls( object : Camera, domElement : HTMLDOMElement )
object: (必须)将要被控制的相机。该相机不允许是其他任何对象的子级,除非该对象是场景自身。
domElement: 用于事件监听的HTML元素。2、Events
1)change
Fires when the camera has been transformed by the controls.
2)start
Fires when an interaction was initiated.
3)end
Fires when an interaction has finished.
3、属性
1).autoRotate : Boolean
将其设为true,以自动围绕目标旋转。
请注意,如果它被启用,你必须在你的动画循环里调用.update()。2).autoRotateSpeed : Float
当.autoRotate为true时,围绕目标旋转的速度将有多快,默认值为2.0,相当于在60fps时每旋转一周需要30秒。
请注意,如果.autoRotate被启用,你必须在你的动画循环里调用.update()。3).maxAzimuthAngle : Float
你能够水平旋转的角度上限。如果设置,其有效值范围为[-2 * Math.PI,2 * Math.PI],且旋转角度的上限和下限差值小于2 * Math.PI。默认值为无穷大。
4).maxDistance : Float
你能够将相机向外移动多少(仅适用于PerspectiveCamera),其默认值为Infinity。
5).maxPolarAngle : Float
你能够垂直旋转的角度的上限,范围是0到Math.PI,其默认值为Math.PI。
6).maxZoom : Float
你能够将相机缩小多少(仅适用于OrthographicCamera),其默认值为Infinity。
7).minAzimuthAngle : Float
你能够水平旋转的角度下限。如果设置,其有效值范围为[-2 * Math.PI,2 * Math.PI],且旋转角度的上限和下限差值小于2 * Math.PI。默认值为无穷大。
8).minDistance : Float
你能够将相机向内移动多少(仅适用于PerspectiveCamera),其默认值为0。
9).minPolarAngle : Float
你能够垂直旋转的角度的下限,范围是0到Math.PI,其默认值为0。
10.minZoom : Float
你能够将相机放大多少(仅适用于OrthographicCamera),其默认值为0。
4、方法
1).dispose () : undefined
移除所有的事件监听。
2).getAzimuthalAngle () : radians
获得当前的水平旋转,单位为弧度。
3).getPolarAngle () : radians
获得当前的垂直旋转,单位为弧度。
这里只做简单介绍,更多参见 :three.js docs
六、注意事项
1、这个案例并没有使用光,却有很好的光照效果,完全是是因为 scene.environment ,加载的 hdr 文件(后面有hdr 相关说明),以及 renderer.toneMappingExposure 配合使用来合理的调节光照的渲染
// 这是关键,包含了光照信息的环境 hdr 图,(因为没有添加灯光,少了他,场景就是黑色的)
scene.environment = new RGBELoader().load( './src/venice_sunset_1k.hdr' );
scene.environment.mapping = THREE.EquirectangularReflectionMapping;
// renderer.toneMappingExposure 可以调整光照渲染强度
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.85;
HDR 说明:
HDR的全称是High Dynamic Range,即高动态范围,在此,我们先解释一下什么是Dynamic Range(动态范围),动态范围是指图像中所包含的从“最亮”至“最暗”的比值,也就是图像从“最亮”到“最暗”之间灰度划分的等级数;动态范围越大,所能表示的层次越丰富,所包含的色彩空间也越广。那高动态范围(HDR)顾名思义就是从“最亮”到“最暗”可以达到非常高的比值。
HDR文件的像素值可以覆盖现实世界中存在的整个色调范围。HDR图像能够显示尽可能大的像素值范围。 因此,可以通过栩栩如生的色彩描绘来捕捉直射光和阳光以及极端阴影。HDR图像可以通过以特定方式合成不同的照片、使用特殊的图像传感器或通过计算机渲染来创建。
七、 第二个入门案例 之 汽车模型
1、引入 Three js 相关 js 文件
<script type="importmap">
{
"imports": {
"three": "./js/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from './js/OrbitControls.js';
import { GLTFLoader } from './js/GLTFLoader.js';
import { DRACOLoader } from './js/DRACOLoader.js';
import { RGBELoader } from './js/RGBELoader.js';
2、renderer、camera 和 scene 创建
// 渲染器
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
// 该函数将在每一帧被调用。如果传入' null ',它将停止任何正在进行的动画。
renderer.setAnimationLoop( render );
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.85;
container.appendChild( renderer.domElement );
// camera
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.set( 4.25, 1.4, - 4.5 );
// 场景,背景色
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x333333 );
// 这是关键,包含了光照信息的环境 hdr 图,(因为没有添加灯光,少了他,场景就是黑色的)
scene.environment = new RGBELoader().load( './src/venice_sunset_1k.hdr' );
scene.environment.mapping = THREE.EquirectangularReflectionMapping;
scene.fog = new THREE.Fog( 0x333333, 10, 15 );
3、OrbitControls 视角操控器,控制 camera 可以变化角度位置观察车子
// 视角操控器,控制 camera 可以变化角度位置观察车子
controls = new OrbitControls( camera, container );
controls.enableDamping = true;
controls.maxDistance = 9;
controls.target.set( 0, 0.5, 0 );
controls.update();
4、Material 材质创建、和汽车模型加载
// 材质,控制车子的各部分的颜色显示
const bodyMaterial = new THREE.MeshPhysicalMaterial( {
color: 0xffff00, metalness: 1.0, roughness: 0.5, clearcoat: 1.0, clearcoatRoughness: 0.03, sheen: 0.5
} );
const detailsMaterial = new THREE.MeshStandardMaterial( {
color: 0xffffff, metalness: 1.0, roughness: 0.5
} );
const glassMaterial = new THREE.MeshPhysicalMaterial( {
color: 0xffffff, metalness: 0.25, roughness: 0, transmission: 1.0
} );
// 汽车加载,这里使用到了 DRACOLoader 和 GLTFLoader
const shadow = new THREE.TextureLoader().load( './src/ferrari_ao.png' );
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'js/gltf/' );
const loader = new GLTFLoader();
loader.setDRACOLoader( dracoLoader );
loader.load( './src/ferrari.glb', function ( gltf ) {
const carModel = gltf.scene.children[ 0 ];
// 根据名字获取 3D 物体,并设置 材质
carModel.getObjectByName( 'body' ).material = bodyMaterial;
carModel.getObjectByName( 'rim_fl' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_fr' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_rr' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_rl' ).material = detailsMaterial;
carModel.getObjectByName( 'trim' ).material = detailsMaterial;
carModel.getObjectByName( 'glass' ).material = glassMaterial;
// 获取车子轮子
wheels.push(
carModel.getObjectByName( 'wheel_fl' ),
carModel.getObjectByName( 'wheel_fr' ),
carModel.getObjectByName( 'wheel_rl' ),
carModel.getObjectByName( 'wheel_rr' )
);
5、地面网格、和汽车阴影的创建
// 地面网格线
grid = new THREE.GridHelper( 20, 40, 0xffffff, 0xffffff );
grid.material.opacity = 0.2;
grid.material.depthWrite = false;
grid.material.transparent = true;
scene.add( grid );
// 添加车子底部的一个简单阴影
const mesh = new THREE.Mesh(
new THREE.PlaneGeometry( 0.655 * 4, 1.3 * 4 ),
new THREE.MeshBasicMaterial( {
map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true
} )
);
mesh.rotation.x = - Math.PI / 2;
mesh.renderOrder = 2;
carModel.add( mesh );
6、汽车的行驶模拟
// 车轮模拟滚动
const time = - performance.now() / 1000;
for ( let i = 0; i < wheels.length; i ++ ) {
wheels[ i ].rotation.x = time * Math.PI * 2;
}
// 网格移动 与 车轮模拟滚动 ,从而制造车子向前移动的效果
grid.position.z = - ( time ) % 1;
7、汽车模型颜色调整的几个 UI 按钮事件
// 车身颜色 按钮事件
const bodyColorInput = document.getElementById( 'body-color' );
bodyColorInput.addEventListener( 'input', function () {
bodyMaterial.color.set( this.value );
} );
// 座椅轮毂细节颜色 按钮事件
const detailsColorInput = document.getElementById( 'details-color' );
detailsColorInput.addEventListener( 'input', function () {
detailsMaterial.color.set( this.value );
} );
// 玻璃颜色 按钮事件
const glassColorInput = document.getElementById( 'glass-color' );
glassColorInput.addEventListener( 'input', function () {
glassMaterial.color.set( this.value );
} );
9、效果预览
10、关键代码(代码带有注释解释)
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>CarShow</title>
<style>
body {
color: #bbbbbb;
background: #333333;
}
a {
color: #08f;
}
.colorPicker {
display: inline-block;
margin: 0 10px
}
</style>
</head>
<body>
<div id="app" style="text-align: center">
<br><br>
<span class="colorPicker"><input id="body-color" type="color" value="#ff0000"/><br/>车身颜色</span>
<span class="colorPicker"><input id="details-color" type="color" value="#ffffff"/><br/>座椅轮毂细节颜色</span>
<span class="colorPicker"><input id="glass-color" type="color" value="#ffffff"/><br/>玻璃颜色</span>
</div>
<div id="container"></div>
<script type="importmap">
{
"imports": {
"three": "./js/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from './js/OrbitControls.js';
import { GLTFLoader } from './js/GLTFLoader.js';
import { DRACOLoader } from './js/DRACOLoader.js';
import { RGBELoader } from './js/RGBELoader.js';
let camera, scene, renderer;
let grid;
let controls;
const wheels = [];
function init() {
const container = document.getElementById( 'container' );
// 渲染器
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
// 该函数将在每一帧被调用。如果传入' null ',它将停止任何正在进行的动画。
renderer.setAnimationLoop( render );
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.85;
container.appendChild( renderer.domElement );
// camera
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.set( 4.25, 1.4, - 4.5 );
// 场景,背景色
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x333333 );
// 这是关键,包含了光照信息的环境 hdr 图,(因为没有添加灯光,少了他,场景就是黑色的)
scene.environment = new RGBELoader().load( './src/venice_sunset_1k.hdr' );
scene.environment.mapping = THREE.EquirectangularReflectionMapping;
scene.fog = new THREE.Fog( 0x333333, 10, 15 );
// 监控窗口变化
window.addEventListener( 'resize', onWindowResize );
// 视角操控器,控制 camera 可以变化角度位置观察车子
controls = new OrbitControls( camera, container );
controls.enableDamping = true;
controls.maxDistance = 9;
controls.target.set( 0, 0.5, 0 );
controls.update();
// 地面网格线
grid = new THREE.GridHelper( 20, 40, 0xffffff, 0xffffff );
grid.material.opacity = 0.2;
grid.material.depthWrite = false;
grid.material.transparent = true;
scene.add( grid );
// 材质,控制车子的各部分的颜色显示
const bodyMaterial = new THREE.MeshPhysicalMaterial( {
color: 0xffff00, metalness: 1.0, roughness: 0.5, clearcoat: 1.0, clearcoatRoughness: 0.03, sheen: 0.5
} );
const detailsMaterial = new THREE.MeshStandardMaterial( {
color: 0xffffff, metalness: 1.0, roughness: 0.5
} );
const glassMaterial = new THREE.MeshPhysicalMaterial( {
color: 0xffffff, metalness: 0.25, roughness: 0, transmission: 1.0
} );
// 车身颜色 按钮事件
const bodyColorInput = document.getElementById( 'body-color' );
bodyColorInput.addEventListener( 'input', function () {
bodyMaterial.color.set( this.value );
} );
// 座椅轮毂细节颜色 按钮事件
const detailsColorInput = document.getElementById( 'details-color' );
detailsColorInput.addEventListener( 'input', function () {
detailsMaterial.color.set( this.value );
} );
// 玻璃颜色 按钮事件
const glassColorInput = document.getElementById( 'glass-color' );
glassColorInput.addEventListener( 'input', function () {
glassMaterial.color.set( this.value );
} );
// 汽车加载,这里使用到了 DRACOLoader 和 GLTFLoader
const shadow = new THREE.TextureLoader().load( './src/ferrari_ao.png' );
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'js/gltf/' );
const loader = new GLTFLoader();
loader.setDRACOLoader( dracoLoader );
loader.load( './src/ferrari.glb', function ( gltf ) {
const carModel = gltf.scene.children[ 0 ];
// 根据名字获取 3D 物体,并设置 材质
carModel.getObjectByName( 'body' ).material = bodyMaterial;
carModel.getObjectByName( 'rim_fl' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_fr' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_rr' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_rl' ).material = detailsMaterial;
carModel.getObjectByName( 'trim' ).material = detailsMaterial;
carModel.getObjectByName( 'glass' ).material = glassMaterial;
// 获取车子轮子
wheels.push(
carModel.getObjectByName( 'wheel_fl' ),
carModel.getObjectByName( 'wheel_fr' ),
carModel.getObjectByName( 'wheel_rl' ),
carModel.getObjectByName( 'wheel_rr' )
);
// 添加车子底部的一个简单阴影
const mesh = new THREE.Mesh(
new THREE.PlaneGeometry( 0.655 * 4, 1.3 * 4 ),
new THREE.MeshBasicMaterial( {
map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true
} )
);
mesh.rotation.x = - Math.PI / 2;
mesh.renderOrder = 2;
carModel.add( mesh );
scene.add( carModel );
} );
}
function onWindowResize() {
// 窗口变化时,相机更新
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
// 渲染大小的更新
renderer.setSize( window.innerWidth, window.innerHeight );
}
function render() {
// 操控器更新
controls.update();
// 车轮模拟滚动
const time = - performance.now() / 1000;
for ( let i = 0; i < wheels.length; i ++ ) {
wheels[ i ].rotation.x = time * Math.PI * 2;
}
// 网格移动 与 车轮模拟滚动 ,从而制造车子向前移动的效果
grid.position.z = - ( time ) % 1;
renderer.render( scene, camera );
}
init();
</script>
</body>
</html>
八、案例代码下载:第二个入门案例之汽车模型加载和简单模型展示
csdn 下载 :Three之three.js(webgl)基础第二个入门案例之汽车模型加载和简单模型展示CarShow-Javascript文档类资源-CSDN下载