Three 之 three.js (webgl)基础 第二个入门案例之汽车模型加载和简单模型展示



Three 之 three.js (webgl)基础 第二个入门案例之汽车模型加载和简单模型展示

目录

​Three 之 three.js (webgl)基础 第二个入门案例之汽车模型加载和简单模型展示

一、简单介绍

二、实现原理

三、GLTF加载器(GLTFLoader)

四、材质(Material)

五、轨道控制器(OrbitControls)

六、注意事项

 七、 第二个入门案例 之 汽车模型

八、案例代码下载:第二个入门案例之汽车模型加载和简单模型展示


一、简单介绍

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下载

猜你喜欢

转载自blog.csdn.net/u014361280/article/details/124522330