我和AI做同事:10分钟搞定 3D 人物模型的创建和展示

1,045 阅读7分钟

背景

最近有个需求,需要在项目中插入一个 3D 人物。为了能够在需求出来之前,从前端技术角度初步排除下有没有坑,所以准备尝试做一下简单的技术调研。

这里跟大家分享下我跟我的 AI 同事们合作搞定这件事的过程,希望可以给大家提供一些参考。

目录

  • 需求分析
  • 技术方案
  • 核心实现
  • 效果展示

需求分析

通过仔细分析了项目的实际需求后,抽象出了核心功能:在网页中渲染一个 3D 人物模型。

将需求拆分成两个步骤:

  • 创建一个可以在网页中渲染的 3D 人物模型
  • 在网页中渲染这个 3D 模型

简化后:

  • 获取 3D 人物模型
  • 渲染 3D 人物模型

技术方案

明确了需求后,接下来就该我的 AI 同事们上场了。这里我对它们做了简单的分工:

  • 通用 LLM 大模型部门的同事:生成一段可以创建人物图片的提示词
  • 文生图大模型部门的同事:生成一张人物图片
  • 图片生成模型部门的同事:生成一个 3D 任务模型
  • 代码生成部门的同事:根据我的需要,生成能够直接渲染 3D 模型的代码

听说不会偷懒的程序员,不是好的厨子。

所以我准备不写一行代码,来实现整个需求。完全信任我的同事们。

tips:本人不为任何 AI 产品做广告,此处只是为了做演示,大家可以根据自身情况选择合适的 AI 产品。

核心实现

提示词生成(用时约为1分钟)

之所以需要借助 AI 生成“文生图”的提示词,是因为我不太相信自己提示词编写的水平,因此不如直接交给我的同事来搞定,而我只需要说出需求即可。

我跟同事的对话如下:

图3-1.png

太长了,继续精简:

图3-2.png

精简后的提示词:

生成一位30岁左右亚洲女性穿正装的3D渲染全身像。要求:面容精致,气质优雅。着装包括深色西装外套、白色衬衫、直筒西裤,配黑色尖头高跟鞋。背景为简约办公室,模特自然站立,微笑。图像高清,适合商业用途。

搞定,去找下一位同事吧。

人物图片渲染(用时约为2分钟)

因为我对人物的形象没有太多细节要求,因此只要不太离谱,越快生成越好。

图3-3.png

这里选择一个自己看上去比较还不错的图片,下载到本地。如果你不满意,可以微调,甚至相同的文案,再次生成。

图3-4.png

搞定,继续找下一位同事。

3D 模型生成(用时约2分钟)

进入 ReadyPlayer.Me 网站,选择我们刚才下载的图片。

图3-5.png

上传文件。

图3-6.png

接着下一步即可。

图3-7.png

最终我们可以复制生成的这个模型的 GLB 地址:models.readyplayer.me/66f6497420c…

需要注意的是:

  • 这个过程可能会提示一些报错,建议无痕模式访问。
  • 生成的模型并不是完全跟我们的图片一样,但作为测试,基本够用。

接着我们可以拿着模型,去找我们的“开发同学”。

代码生成(用时约5分钟)

打开 MarsCode,创建一个前端页面项目。

图3-9.png

向它说明你的来意:

我想实现通过js加载glb模型,在web页面中渲染3D人物,请给我一个完整的代码示例,而我只需要传入特定的 glb 模型地址即可

图3-10.png

把代码放到左侧的 html 文件中。

图3-11.png

把我们上一个步骤复制的 glb 地址,替换path_to_your_model.glb即可。点击运行。

图3-12.png

此时,发现右侧预览页面,什么也没有,接下来我们该请教同事帮我们解决这个问题。因为预览页面无法看到实际的报错,我们可以点击预览或直接复制预览地址在新的浏览器 Tab 中打开,可以看到相关报错信息。

图3-13.png

定位到问题,那么就继续请教我们的同事,看看它有什么好的建议。

图3-14.png

再探,再报。在我一步一步解决了引入问题后,发现依然无法渲染。

最终。。。。我放弃了使用 MarsCode,因为它让我自己排查错误,而不是再帮我生成一段代码。

图3-15.png

为了不耽误时间,我决定用 ChatGPT 来帮我救场,最终它给我了全新的代码,并正常渲染了页面,当然我跟 ChatGPT 更多纠缠的是 Three.js 的引入问题,而非逻辑问题。

