【ThreeJS基础教程-高级几何体篇】2.2 加载模型,加载模型时的常见错误

学习ThreeJS的捷径

本段内容会写在0篇以外所有的,本人所编写的Threejs教程中

对,学习ThreeJS有捷径
当你有哪个函数不懂的时候,第一时间去翻一翻文档
当你有哪个效果不会做的时候,第一时间去翻一翻所有的案例,也许就能找到你想要的效果
最重要的一点,就是,绝对不要怕问问题,越怕找找别人问题,你的问题就会被拖的越久

如果你确定要走WebGL/ThreeJS的开发者路线的话,以下行为可以让你更快的学习ThreeJS

  1. 没事就把所有的文档翻一遍,哪怕看不懂,也要留个印象,至少要知道Threejs有什么
  2. 没事多看看案例效果,当你记忆的案例效果足够多时,下次再遇到相似问题时,你就有可能第一时间来找对应的案例,能更快解决你自己的问题
  3. 上述案例不只是官网的案例,郭隆邦技术博客,跃焱邵隼,暮志未晚等站点均有不少优质案例,记得一并收藏
    http://www.yanhuangxueyuan.com/ 郭隆邦技术博客
    https://www.wellyyss.cn/ 跃焱邵隼
    http://www.wjceo.com/ 暮志未晚
    这三个站点是我最常逛的站点,推荐各位有事没事逛一下,看看他们的案例和写法思路,绝对没坏处

关于加载模型篇的一些建议

本篇中将主要介绍如何加载模型,会使用到模型界中最通用的模型格式OBJ格式,但是OBJ格式并非我们实际开发中使用的理想格式,在下一篇中,我们会着重讲解GL格式的加载与获取方式,本篇的重点在于加载模型以及加载模型后的接下来一系列操作

加载OBJ格式模型

案例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        canvas{
            display: block;
        }
        body {
            margin: 0;
            overscroll-behavior: none;
        }
        #btns{
            position: absolute;
            top:10%;
            width: 500px;
            height: 100px;
            left: 50%;
            transform:translateX(-50%);
        }
    </style>
</head>
<body>
<div id="btns"></div>

<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>

<script type="importmap">
			{
				"imports": {
					"three": "../../three.js-master/build/three.module.js"
				}
			}
		</script>
<script type="module">
    import * as THREE from '../../three.js-master/build/three.module.js';
    import {OrbitControls} from "../../three.js-master/examples/jsm/controls/OrbitControls.js";
    import {OBJLoader} from "../../three.js-master/examples/jsm/loaders/OBJLoader.js";

    let scene,renderer,camera,orbitControls;

    function init(){
        scene = new THREE.Scene();
        renderer = new THREE.WebGLRenderer({
            antialias:true
        });
        renderer.setSize(window.innerWidth,window.innerHeight);
        document.body.appendChild(renderer.domElement);
        camera = new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,1,1000);
        camera.position.set(10,10,10);
        orbitControls = new OrbitControls(camera,renderer.domElement);

        let helper = new THREE.AxesHelper(5);
        scene.add(helper);

        let light = new THREE.PointLight();
        camera.add(light);
        scene.add(camera);
    }
    function addMesh(){

        let loader = new OBJLoader();

        loader.load("./tree.obj",(obj)=>{
            console.log(obj);
            scene.add(obj);
        },null,(error)=>{
            console.log("加载模型时出错,错误原因:"+error);
        });
    }
    function render(){
        renderer.render(scene,camera);
        requestAnimationFrame(render);
    }
    init();
    addMesh();
    render();
</script>
</body>
</html>

案例效果
在这里插入图片描述

这里我们使用的文件在threejs开发包中:three.js-master\examples\models\obj\tree.obj

案例分析

引入我们的加载器,加载器一般都需要外部引入,如果是使用< script>引入,需要从以下地址引入
three.js-master/examples/js/loaders/OBJLoader.js

2023年1月6日追加:ThreeJS148版本不再支持< script src> 方式引入组件了,如有jquery开发者在学习本篇教程,请选用148以前的版本

	import {OBJLoader} from "../../three.js-master/examples/jsm/loaders/OBJLoader.js";

接下来是声明我们的Loader

	let loader = new OBJLoader();

如果你使用< script >引入则需要加上THREE前缀

    //threejs148版本之后不再支持使用<script src="">方式引入组件
	let loader = new THREE.OBJLoader();

