又一开源神器面世,一键生成3D图&代码

我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛

Hi,大家好,我是扫地盲僧,时隔数年,我们又见面了。这次给大家带来有一个大礼品,标题党为:《扫地盲僧又一开源效率神器》

炎热的夏天,疫情让技术内卷越来越严重。大家拼命的加班,爆肝,熬夜,为富裕的生活努力。作为一个多年被内卷的老程序员,给大家炎热的夏天解解暑,减少一半的加班时间,换来2倍的产出效率,让你空出时间欣赏欣赏日落,体验体验走在大街上的单身生活。

为了给你们解暑,我又卷了2周才把代码抽离出来。

2022年,炎夏最强3D解暑神器:3DD(3D Design),号称提高300%的产出效率,代码下高达80%的视觉还原度的调优神器。

背景

随着3D、AR、VR等技术的兴起,很多技术团队面临新视觉和新创新的压力。对于中小型企业来说,高级的技术美术、图形工程师、Web前端等人才缺失尤为严重。对于Web前端来说,更是隔行如隔山,如果没有图形技术的基础很难在短时间涉足3D图形领域。

为了保证交付和还原,即使在移动端,很多数团队也无奈选择加持庞大的动画渲染引擎。

当前3D交互技术面临的问题:

  • 3D交互技术对前端不友好,兼容性差
  • 技术资源稀缺,专业领域越来越细分
  • 建模软件渲染时间长,交付周期长
  • 软件代码还原度低,沟通成本高
  • 资产复用困难,尤其是材质属性“因软件而异,因引擎而异”

为了解决以上问题,我们在项目结束后,进行了无数次的脑暴和Search,最终也没有找到合适的方案。我们决定自己动手解决这个问题,无意间看到了五福背后的 Web 3D 引擎 Oasis Engine 正式开源 ,体验了一下,正是我们需要的效果,于是便开始了我们3D领域探索的征程。

架构-设计

我们希望交互效果的验收不仅仅是技术和美术,还可以是UE,甚至PM。让团队所有人一起参与到3D交互设计的过程中来。

针对以上问题,我们加持Oasis引擎,通过整合3D资产、在线材质调整、实时渲染等技术来提供问题的解决方案。

3DD的架构设计-架构设计

3DD介绍

优势

  • 移动端优先,轻量级框架
  • 模型设计规范统一,3D资产复用
  • 建模+调优协同进行,降低沟通成本
  • JS自定义脚本,无缝调用3D视觉模型
  • 提升还原度,代码还原下可以选择实时渲染,提高产出效率

3DD

技术框架

3DD,基于Vite+TypeScript+Vue3+Ant-design等先进框架,加持蚂蚁技术团队强大的Oasis引擎,打造的一款3D资产调优工作台。

  • Vite2.* ,下一代前端开发与构建工具
  • Vue3.0, 渐进式JavaScrip框架
  • TypeScript,离不开的JS超集,持续霸占开发语言榜首
  • Pinia,被任命为下一代的vuex状态管理方案
  • Oasis,出自蚂蚁技术团队,移动优先的高性能 Web 图形引擎
  • …扫地盲僧没啥大本事,就喜欢折腾新技术

底层实现

更换材质


  /**基础颜色纹理。搭配基础颜色使用,是个相乘的关系。 */
  baseTexture: Texture2D | undefined;
  /**法线纹理。可以设置法线纹理 ,在视觉上造成一种凹凸感,还可以通过法线强度来控制凹凸程度。*/
  normalTexture: Texture2D | undefined;
  normalTextureIntensity: number | undefined;
  /**自发射光纹理。我们可以设置自发光纹理和自发光颜色(emissiveFactor)达到自发光的效果,即使没有光照也能渲染出颜色。 */
  emissiveTexture: Texture2D | undefined;
  /**阴影遮蔽纹理。我们可以设置阴影遮蔽纹理来提升物体的阴影细节。 */
  occlusionTexture: Texture2D | undefined;
  /**金属粗糙度纹理。搭配金属粗糙度使用,是相乘的关系。 */
  roughnessMetallicTexture: Texture2D | undefined;

  changeMaterial(material: PBRMaterial | Partial<OneMaterial>) {
    const createTinyColorFun =
      material instanceof PBRMaterial ? tinycolor.fromRatio : tinycolor;
    this.name = material?.name || "";
    this.baseColor = createTinyColorFun(
      material?.baseColor || { r: 1, g: 1, b: 1, a: 1 }
    ).toRgb();
    this.opacity = this.baseColor.a;
    this.emissiveColor = createTinyColorFun(
      material?.emissiveColor || { r: 1, g: 1, b: 1, a: 1 }
    ).toRgb();
    this.metallic = material?.metallic || 0;
    this.roughness = material?.roughness || 0;
    this.isTransparent = !!material?.isTransparent;
  }

复制代码

更换纹理

