Threejs - 陆冲板定制平台~

3,275 阅读9分钟

前言

🏄️陆地冲浪板,简称陆冲板,可以在地面上模拟海浪的效果,站上滑板,即可一直滑行了!

001.gif

前期调研

前期是收集资料阶段,看看有哪些品牌,哪些可以定制的地方。主要搜索渠道知乎、淘宝、B站等。从知乎上了解品牌、陆冲板结构、实用人群等,然后去淘宝看实际商品,B站看看教程!

收集资料

陆冲板的主要品牌有...... 嗯,这里选的是国产品牌 - 牛匠 来作为参考对象!逛一逛淘宝~

image.png

从图中我们可以看到

  • 陆冲板有不同的板型
  • 板型有不同尺寸之分
  • 板面的贴图不同
  • 轮子颜色也有不同
  • 和其他品牌相比,在材料、做工、支架等方面也有区别。

这里的定制主要考虑的是板型、板面和轮子颜色✔️

数据收集

模型制作的前期是数据收集阶段。

收集的信息有:

  • 陆冲板的不同板型 - 来自淘宝,选取了五款板型🥚
  • 陆冲板的不同板面 - 来自百度,选取了合适的图片风格🖼️
  • 陆冲板轮子的颜色 - 来自颜色表网站,从中选取颜色~🎨

陆冲板图片

  • image.png
  • 来自淘宝,下载的图片

支架图片参考

  • image.png
  • 来自知乎和百度搜索的图片

板面图片来源

图片处理

PS使用网站

图片处理操作主要是陆冲板的板面素材,因为它们可能不是一样大的,这样直接贴到模型上,会有问题!用PS软件,将这些图片处理成统一大小(尺寸)💯修改图片的名字,方便程序点击切换图片!

image.png

我们共收集了五种板型,为了方便操作和选取,用PS的钢笔工具绘制出板型的形状,也是方便切换板型,这里共绘制了五张png透明图片,程序实现时,将图片顺时针旋转45°✏️,倾斜角度,更有运动效果~

image.png

模型制作

模型建立

进入正题!模型制作用的是Blender,先前收集的版式图,先导入到Blender中,然后将他们放大到同一大小,这样制作处理的模型,不会差距太大。

导入图片🖼️

image.png

框选出来的,就是要导入的图片,可以看到它们的板头、板尾、外形等有所不同,这就是我们要实现的切换效果。

image.png

调整比例,这一步,保证了制作模型的大小,不会相差太大,对比标准是图片中滑板的宽度,先保证宽度一致!有了参考图,之后就是依据参考图进行模型制作,因为模型是立体的,还需要其他角度的参考图一起参考!

image.png

建立模型,模型主要由三部分组成,板面、支架和轮子,其中支架和轮子可以重复使用,只需要建立板面(5个)的模型即可。建立平面,依据参考图的样子创建板面,整体完成效果如下。

image.png

看一下细节部分,为什么要分为两个部分?因为我们切换的部分有板面和板型,切换板面会直接将材质替换,侧边的材质是木纹,不需要替换。同样,切换板型的时候,侧边也需要一起变化,所以分为两个部分。这个在程序中很好实现,一起显示即可!

image.png

来一张爆炸图💣💥,主要结构部分~

image.png

模型贴图

模型贴图,主要是在模型中查看实际效果(大小、位置等效果),我们导出的时候,并不需要导出贴图(在程序中实现,初始化加载一张图片即可),重点是查看板面的贴图效果!

关于板面贴图,我们最主要的任务就是确定模型的UV位置!因为我们切换贴图的时候,贴图的位置、缩放比例就是UV(2维坐标系)决定的!最后导出模型的时候也需要这个坐标系!

什么是UV贴图?

看看板型4的UV

image.png

板型5的UV

image.png

这里的UV展开方式是从视角展开,从顶视图的角度(俯视)。我们使用一张图来测试模型贴图效果,再根据不同板面模型,调整不同的位置,切换其他图片(先前我们处理过了,把所有图片都设置为同样大小),可以实现同样的效果,就算完成板面切换的功能了!

其他的材质,就好整了,调一下粗糙度、颜色即可!

  • 粗糙度🧔🏽可以决定模型表面光滑程度,轮子的粗糙度调低了一些,可以反射部分光线,符合塑料材质特点!

image.png

模型导出

模型导出,将板面的贴图删除,其他部分材质保留(因为不需要改变),即可导出贴图了,导出的格式为gltf,方便后续程序处理(轮子颜色切换、板面贴图切换等)

image.png

因为我们要实现切换效果,所以需要每个部件单独导出!共计12次。。。最后的输出内容如下。。

image.png

代码部分

环境搭建

主要是模型环境和操作界面