构造器:new OBJLoader( manager );
manager:加载管理器,这个在后面的教程中会着重讲解,这里我们不需要填充它

接下来进入我们的加载过程,加载过程为异步,一定要搞清楚,这里是异步加载

	    loader.load("./tree.obj",(obj)=>{
	    	//这里的代码在模型加载完成后才执行
            console.log(obj);
            scene.add(obj);
        },null,(error)=>{
            console.log("加载模型时出错,错误原因:"+error);
        });
        //这里的代码可能在模型加载完之前就执行,所以如果这里的代码涉及到模型处理

如果你对异步函数使用比较熟悉的话,你还可以使用异步函数来让代码更具有逻辑性,本段代码仅供初期学习时参考使用

	//注意,Loader这个对象仅建议在全局上创建一个
	let loader = new OBJLoader();

	 function loadModel(path){
        return new Promise((resolve)=>{
            loader.load(path,(obj)=>{
                resolve(obj)
            })
        })
    }

    async function addMesh(){
        let model = await loadModel("./tree.obj");
        console.log(model);
        scene.add(model);
    }

还有一件事,养成好习惯,加载模型后console.log()一下你的模型

在这里插入图片描述

这里我们拿到的模型,是一个THREE.Group()对象,从文档得知,THREE.Group()是继承了Object3D的子类,所以我们可以直接将它添加到scene中
注意:不同的Loader加载完成后,交给我们的返回值,不一定都是一个THREE.Group对象,常见的obj格式一般是一个Group对象,STL是一个 Geometry,gl格式通常是一组数据等

模型加载时的常见错误

模型文件数据错误

这类错误一般可以在load()的第四个参数,onError这个函数中输出,根据函数的输出内容去判断问题在哪即可

路径错误

加载模型一般是使用字符串来表示路径,最常见的错误就是路径写错,相对路径和绝对路径错误等等

没有报错的情况下,我们看不到模型怎么办

3.1 首先是上面的养成好习惯,拿obj格式举例,obj格式加载出来是一个Group,那么我们可以展开它的Children属性,以及其下的children,先确认这里是否有Mesh存在,空的Group无论你怎么加载都不会加载任何东西的在这里插入图片描述

经过上述检查后,模型数据正常,我们看不到怎么办

前面讲到了OrbitControls【ThreeJS基础教程】1.1. 使用Helper让我们的开发变的更流畅,我们使用这个的视角旋转,缩放功能,来尽可能的扩大或者缩小范围去寻找我们的模型

在进行上述操作时,应尽可能大的增加相机的远端面和尽可能小的缩小近端面

	camera = new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,0.1,50000);

笔者这里使用的近端面为0.1,远端面为5万,如果还是找不到还能再扩大

还有一种方式,就是查看Mesh下的Attribute
展开模型下的任意Mesh,然后依次展开Geometry,attributes,position,array,查看里面的具体数字,如果里面的数字在小数点后好几位,那么我们需要对模型进行缩放操作后,更容易找到模型
在这里插入图片描述
上图为官方的模型的数据,这里的数据就属于偏小,此时,我们修改相机与模型的距离也可以,对模型进行缩放也可以,两种方式都可以增加你渲染出来的模型大小

	//把相机放到距离模型足够近的位置
	//这里需要多次调试才能找到比较完美的位置
	camera.position.set(1,1,1);
	//scale是一个Vector3对象,每一个Object3D对象均有一个scale属性
	//将模型的x,y,z三个轴向均扩大10倍
	model.scale.set(10,10,10);

如果你想整体等比缩放

	model.setScalar(10);

根据你的实际情况去修改大小与相机位置即可

笔者这里不建议过多修改缩放,缩放值也受相对坐标影响,处理不好很容易造成某个模型过大或者过小的情况,模型大小应由建模师统一处理好后,交给程序,程序后续不应对scale做过多处理

函数:Vector3.set(x,y,z) 设置这个三维向量的值

函数Vector3.setScalar(scalar) 设置这个三维向量为同一个值