/**
   * 修改部件的材质纹理
   * @param textureName 纹理名称
   * @param textureType 纹理的类型 'baseTexture'| 'normalTexture'| 'emissiveTexture'| 'occlusionTexture'| 'roughnessMetallicTexture'
   */
  async changeTexture(textureName: string, textureType: string) {
    let texture = TextureManager.defaultTextures[textureName];
    if (!texture.texture) {
      texture.texture = await GameManager.ins.engine.resourceManager.load(
        texture.path
      );
    }
    // 有可能加载纹理失败,所以还是要判断纹理是否存在
    if (texture.texture) {
      let material_in_engine = this.renderer?.getInstanceMaterial() as PBRMaterial;
      material_in_engine[textureType] = texture.texture;
      this.material[textureType] = texture.texture;
    }
  }

复制代码

饱和度亮度调整

	// 饱和度和亮度
    function handleChangeSV(e) {
      let w = refs.saturation_value.value.clientWidth;
      let h = refs.saturation_value.value.clientHeight;
      let x = e.pageX - refs.saturation_value.value.getBoundingClientRect().left;
      let y = e.pageY - refs.saturation_value.value.getBoundingClientRect().top;
      x = (x < w && x > 0) ? x : (x > w ? w : 0);
      y = (y < h && y > 0) ? y : (y > h ? h : 0);
      // 计算饱和度和亮度
      saturation.value = Math.floor((x / w) * 100 + 0.5) / 100;
      value.value = Math.floor((1 - y / h) * 100 + 0.5) / 100;
      // hsv转化为rgb
      let { r, g, b } = hsv2rgb(hue.value, saturation.value, value.value);
      red.value = r;
      green.value = g;
      blue.value = b;
      // 移动背景板圆圈
      pointStyle.value = `top: ${y}px;left: ${x}px;`;
    }

复制代码

播放模型动画

/**播放模型动画 */
  playAnimation() {
    let { animations } = this.modelGltf;
    if (animations && animations.length > 0) {
      const animator = this.modelGltf?.defaultSceneRoot.getComponent(Animator);
      const animatorController = new AnimatorController();
      animations?.forEach((clip: AnimationClip, index) => {
        const layer = new AnimatorControllerLayer("layer" + index);
        const animatorStateMachine = new AnimatorStateMachine();
        animatorController.addLayer(layer);
        layer.stateMachine = animatorStateMachine;
        if (index > 0) layer.blendingMode = AnimatorLayerBlendingMode.Additive;
        const animatorState = animatorStateMachine.addState(clip.name);
        animatorState.clip = clip;
        if (index > 0) animatorState.clipStartTime = 1;
      });
      animator.animatorController = animatorController;
      animator.speed = 1;
      animations?.forEach((clip: AnimationClip, index) => {
        index == 0 && animator.play(clip.name, index);
      });
    }
  }

复制代码

…更多黑科技请移步仓库

Oasis使用体验

优点

  • glTF模型资源的加载和管理
  • PBR材质的渲染
  • 对节点部件的位移、旋转、缩放、矩阵等操作
  • 鼠标交互实现组件高精度拾取
  • JS\TS脚本实现交互通信
  • 相比Three,拥有完善的中文文档和专业的维护团队
  • npm 工作流,纯 TypeScript 编写,对前端工程化优化
  • …more

不足

  • 功能相对Three,不太齐全,当然定位也有所不同
  • 生态不够丰富,优秀参考案例有待补充
  • 社区插件比较少,编辑器…这个官方的RoadMap好像已经规划

神器-功效

产出效率的提升

以前的模式:美术大量时间花费在于找平建模软件和Web 3D引擎之间的渲染差异,以及沟通技术对材质反复调整。

现在的模式:美术集中精力在建模和材质微调上,只需要简单的沟通即可实现秒级渲染和导出图片或代码。

为此我们特意做了一个实验,实验项目为:设计一个相对复杂的IP模型,计算从建模、调试、渲染、导出、骨骼动画等整个设计过程的工时,和结合3DD方案以后做了对比。

image-20220521112838104

效能计算公式:正常人效1/5,实际人效1/2,单个效能提升=(1/2-1/5)* 5 *100%

还原质量的提高

我们经过也通过专业软件 Beyond Compare 4,做了无数次渲染图和引擎实时渲染图的对比,发现其折损率在一个相对可以接受的范围内,当然依旧有很大的提升空间。

image (1)

image

能够肉眼可见的是,建模软件的渲染图拥有更好的光影效果,但3DD秒级实时渲染的产出图,也是完全可以接受的。

针对阴影渲染的补偿方案也有很多,Oasis官网也给出了一些方案,感兴趣的小伙伴可以去扒拉扒拉。

实战-案例

请欣赏我们针对以前项目,进行3DD爆改以后的效果

IP模型和交互动画结合以后的效果

runman

我们在图标交互领域的尝试,以下为多个JS动画方案

还有更多姿势,等着你来挖掘…

不值一提小成绩

Github仓库

github.com/tobe-fe-dal… || 在线体验 || 手心出汗就帮我点点赞和Star

3DD开源直播夜

为了更好的展示3DD的魅力,扫地盲僧决定于5月23晚8点,三端(抖音、B站、微信)同步直播聊聊它的能力&连线蚂蚁技术专家&公开招募开源维护者。请点击选择自己喜欢的渠道,提前锁定我的直播间。

猜你喜欢

转载自juejin.im/post/7100760132430069773