图3-16.png

这里我把最终可以渲染的完整代码贴出来,感兴趣的同学可以本地跑一下试试。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>GLB 3D Model Viewer</title>
    <style>
      body {
        margin: 0;
      }
      canvas {
        display: block;
      }
    </style>
  </head>
  <body>
    <!-- 使用 import map 来简化导入路径 -->
    <script type="importmap">
      {
        "imports": {
          "three": "./libs/three.module.min.js"
        }
      }
    </script>

    <script type="module">
      // 导入 three.js 和 GLTFLoader 模块
      import * as THREE from "three";
      import { GLTFLoader } from "./libs/GLTFLoader.js";

      let scene, camera, renderer, model;

      function init() {
        // 创建场景
        scene = new THREE.Scene();
        scene.background = new THREE.Color(0xdddddd);

        // 创建摄像机
        camera = new THREE.PerspectiveCamera(
          75,
          window.innerWidth / window.innerHeight,
          0.1,
          1000
        );
        camera.position.z = 5;

        // 创建渲染器
        renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 添加光源
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
        scene.add(ambientLight);

        const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
        directionalLight.position.set(1, 1, 1).normalize();
        scene.add(directionalLight);

        // 加载并渲染 GLB 模型
        const loader = new GLTFLoader();
        const glbUrl =
          "https://models.readyplayer.me/66f2af93e85af47110f16079.glb"; // 替换为你的 GLB 模型 URL

        loader.load(
          glbUrl,
          function (gltf) {
            model = gltf.scene;
            scene.add(model);
            animate();
          },
          undefined,
          function (error) {
            console.error("An error occurred:", error);
          }
        );

        // 响应窗口大小调整
        window.addEventListener("resize", onWindowResize, false);
      }

      function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      }

      function animate() {
        requestAnimationFrame(animate);

        // 旋转模型以展示效果
        if (model) {
          model.rotation.y += 0.01;
        }

        renderer.render(scene, camera);
      }

      // 初始化场景
      init();
    </script>
  </body>
</html>

需要注意的是:

  • 上述代码中,我是将 Three.js 的代码下载到了本地,因此注意替换上面的引入路径
  • 为了方便对比,我也把 MarsCode 生成的代码放到本地运行,依然无法正常渲染。不过本文的核心还是为了以实现最初的目标为主,因此没有过多的再花时间在 MarsCode 上调优。

效果展示

在多个同事的努力下,我们最终可以看到如下的效果。

图4-1.gif

至此,我们完成了我们想要的结果,当然你也可以基于此让 AI 帮你优化出更好的效果。

杂谈

  • 本文不对使用过程中的任何工具做测评,因此如果你想把某个环节中的效果打磨的更好,可以根据你对 AI 工具的理解,做相关平替。
  • 经过上面的案例,你会发现,在 AI 时代,编码能力变得越来越不重要了。重要的是如何梳理出一套可以让 AI 帮你编码的业务拆解能力和需求描述能力。拥有了这两个能力,你可以知道如此多的 AI 工具,哪一个能帮你解决不同的问题,以及如何跟 AI 对话,让它真正的帮你写出你想要的代码。
  • 在使用 AI 工具的过程中,我越来越体会到高质量的训练数据对大模型的重要性。很多时候,大模型的答案是有时效性的。也可以这么理解,现阶段的大模型能够回答你的问题,更多的是这个行业或这个世界已经存在的方案。如果这些方案还不存在,那么大模型更多的是猜测可能的结果,而达不到精准的结果。
  • 深度的使用过 AI ,才不会畏惧 AI,我们因此才能更从容的接受 AI。

最后

其实内心稍微纠结了一下,要不要让这篇文章参加 “码” 上双节,共话精彩 ——豆包MarsCode 放“码”过来!创意征文活动,毕竟在使用 MarsCode 生成代码的环节,效果并不理想。

但是反过来想了下,无论任何一个大模型,都需要有一个不断打磨的过程,如果因为挂上了这个话题,能让负责 MarsCode 的同学更快速的看到一个特殊的 Case,未尝不是一件好事,希望有助于 MarsCode 底层大模型不断优化。

所以,也就释然了。也许下一次再参加活动时,MarsCode已经变得更强了。