模型加载使用的是Three.js(参考官方案例加载gltf模型部分,修改模型路径即可

  • scene
  • camera
  • renderer
  • gltfLoader
  • orbitControls

操作界面使用幽灵按钮👻,点击展开侧边页面,考虑到不同尺寸设备下的用户体验,使用百分比布局(50%),在小尺寸下,宽度变小,图片变大,有利于操作。

可选内容有板型、板面、和轮滑,分为三部分,多出部分可以滑动,主色调为红色,选中状态添加红色边框,红色是生命的象征❤️,结合滑板,更有运动风格~ 在文字部分添加icon,更直观展现文字内容。

image.png

代码细节部分,我们展开部分占据页面整体宽度的50%,在Three.js的场景部分也要进行相应的调整,这样模型就会显示在画面中间!我们使用个变量 - isCustomize,如果点击按钮,即是进入定制效果,相应的它的绘制窗口就缩小一半


window.addEventListener("resize", resize);

function resize() {
  (camera.aspect =
    window.innerWidth / (state.isCustomize ? 2 : 1) / window.innerHeight),
    camera.updateProjectionMatrix();

  renderer.setSize(
    window.innerWidth / (state.isCustomize ? 2 : 1),
    window.innerHeight
  );
}

板型切换

主要使用排他法💨,点击的显示,其他模型隐藏。

在切换板型的img上,添加data属性,点击时候对模型进行查找即可!

image.png


// 可选版型

surfBodys.forEach((item) => {
  item.addEventListener("click", (e) => {
    audio.play();

    surfBodys.forEach((item) => {
      item.classList.remove("active");
    });

    item.classList.add("active");

    // 加载对应的模型。。
    state.handleSurfBodyIndex = e.target.dataset.surfbodyindex;
    
    // 分两次查找,因为我们有板面的主体和侧边。。(body,side)
    // 显示为true,其他为false
    Object.entries(theModel.surfBodys).forEach((value) => {
      value[1].visible = false;

      if (e.target.dataset.surfbodyindex === value[0]) {
        value[1].visible = true;
        handleSurfBodyMtl(value[1]);
      }
    });

    Object.entries(theModel.surfSides).forEach((value) => {
      value[1].visible = false;

      if (e.target.dataset.surfbodyindex === value[0]) {
        value[1].visible = true;
      }
    });
  });
});

板面切换

切换板面,主要是切换它的贴图!我们直接传递图片的src地址即可!

image.png

我们写一个处理贴图的方法,修改贴图🖼️

function handleSurfBodyMtl(gltf) {

// 这里设置传进来的贴图 - state.handleSurfBodyMtl这个就是图片地址。。
// 设置水平垂直平铺。
  let txt = new THREE.TextureLoader().load(state.handleSurfBodyMtl);
  txt.wrapS = THREE.RepeatWrapping;
  txt.wrapT = THREE.RepeatWrapping;

// 设置后,图片不会出现模糊效果
  txt.anisotropy = 16;
  
  // 调试过程中,出现的问题(查看源码得知。。。)
  // 为什么图像会反转,这里要设置 flipY 为false!!!
  // txt.rotation = Math.PI;
  txt.flipY = false;
   
  // OK,把设置好的图片放到模型上!
  gltf.traverse((item) => {
    if (item.isMesh) {
      item.material = new THREE.MeshPhongMaterial({
        map: txt,
      });
    }
  });
}

滑轮切换

轮滑的颜色修改需要作用在它本身的材质上(我们在Blender中设置过了!)我们打印一下!

这是轮子模型加载后打印出来的!可以看到它的材质以及颜色!我们要做的就是修改它的颜色!

image.png

定义一个theModel对象,将每次获取到的模型都放进去(板面、板型、轮子等,前面用的也是同样的方法,可以这样理解,这个theModel就是实际在场景中展示的部分,我们切换模型,修改的也是theModel中存储的模型,前面我们创建的模型都是在获取模型时候用的,然后放入theModel中!

加载轮子模型,放到theModel中了!

  // wheels
  loader.load( path + models.wheels, (gltf) => {
    theModel.wheels = gltf.scene;

    scene.add(gltf.scene);
  });

修改轮子颜色🎨!

// 定义轮子颜色
const wheelsColor = [
  "#CD5C5C", //红
  "#F08080",
  "#FF0000",

  "#ADFF2F", //绿
  "#00FF00",
  "#98FB98",

  "#FF4500", //黄
  "#FFA500",
  "#FFFF00",

  "#D8BFD8", //粉
  "#DA70D6",
  "#FF00FF",

  "#00BFFF", //蓝
  "#0000FF",
  "#191970",

  "#DCDCDC", //灰
  "#778899",
  "#000000",
];

// 添加到dom,添加data属性
// 可选颜色
let wheelsContent = "";
wheelsColor.forEach((item, index) => {
  wheelsContent += `<li class="itemBox ${
    index === wheelsColor.length - 1 ? "active" : ""
  }" style='background: ${item}' data-color='${item}'></li>`;
});


// 修改轮子颜色!
wheelsItems.forEach((item) => {
  item.addEventListener("click", (e) => {
    // 改变轮子颜色!

    wheelsItems.forEach((item) => {
      item.classList.remove("active");
    });
    item.classList.add("active");

    audio.play();

    // 这是引用 - 直接就修改了!!!
    console.log(theModel.wheels.children[0].material, 'wheel----')
    const wheelsMtl = theModel.wheels.children[0].material;
    wheelsMtl.color = new THREE.Color(e.target.dataset.color);
  });
});

码上掘金

最后就是上线了~ 模型先放到github上,用于获取模型,图片放在掘金的草稿箱里,,,可以获取图片。。。

最终的效果如下😎~

总结

思考过程经历了前期调研 - 数据收集 - 图片处理 - 模型制作 - 代码部分 - 上线,很像产品生产过程,总结一下还是很有收获的。

  • 前期调研考虑到了从网站获取用户及产品的信息资料,了解可以定制的内容以及关于陆地冲浪板的基本介绍
  • 数据收集方面,利用淘宝平台获取图片信息,分析板面的应用风格,收集对应的素材图片。利用百度、知乎等,获取模型相关的细节图片,将素材分为不同的文件夹,方便浏览
  • 图片处理阶段应用了PS,考虑到图片与模型的应用细节,提前处理图片大小和命名
  • 模型的分层导出,是本次最主要的收获,将主体和侧边分为两个部分,可以更好的完成交互任务
  • 代码部分和上线最大的收获是,掘金也可以当图库。。。
  • 借助Blender和Threejs实现了陆冲板的定制效果还是很😎的,继续练习Blender和JS~。

带着我们的滑板一起溜达溜达~~

执念的鱼 提着灯闯过远洋的甄选 继续下潜 无需誓言