VEX-IQ 3D 模型解决方案

本贴最后更新于 1674 天前,其中的信息可能已经时移俗易

一、前言

近期有个小程序 +pc 的项目,需要将客户的 VEX-IQ 模型做成 3D 效果
经过一番研究,最终采用 Threejs 实现成功,记录一下...

二、准备工具

2.1 SnapCAD

客户编辑 VEX-IQ 模型所用工具,生成的文件为.ldr
主要用来安装后获取里面的零件库
下载地址:SnapCAD

2.2 Blender 2.80

Blender 用于将.ldr 转换成 Three 目前主流支持的格式为 gftl/glb
下载地址:Blender

2.3 ImportLDRaw

Blender 默认不支持.ldr 导入,ImportLDRaw 为 Blender 识别.ldr 文件插件。
此插件包含两个部分,一个为导入插件,使 Blender 支持 LDRaw 导入。
另一个为零件库,主要负责导入进来的模型颜色和图案显示
操作说明:https://github.com/TobyLobster/ImportLDraw
插件 zip:https://github.com/TobyLobster/ImportLDraw/releases
零件库:https://ldraw.org/parts/latest-parts.html

2.4 ThreeJs
一个非常好用的实现浏览器端 3D 效果的工具,用于渲染 Blender 导出后的模型文件
下载地址:https://threejs.org/

三、安装工具和插件

3.1 安装 SnapCAD 和 Blender

将下载好的安装文件安装,直接下一步到成功,非常简单。

3.2 安装 ImportLDRaw 插件

打开 Blender
1.png

默认是不支持导入.ldr 格式文件的
2.png

选择菜单 Edit – Preferences 进入设置画面
5.png

选择 Add-on -- Install.. 进入添加插件设置画面
3.png

选择下载好的 ImportLDRaw 插件 ZIP 包,双击或者点击 Install Add-on … 按钮
4.png

勾选 ImportLDRaw,勾选后才会生效
6.png

插件验证,选择 File -- Import
已经支持导入.ldr 文件
7.png

3.2 设置零件库

若不设置该步骤,.ldr 无法在 Blender 中正常显示

前往 https://ldraw.org/parts/latest-parts.html 下载完整零件库包,下载后随便解压到一个目录下。

解压完成后,将 SnapCAD 下的 p 和 parts 文件夹复制到零件库 ldraw 文件夹下,重复文件选择跳过,该操作用于.ldr 颜色渲染,不执行会导致.ldr 在 Blender 中没有颜色
8.png

在 Blender 中选择导入,类型选择 LDRaw,在左下角 Import Options 中 LDRaw path 填入刚刚解压的零件库地址
9.png

导入成功后效果如下
10.png

四、导出模型且渲染(FBX)

4.1 导出模型

删除灯光 Light 和地平面 LegoGroundPlane(建议)

因为如果灯光位置不好,会导致整个模型太亮而发白,而导入时模型可能会在地平面之下,需要将模型手动移到地平面上面,否则导出的模型会是空白的。所以建议删除这两项(个人操作下来的结果,也可能是我不会用)

右上角选择 Light 右键菜单选择 Delete 删除灯光,LegoGroundPlane 也是同样操作
11.png

导出 fbx,选择 File – Export – FBX
threeJS 主要建议使用 gftl 或者 glb 格式,但是本次项目.ldr 文件即使所有零件库都已加载,导出 gftl 或者 glb 还是会纯白色。而 fbx 则是正常显示的。
12.png

选择好保存路径和文件名,点击 Export FBX 按钮,等待 Blender 返回到模型展示页面即可
13.png

查看导出结果,若是 win10,可以用自带的 3D 查看器查看效果
14.png

15.png

五、渲染模型

参照 ThreeJS 官方文档 https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene

实例代码结构
image.png

index.html

<html lang="en">
	<head>
		<title>FBX-3D模型观看</title>
		<meta charset="utf-8">
	</head>
	<body>
		<script type="application/javascript">
			//定义模型路径
			var fbxUrl = 'model/1.fbx'     
		</script>
		<script src="js/three.min.js?v=1.0.0"></script>
		<script src="js/inflate.min.js?v=1.0.0"></script>
		<script src="js/OrbitControls.js?v=1.0.0"></script>
		<script src="js/FBXLoader.js?v=1.0.0"></script>
		<script src="js/stats.min.js?v=1.0.0"></script>
		<script src="js/3dfbx.js?v=1.0.0"></script>
	</body>
</html>

3dfbx.js

//3dfbx.js

var container, stats, controls;
var camera, scene, renderer, light, bbox;
var rotating = true;
 
init();
animate();
  
function init() {
    if (!fbxUrl) {
        return false;
    }
    container = document.createElement( 'div' );
    //创建div,并加载到html里,这里的document.body可以换成你想让模型加载的地方。
    document.body.appendChild( container );    
    //创建场景
    scene = new THREE.Scene();
    //3D盒子
    bbox = new THREE.Box3();
    //场景背景颜色
    scene.background = new THREE.Color( 0xeeeeee );
    //半球光
    light = new THREE.HemisphereLight( 0xbbbbff, 0x444422, 1.5 );
    light.position.set( 0, 1, 0 );
    scene.add( light );
    //fbx加载器
    var loader = new THREE.FBXLoader();
    loader.load( fbxUrl, function ( obj ) {
        this.setContent(obj);
        scene.add( obj );
    }, undefined, function ( e ) {
        console.error( e );
    } );
    //创建webgl渲染器
    renderer = new THREE.WebGLRenderer( { antialias: true } );
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.gammaOutput = true;
    container.appendChild( renderer.domElement );
    window.addEventListener( 'resize', onWindowResize, false );
    camera = new THREE.PerspectiveCamera(45,window.innerWidth / window.innerHeight,	0.5,10000);
 
    controls = new THREE.OrbitControls(camera);
    // to disable pan
    controls.enablePan = false;
    // to disable zoom
    controls.enableZoom = false;
    controls.target.set(0,0,0);
    controls.update();
}
 
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
    requestAnimationFrame( animate );
    if (rotating) {
        scene.rotation.y += -0.005;
    } else {
        scene.rotation.y = scene.rotation.y;
    }
 
    renderer.render( scene, camera );
}
 

function setContent(object) {
 
    object.updateMatrixWorld();
    const box = new THREE.Box3().setFromObject(object);
    const size = box.getSize(new THREE.Vector3()).length();
    const boxSize = box.getSize();
    const center = box.getCenter(new THREE.Vector3());
 
    object.position.x += object.position.x - center.x;
    object.position.y += object.position.y - center.y;
    object.position.z += object.position.z - center.z;
 
    this.camera.position.copy(center);
    if (boxSize.x > boxSize.y) {
        this.camera.position.z = boxSize.x * -2.85
    } else {
        this.camera.position.z = boxSize.y * -2.85
    }
    this.camera.lookAt(0, 0, 0);
}

浏览器最终效果:

2.gif

  • WebGL
    4 引用 • 5 回帖
  • three.js
    3 引用 • 2 回帖
  • Blender
    2 引用
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    710 引用 • 1173 回帖 • 173 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...