用Threejs搭建一个Web3D汽车展厅!

在网页里360度展示它家新款汽车的3d模型,还要可以让用户DIY汽车部件的颜色。

先看最终效果

3D引擎的基本知识

本文的目标是让大家看完之后可以立刻上手用起来,既然要用3d引擎,那我们理解了一些3d的基本知识后,再看threejs的API文档效率就会很高。无论什么3d引擎,都不外乎由以下几种基本元素构成

场景(scene

一个容器,容纳着除渲染器以外的三维世界里的一切。场景的元素采用右手笛卡尔坐标系,x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外

图片

image.png

摄像机(camera

就像人的眼睛,在一个空间里可以看向任意方向,可以通过参数调节可视角度和可视距离。

一般我们使用符合物理世界近大远小真实情况的透视相机PerspectiveCamera,还有一些特殊情况,需要远近大小是一样的,那就要用正交相机OrthographicCamera

PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
//构造函数参数
//fov:视场角
//aspect:视场宽高比(一般用 画布宽/画布 高即可)
//near:能看多近
//far:能看多远
//这几个参数决定了哪些scene里的三维顶点会被渲染/绘制出来

图片

image.png

渲染器(renderer

camerascene里看到的内容渲染/绘制到画布上

几何体(geometry

3D世界里的所有物体都是点组成面面组成几何体。相信大家对以下标准的几何体比较熟悉

  • 球体

  • 立方体

  • 圆锥体

  • 圆柱体

  • ...

是由点构成的,又可以组成各式各样的几何体。以球体举例,球体面上的点越多,球就越圆。但点越多,运算量也会越大...

图片

image.png

另外我们一般说的3d模型就是一个或多个几何体,只是有的3d模型文件里除了包含几何体还可以包含一些额外的信息,比如贴图,材质等...需要在读取模型文件时解析出来

灯光(light

3d引擎在没有手动创建光的情况下会默认有个环境光,不然你什么都看不到。常见的灯光有以下几种类型

  • AmbientLight(环境光,没有方向全局打亮,不会产生明暗)

  • DirectionLight(平行光,参考日光来理解)

    图片

  • PointLight(点光源,参考灯泡来理解)

图片

image.png

  • SpotLight(聚光灯,参考舞台聚光灯)

    图片

贴图(texture

想象一下你手里有一个立方体,你用一张A4纸包裹上立方体的所有面,并在上面画画。你画的内容就是贴图

图片

image.png

有一些类型的贴图会和光照发生反应...后面我们用到的时候再说

材质(material

延续贴图里的想象,你用白卡纸画画,还是用油纸画画,呈现出来的质感是不同的对不对,这就是材质!下面五个球的颜色都是一样的,而材质从左至右分别是

  • MeshBasicMaterial(基础材质,不受光照影响)

  • MeshStandardMaterial(PBR标准材质)

  • MeshPhongMaterial(高光材质,适用于陶瓷,烤漆类质感)

  • MeshToonMaterial(卡通材质,俗称三渲二)

  • MeshStandardMaterial(PBR标准材质模拟金属反射)

图片

image.png

来实战吧!

有了这些基础知识,再来使用threejs就很容易上手了。可以说在3dmax等软件中调出来的90%的效果,用threejs都能找到对应的配置参数。

搭建基础场景

//<div id='container' style="width:100%;height: 100%;"></div>
var scene, camera, renderer;

function init(){
    scene = new THREE.Scene();
    //这里参数不懂的同学回去看基本知识里的camera部分
    camera = new THREE.PerspectiveCamera(90, document.body.clientWidth / document.body.clientHeight, 0.1, 100);
    //camera的位置在x0,y0,z3,还记得迪尔卡右手坐标系吗?
    camera.position.set(0, 0, 3);
    
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(document.body.clientWidth, document.body.clientHeight);
    document.getElementById("container").appendChild(renderer.domElement);
    
    var controls = new THREE.OrbitControls(camera, renderer.domElement);

    //等待添加模型

    loop();
}

function loop() {
    requestAnimationFrame(loop);
    renderer.render(scene, camera);
}

window.onload = init;

现在我们可以先添加一个标准几何体来试试看,比如我们添加一个立方体来试试看

const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );

图片

2021-06-28 14_14_45.gif

很显然,场景是生效的...大家注意看源码块中的注释

汽车模型

回到咱们的项目上来,品牌方给的是一个非常精细的模型,文件量有好几百兆,数百万面(triangles)。

图片

Snipaste_2021-07-01_11-02-49.jpg

我说这可用不了,你得减面还得给我转成引擎能支持的格式gltfobj

根据我的评估,要想在移动端网页里流畅运行,最多不能超过10万面

外包工头老杨说,你也别让客户给你弄了,他们都不会

我知道你懂,你就给弄了算了,我给你加【5K】

加5K你让我怎么好意思拒绝呢...

图片

image.png

然后,我花25美刀巨资在sketchfab上购买了一个模型

再稍微改改就能满足要求,当然sketchfab也有免费模型

但毕竟收了老杨5K,不花点钱我心里略感不安呐 :p

优化模型结构

图片

image.png

根据实际的需求,比如车窗要透明可以看到内饰,所以车窗就得单独给有透明属性的材质。车轮,灯罩,车网,车架,车身等等都要拆成独立的几何体才能独立配置材质

图片

image.png

梳理好模型结构后,我们就要准备模型文件了

加载模型

3d模型的文件格式有很多,但threejs里常用的基本是

  • OBJ格式

老牌通用3d模型文件,不包含贴图,材质,动画等信息。

  • GLTF格式(图形语言传输格式)

由OpenGL官方维护团队推出的现代3d模型通用格式,可以包含几何体、材质、动画及场景、摄影机等信息,并且文件量还小。有3D模型界的JPEG之称。

原项目中我使用的是OBJ格式,本文里我们使用GLTF格式。利用threejs提供的editor,我们可以将模型的格式进行转换并导出。

图片

image.png

通过GLTFLoader,我们可以加载一个.gltf格式的3d模型文件。需要注意的是,这些Loader都以插件的形式存在,需要引入相应的XXXLoader.js才能使用

//<script src="js/GLTFLoader.js"></script>
//放到之前添加立方体的代码处
const loader = new THREE.GLTFLoader();

loader.load(
    'images/model.gltf',
    function ( gltf ) {
        scene.add( gltf.scene );
    },
    function ( xhr ) {
        //侦听模型加载进度
        console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
    },
    function ( error ) {
        //加载出错时的回调
        console.log( 'An error happened' );
    }
);

图片

2021-07-01 14_22_13.gif

通过这个代码可以遍历查看模型里的几何体列表

console.log(gltf.scene.children);

//可以用for,也可以用traverse api
//gltf.scene.children.traverse((child){});

贴图和材质

现在我们来给几何体添加贴图,贴图怎么做是设计师的专业。这里不过多的说,我们只需要知道,这些贴图如何使用即可。

图片

image.png

  • 普通贴图(_col

material.map,替代颜色

  • 法线贴图(_nor

material.normalMap,让细节程度较低的表面生成高细节程度的精确光照方向和反射效果

  • 环境光遮蔽贴图(_occ

material.aoMap,用来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果

  • 环境反射贴图

material.envMap,用于模拟材质反射周围环境的效果

我们现在先把这些贴图文件统一加载到内存里

var allTexture;
function loadAllTexture(cb){
    allTexture = {};

    var loadIndex = 0;
    var textures = [
        "skymap",
        "shache_occ",
        "shache_nor",
        "shache_col",
        "neishi_occ",
        "neishi_nor",
        "mennei_col",
        "luntai_nor",
        "luntai_col",
        "lungu_occ",
        "lungu_nor",
        "lungu_col",
        "linjian_occ",
        "linjian_nor",
        "linjian_col",
        "floor",
        "deng_occ",
        "deng_nor",
        "deng_col",
        "cheshen_occ",
        "cheshen_nor",
        "chejia_occ",
        "chejia_nor",
        "chedengzhao_nor"
    ];

    function loadNextTexture(){
        var textureName = textures[loadIndex];
        loadTexture("images/textures/"+textureName+".jpg",function(texture){
            if(loadIndex<textures.length-1){
                allTexture[textureName] = {
                    texture:texture
                };

                loadIndex++;
                loadNextTexture();
            }else{
                if(cb)cb();
            }
        });
    }
    loadNextTexture();
}
function loadTexture(filepath,cb){
    const textureLoader = new THREE.TextureLoader();
    textureLoader.load(filepath,cb);
}

然后根据名称手动一一对应,比如我们先把车轮毂的贴图给加上

图片

image.png

for(var i=0;i<gltf.scene.children[0].children.length;i++){
    var modelObj = gltf.scene.children[0].children[i];

    if(modelObj.name=="smart_lungu0"||modelObj.name=="smart_lungu1"||modelObj.name=="smart_lungu2"||modelObj.name=="smart_lungu3"){
        modelObj.material = new THREE.MeshStandardMaterial();
        modelObj.material.map = allTexture["lungu_col"].texture;
        modelObj.material.normalMap = allTexture["lungu_nor"].texture;
        modelObj.material.aoMap = allTexture["lungu_occ"].texture;
    }
}

我们继续把车轮的贴图给加上

图片

image.png

else if(modelObj.name=="smart_chelun0"||modelObj.name=="smart_chelun1"||modelObj.name=="smart_chelun2"||modelObj.name=="smart_chelun3"){
    modelObj.material = new THREE.MeshStandardMaterial();
    modelObj.material.map = allTexture["luntai_col"].texture;
    modelObj.material.normalMap = allTexture["luntai_nor"].texture;
}

其余的材质贴图都如此添加上,后续当然还有很多材质的细节是可以去调整的,但这是个细活儿,这里主要重点分享下玻璃的反射和透明金属漆的反光

  • 透明的玻璃

天窗和前挡风玻璃的透明度以及基底颜色是不同的

else if(child.name=="smart_boli"){
    child.material=new THREE.MeshPhongMaterial();
    child.material.color = new THREE.Color( 0x333333 );
    child.material.transparent=true;
    child.material.opacity=.2;
}else if(child.name=="smart_tianchuang"){
    child.material=new THREE.MeshPhongMaterial();
    child.material.color = new THREE.Color( 0x000 );
    child.material.transparent=true;
    child.material.opacity=.5;
}

仔细看看动图里前挡风和天窗透明度的差异

图片

2021-07-02 09_50_58.gif

  • 玻璃的反射

想真的去反射真实的环境?你别想多了,用envMap做个假的看起来就很可以了...

child.material.envMap=allTexture["skymap"].texture;
//环境反射贴图envMap的映射方式,这里用的是一个叫等量矩形投影的映射方法
child.material.envMap.mapping = THREE.EquirectangularReflectionMapping;
//环境反射贴图的强度
child.material.envMapIntensity=1;

图片

2021-07-02 10_02_08.gif

仔细看动图里的前挡风玻璃,是不是反射了什么东西?看过《三种前端实现VR全景看房的方案!说不定哪天就用得上!》的小伙伴们,记得这张图么?

图片

  • 车身漆面质感

使用MeshStandardMaterial材质,通过调节metalnessroughness的值来调节金属的质感

child.material = new THREE.MeshStandardMaterial();
                    
child.material.color=new THREE.Color(0x70631B);
child.material.metalness = 0.44;
child.material.roughness = 0;

图片

image.png

信息点

毕竟是个在线展厅,在车身周围得呈现一些信息点,点击后可以弹窗显示更多信息对吧。实现方式同样在VR全景的文章中提到过了,就是Sprite+Raycast

图片

2021-07-02 11_01_36.gif

//frame只是一个标记,叫什么都行
var poiPosArray=[
    {x:-1.47,y:0.87,z:-0.36,frame:1},
    {x:-1.46,y:0.49,z:-0.69,frame:2},
    {x:1.5,y:.7,z:0,frame:8},
    {x:0.33,y:1.79,z:0,frame:3},
    {x:0,y:0.23,z:0.96,frame:4},
    {x:0.73,y:1.38,z:-0.8,frame:5},
    {x:-.1,y:1.17,z:0.88,frame:6},
    {x:-1.16,y:0.16,z:0.89,frame:7}
],poiObjects=[];
function setupInfoPoint(){
    const pointTexture = new THREE.TextureLoader().load("images/point.png");

    var group = new THREE.Group();
    var materialC = new THREE.SpriteMaterial( { map: pointTexture, color: 0xffffff, fog: false } );
    for ( var a = 0; a < poiPosArray.length; a ++ ) {
        var x = poiPosArray[a].x;
        var y = poiPosArray[a].y-.5;
        var z = poiPosArray[a].z;

        var sprite = new THREE.Sprite( materialC );
        sprite.scale.set( .15, .15, 1 );
        sprite.position.set( x, y, z );
        sprite.idstr="popup_"+poiPosArray[a].frame;
        group.add( sprite );

        poiObjects.push(sprite);
    }
    scene.add( group );

    document.body.addEventListener("click",function (event) {
        event.preventDefault();

        var raycaster = new THREE.Raycaster();
        var mouse = new THREE.Vector2();
        mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
        mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

        raycaster.setFromCamera( mouse, camera );

        var intersects = raycaster.intersectObjects( poiObjects );
        if(intersects.length>0){
            var popIndex=parseInt(intersects[ 0 ].object.idstr.substr(6,1));
            console.log(popIndex);
        }
    });
}

UI怎么做

图片

image.png

既然我们用了threejs,所以我们就要在threejs里把UI做出来吗?这么想的话,会把自己累死。要知道在3d场景里做2d的UI可不算是一件容易的事,还要实现UI的一些用户行为(点击,拖动等)的话就更麻烦了...所以我们直接用html来做UI就好啦~

图片

2021-07-02 11_20_56.gif

到这里,这个3D汽车展厅的核心部分

  • 42
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: threejs版本的web3d汽车展厅在线网页3d交互展示源码是一种基于WebGL技术的3D互动展示系统,可以进行汽车展销、预览、互动和购买等多种功能。它使用threejs的类库来实现复杂的3D图像和动画效果,以及高性能的渲染和交互体验。 该源码包含了汽车展厅全景展示、选车、配置、试驾、调色、预约等功能,用户可以根据自己的喜好自由选择汽车颜色、内饰材质、轮圈、音响系统等配置,并进行实时变更和试驾。 此外,该系统还支持跨平台、多终端适配,可在电脑、手机、平板等各种设备上进行流畅、逼真的3D互动展示。同时,源码中还提供了详细的API文档和技术支持,方便开发者进行二次开发和自定义调整。 总之,threejs版本的web3d汽车展厅在线网页3d交互展示源码可以为汽车展销企业和广大消费者提供一个数字化、个性化、智能化的汽车购买和预览平台,为汽车行业的数字化转型和升级带来了更多的机会和可能性。 ### 回答2: threejs版本的web3d汽车展厅在线网页3d交互展示源码是一种用于构建3D虚拟展示座舱的工具,允许在半实时和全实时的情况下,以高达数百万个三角形为基础进行渲染。它是一种基于JavaScript的WebGL库,专门设计用来制作3D内容和展示,并且能够在所有的现代Web浏览器中使用。 通过threejs版本的web3d汽车展厅在线网页3d交互展示源码,您能够轻松地将汽车漆面、轮毂、方向盘、座椅等细节以3D形式呈现,接近真实交互的场景,让用户在网页上感受到3D空间带来的全新展览体验。同时,该源码还允许您灵活地添加分组、动画和互动效果,以便更好地呈现汽车的功能和特性。 该源码的使用对设计师、开发人员和汽车制造商的需求提供了新的途径,可以利用该平台提供丰富的3D场景展示及互动体验,使产品更加立体真实,并增强用户的购车体验和认知,同时也能大大提高在线营销的效率和精度。 总之,threejs版本的web3d汽车展厅在线网页3d交互展示源码是一种设计精良、易于使用且功能强大的3D展示平台,能够帮助汽车制造商呈现出更具立体感和生动动态的汽车展览,为用户提供全新的购车体验。 ### 回答3: three.js是一个基于JavaScript的3D库,它可以让开发者在网页中创建复杂的3D场景,从而提高Web3D应用的交互性和视觉效果。在这个基础上,有人开发了一个Web3D汽车展厅在线网页3D交互展示源码,可以在网页上展示汽车的外观和内部结构,让用户可以自行逛一遍展厅实现了优秀的用户交互体验。 这个Web3D汽车展厅源码的核心思想是通过three.js实现汽车3D模型的加载和展示,再通过JavaScript实现用户的交互事件。用户可以通过鼠标拖动、缩放等动作调整模型的位置和方向,实现模型浏览。同时,源码还提供了丰富的汽车展示数据,包括车型、车轮配置、内饰等信息,用户可以根据自己的需求进行定位查看。 除此之外,源码还使用了一些WebGL技术来提高性能,如适当的渲染优化、纹理贴图、阴影渲染等处理,提高了Web3D应用的体验效果。总的来说,Web3D汽车展厅在线网页3D交互展示源码不仅在技术上具备很高的水平,同时也提供了相当好的用户体验,对于汽车品牌展示、销售和宣传都有很大的作用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_35430208

您的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值