做了上述操作我们依然看不到模型

  1. 确认一下你的代码中是否添加了环境光源,如果没有环境光源,且你的背景是黑色的,那么模型实际上已经显示了,只是因为融入了背景所以你看不到

  2. 使用threejsEditor来查看模型是否可以被成功加载,如果在editor中都无法加载出来的话,那么该模型本身就是一个问题模型 threejs官方编辑器,在【ThreeJS基础教程】0.在学习使用ThreeJS之前中我们已经介绍了如何把editor在本地运行,如果直接打开官网速度太慢,我们可以使用本地版
    还有一件事
    在这里插入图片描述
    官方亲切的提供了中文翻译版,也不用太担心这个editor难上手什么的

  3. 一定要有一个参照物,在【ThreeJS基础教程】1.1. 使用Helper让我们的开发变的更流畅中已经对常用的参照物做了介绍,参照物能帮助你更快的找到你的模型

一些碎碎念

很多新人在刚开始入坑threejs时,只知道怎么去加载模型,但是从来没有关注过模型标准这个东西,当没有模型标准时,我们的建模师又不是专业级的游戏建模师的话,我们就很容易遇到上面提到的那些错误,所以我们在使用模型前,最好是能和甲方,建模师他们确立一套模型标准,以最大程度的把这些繁冗的问题在建模软件中就处理好再使用会比较好

笔者在工作中合作的建模师,有很多都是那种出图的建模师,他们不会管你模型大了之后对程序有多少影响,他们会无脑的添加更多的素材进去,增加更多的点线面,然后让电脑花几个小时渲染一张牛逼的渲染成品图,这些建模师一般最终的成品都是一张图片,而非模型,这种模型用到程序中,出的问题多到能把一个新手完全劝退

关于建模师合作问题,笔者在后面模型的最优选的文章中会更多着墨

常见的建模验收标准

该标准为笔者3年来的经验之谈,仅供参考,详细的标准可以参考游戏建模标准

  1. 模型一定要居中
  2. 模型命名绝对不要中文,包括模型内所有零件组件的名字,懂js的应该明白,中文可能会导致编码错误问题,进而导致显示错误以及处理起来的各种不必要的麻烦。。。什么?汉语拼音?汉语拼音还能勉强接受吧,但是请打死所有用汉语拼音首字母命名的人,不论是开发还是建模,即使是汉语拼音也尽量保持驼峰命名法,以用来区分文字,比如说xian,你知道这是线还是西安呢?正确的标准应该为XiAn或Xian来做区分
    在这里插入图片描述
    命名这方面一定要严谨

在threejs中,官方提供了一条能检索模型的方法

	object.getObjectByName( name:String ) 

这个就是通过名称来检索模型内物体的,所以我们开发时怎么用,那么在跟建模师交流时,也应该严格要求对方把命名标准化

  1. 模型分组化处理
    比如说环境啥的,完全不需要程序处理的模型,丢进一个组
    有特定需求的模型,丢进一个组,比如说红绿灯,我们想要拿到指定街道的红绿灯模型,那么,可以要求建模师将这条道路上所有的元素都分为一个组,然后通过上面的函数检索组名,再检索红绿灯相关的名称就可以快速访问到这个红绿灯,
    如果你的场景可交互元素比较少的时候,你也可以要求建模师完全不要分组,命名规范即可
	//假设我要求建模师把红绿灯都丢在了组里,并且进行了标准化命名
	// TrafficLight
	//    - RoadATrafficLight
	//    - RoadBTrafficLight
	let group = scene.getObjectByName('TrafficLight');
	
	let roadATrafficLight = group.getObjectByName('RoadATrafficLight');
	//这样就可以在众多的模型零件中,快速检索到我们需要控制的红绿灯
  1. 模型文件大小应尽可能的小,我们的项目是用于线上端的,如果你使用个几百M的模型,用户可能访问一次就要下载个十几秒甚至以分钟计时,谁愿意等这么长的时间呢?在后续我会着重抽一篇文章详细讲解模型的最优选择
  2. 模型精度尽可能的低,我们的项目是运行在浏览器上的,也就意味着,任何用户都有可能打开我们的项目,比如说一些低价手机和古董手机(5年以上的),这些设备有时候很难带起来我们的项目。
    不仅要尽可能的降低精度,同时也在开发前也尽可能的跟甲方问清楚,我们是否需要更好的特效,或者兼容更低配的设备等等一些列问题,根据这些来确定模型的最大最小精细度等
  3. 贴图精度尽可能的低,不建议过多使用前面地球案例中4096 * 4096这种巨大的贴图,尽量让贴图贴到一张大图上,或者贴图尽量零碎一些,保持总文件大小最小化

下一篇预告:多模型加载管理器

下一篇主要讲解loadingManager

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值