ThreeJS掘金最通俗入门教程04-几何体,材质纹理贴图

1,323 阅读5分钟

献给读者,我起这个标题绝对不是想着靠标题给大家吸引进来给我引流加阅读量什么的,我总结看了一遍掘金所有ThreeJS的入门文章,发现大家很多都是写的给自己看的文章,偏向于让所有人都能看懂的部分还是少了。如果说这篇文章确实对您有帮助您可以点赞支持下我产出更多优秀内容。如果说确实存在需要改进的地方您可以在评论区留下宝贵的意见。我一定认真学习,认真改进

封面是两头小龙在峡谷上方徘徊,一头是电龙,一头是炼金龙,电龙魂其实我觉得非常的鸡肋,因为我感觉那个东西就是加了一个闪电伤害,其实和火龙混并没有太大的差别。但是炼金龙这个设计我真的觉得挺不错的。虽然可能确实超模了点。不过也是设计师思维进步的象征。还有召唤师峡谷的海克斯科技传送门,英雄联盟的新版本多了很多非常有意思的东西。今天我们会学习ThreeJS是怎么给模型贴图的,再顺便介绍下其他模型。

代码仓库:gitee.com/gmlcj/three…

ThreeJS中的常见几何体

BufferGeometry和Geometry有什么不同

如果你就是简单理解BufferGeotry和Geometry有什么不同。就是两者的数据结构不一样。缓冲类型几何体BufferGeometry相比于Geometry性能更好.ThreeJS渲染器在解析几何体对象的时候,如果几何体对象是普通几何体对象Geometry,ThreeJS的WebGL渲染器会把普通的几何体对象Geometry转化为缓冲类型的几何体对象BufferGeometry。然后再提取BufferGeometry包含的顶点信息,这里可以看出来直接使用BufferGeometry解析的时候相对Geometry少了一些。自然性能会高一点。

image.png

以上这张图概括了目前所有的材质

代码片段

我们在开发的过程中啊,可能会遇到很多地方要写重复的代码,然后这个时候的话,我们就可以将写的比较频繁的代码将他抽离出来,组成一个代码片段。然后用VScode进行统一管理【当然,你要是用Vscode最好】,如果你用的其他文本编辑器的话,那么其实最终做到的结果都是一样的,只要能实现功能就行了。

ThreeJS的Vue代码片段

<template>
  <section id="container"></section>
</template>
<script>
import { defineComponent, onMounted, reactive, toRefs } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
let scene, mesh;
export default defineComponent({
  setup() {
    const state = reactive({
      renderer: "",
      controls: "",
      camera: "",
      requestID: "",
    });
    // 加载场景
    const loadScene = () => {
      scene = new THREE.Scene();
    };
    // 加载相机
    const loadCamera = () => {
      state.camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      state.camera.position.set(200, 300, 200); //设置相机位置
      state.camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
    };
    // 加载渲染器
    const loadRenderer = () => {
      state.renderer = new THREE.WebGLRenderer();
      state.renderer.setSize(window.innerWidth, window.innerHeight);
      let container = document.getElementById("container");
      state.renderer.setClearColor(0xb9d3ff, 1);
      container.appendChild(state.renderer.domElement);
      state.renderer.render(scene, state.camera);
    };
    // 加载控制器
    const loadController = () => {
      state.controls = new OrbitControls(
        state.camera,
        state.renderer.domElement
      );
      state.controls.addEventListener("change", render);
    };
    // 加载渲染实体
    const loadGeometry = () => {
        // ..此处往场景中做对应的添加操作
    };
    // 实际要执行的渲染
    const render = () => {
      mesh.rotation.x += 0.01;
      mesh.rotation.y += 0.01;
      state.renderer.render(scene, state.camera);
    };
    // 执行动画
    const animation = () => {
      state.requestID = requestAnimationFrame(animation);
      render();
    };
    // 光源设置
    const loadPoint = () => {
      /**
       * 光源设置
       */
      //点光源
      var point = new THREE.PointLight(0xffffff);
      point.position.set(400, 200, 300); //点光源位置
      scene.add(point); //点光源添加到场景中
      //环境光
      var ambient = new THREE.AmbientLight(0x444444);
      scene.add(ambient);
    };
    const init = () => {
      loadScene();
      loadCamera();
      loadRenderer();
      loadController();
      loadGeometry();
      loadPoint();
      animation();
    };
    onMounted(() => {
      init();
    });
    return {
      ...toRefs(state),
    };
  },
});
</script>

上面这个代码片段将环境光,场景,相机,渲染器,还有控制器都给您配置好了,你可以直接将上面的代码拿去Vscode做一个代码片段。

选择vscode的文件→首选项→用户片段

image.png

然后选择你想要在哪个类型的文件后面生成代码片段。这里以html文件为例子:

image.png

vscode会打开一个叫做html.json的文件让你来生成代码片段。一般是写一个类型的代码,然后你在相应类型的文件后面输入prefix就可以生成这个类型的文件代码。

这里推荐一个网站,帮助你快速生成代码片段:snippet-generator.app/

image.png

你只需要填写左边的代码片段名称,代码片段生成快捷键,要生成的代码块,然后将右边的内容复制到vscode里面去就好了。

代码片段创建好了之后使用方式非常简单。新建一个对应类型的文件,输入上面你选择的prefix,然后直接回车,就可以非常方便的按照模板生成一套套的代码了。

可以看到,我们上面的代码片段唯一跳过的就是geometry这个部分的函数,也就是往场景中加内容的函数,场景定义好了,其他的地方你都是可以自己自由发挥的,包括下面各种常用的材质几何体的代码实现,都是只改变了loadGeometry方面的函数,然后达成实现不同功能的效果。

点材质

所谓点材质,就是以点来作为物体的材质。

代码实现

<template>
  <section id="container"></section>
</template>
<script>
import { defineComponent, onMounted, reactive, toRefs } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
let scene, point;
export default defineComponent({
  setup() {
    const state = reactive({
      renderer: "",
      controls: "",
      camera: "",
      requestID: "",
    });
    // 加载场景
    const loadScene = () => {
      scene = new THREE.Scene();
    };
    // 加载相机
    const loadCamera = () => {
        state.camera=new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        state.camera.position.set(200, 300, 200); //设置相机位置
        state.camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
    };
    // 加载渲染器
    const loadRenderer = () => {
        state.renderer = new THREE.WebGLRenderer();
        state.renderer.setSize(window.innerWidth, window.innerHeight);
        let container = document.getElementById("container");
        state.renderer.setClearColor(0xb9d3ff, 1);
        container.appendChild(state.renderer.domElement);
        state.renderer.render(scene, state.camera);
    };
    // 加载控制器
    const loadController = () => {
        state.controls=new OrbitControls(state.camera,state.renderer.domElement);
        state.controls.addEventListener('change', render);
    };
    // 加载渲染实体
    const loadGeometry = () => {
      var geometry = new THREE.SphereGeometry(100, 25, 25); //创建一个球体几何对象
      var material = new THREE.PointsMaterial({
        color: 0x0000ff, //颜色
        size: 1, //点渲染尺寸
      });
      point=new THREE.Points(geometry,material)
      scene.add(point)
    };
    // 实际要执行的渲染
    const render = () => {
        point.rotation.x += 0.01;
        point.rotation.y += 0.01;
        state.renderer.render(scene, state.camera);
    };
    const animation = () => {
      state.requestID = requestAnimationFrame(animation);
      render();
    };
    const init = () => {
      loadScene();
      loadCamera();
      loadRenderer();
      loadController();
      loadGeometry();
      animation();
    };
    onMounted(() => {
      init();
    });
    return {
      ...toRefs(state),
    };
  },
});
</script>

实现效果

image.png

基础线材质

基础线材质,就是将线附加到材质球体上面去 ##代码实现

<template>
  <div id="container"></div>
</template>
<script>
import {
  defineComponent,
  onBeforeMount,
  onMounted,
  reactive,
  toRefs,
} from "vue";
import * as THREE from "three";
let scene, line;
export default defineComponent({
  setup() {
    const state = reactive({
      renderer: "",
      requestID: "",
      point: "",
    });
    onMounted(() => {
      init();
    });
    onBeforeMount(() => {
      // 页面跳转的时候停止RAF
      window.cancelAnimationFrame(state.requestID);
    });
    // 创建相机
    const initCamera = () => {
      // 创建相机
      state.camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      state.camera.position.z = 5;
      state.camera.position.x = 0;
    };
    // 创建渲染器
    const initRenderer = () => {
      state.renderer = new THREE.WebGLRenderer();
      state.renderer.setSize(window.innerWidth, window.innerHeight);
      let container = document.getElementById("container");
      state.renderer.setClearColor(0xb9d3ff, 1);
      container.appendChild(state.renderer.domElement);
    };
    // 执行动画
    const animate = () => {
      state.requestID = requestAnimationFrame(animate);
      render();
    };

    // 具体用来调用的动画
    const render = () => {
      line.rotation.x += 0.01;
      line.rotation.y += 0.01;
      state.renderer.render(scene, state.camera);
    };
    const loadGeometry = () => {
      var geometry = new THREE.SphereGeometry(100, 25, 25); //球体
      // 直线基础材质对象
      var material = new THREE.LineBasicMaterial({
        color: 0x0000ff,
      });
      line = new THREE.Line(geometry, material);
      scene.add(line);
      state.renderer.render(scene, state.camera);
    };
    // 光源设置
    const initLight = () => {
      state.point = new THREE.PointLight(0xffffff);
      state.point.position.set(400, 200, 300); //点光源位置
      var ambient = new THREE.AmbientLight(0x444444);
      scene.add(ambient);
    };
    const init = () => {
      //加载场景
      scene = new THREE.Scene();
      // 加载相机
      initCamera();
      // 加载渲染器
      initRenderer();
      // 加载物体材质
      loadGeometry();
      initLight();
      animate();
    };
    return {
      ...toRefs(state),
    };
  },
});
</script>

实现效果

image.png

网格材质对象比较

这里我们将圆柱体纯色填充,并且设置环境光和点光源的角度,实习一个被纯色填充的光照效果

代码实现

<template>
  <section id="container"></section>
</template>
<script>
import { defineComponent, onMounted, reactive, toRefs } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
let scene, mesh;
export default defineComponent({
  setup() {
    const state = reactive({
      renderer: "",
      controls: "",
      camera: "",
      requestID: "",
    });
    // 加载场景
    const loadScene = () => {
      scene = new THREE.Scene();
    };
    // 加载相机
    const loadCamera = () => {
      state.camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      state.camera.position.set(200, 300, 200); //设置相机位置
      state.camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
    };
    // 加载渲染器
    const loadRenderer = () => {
      state.renderer = new THREE.WebGLRenderer();
      state.renderer.setSize(window.innerWidth, window.innerHeight);
      let container = document.getElementById("container");
      state.renderer.setClearColor(0xb9d3ff, 1);
      container.appendChild(state.renderer.domElement);
      state.renderer.render(scene, state.camera);
    };
    // 加载控制器
    const loadController = () => {
      state.controls = new OrbitControls(
        state.camera,
        state.renderer.domElement
      );
      state.controls.addEventListener("change", render);
    };
    // 加载渲染实体
    const loadGeometry = () => {
      /**
       * 创建网格模型
       */
      //var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
      var geometry = new THREE.SphereGeometry(100, 25, 25); //球体
      //

      //基础材质`MeshBasicMaterial`不受带有方向光源影响,没有棱角感
      var material = new THREE.MeshBasicMaterial({
        color: 0x0000ff,
      });

      //MeshLambertMaterial`材质可以实现网格Mesh表面与光源的漫反射光照计算,有了光照计算,物体表面分界的位置才会产生棱角感。
      // var material = new THREE.MeshLambertMaterial({
      //     color: 0x0000ff,
      // });
      //高光网格材质MeshPhongMaterial除了和MeshLambertMaterial一样可以实现光源和网格表面的漫反射光照计算,还可以产生高光效果(镜面反射)。
      var material = new THREE.MeshPhongMaterial({
        color: 0x0000ff,
        specular: 0x444444, //高光部分的颜色
        shininess: 30, //高光部分的亮度,默认30
      });

      mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
      scene.add(mesh);
    };
    // 实际要执行的渲染
    const render = () => {
      mesh.rotation.x += 0.01;
      mesh.rotation.y += 0.01;
      state.renderer.render(scene, state.camera);
    };
    // 执行动画
    const animation = () => {
      state.requestID = requestAnimationFrame(animation);
      render();
    };
    // 光源设置
    const loadPoint = () => {
      /**
       * 光源设置
       */
      //点光源
      var point = new THREE.PointLight(0xffffff);
      point.position.set(400, 200, 300); //点光源位置
      scene.add(point); //点光源添加到场景中
      //环境光
      var ambient = new THREE.AmbientLight(0x444444);
      scene.add(ambient);
    };
    const init = () => {
      loadScene();
      loadCamera();
      loadRenderer();
      loadController();
      loadGeometry();
      loadPoint();
      animation();
    };
    onMounted(() => {
      init();
    });
    return {
      ...toRefs(state),
    };
  },
});
</script>

实现效果

image.png

创建纹理贴图

这里我们学会如何加载本地服务器上面的图片,并且将图片放到我们的材质上面去

代码实现

<template>
  <section id="container"></section>
</template>
<script>
import { defineComponent, onMounted, reactive, toRefs } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
let scene, mesh;
export default defineComponent({
  setup() {
    const state = reactive({
      renderer: "",
      controls: "",
      camera: "",
      requestID: "",
    });
    // 加载场景
    const loadScene = () => {
      scene = new THREE.Scene();
    };
    // 加载相机
    const loadCamera = () => {
      state.camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      state.camera.position.set(200, 300, 200); //设置相机位置
      state.camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
    };
    // 加载渲染器
    const loadRenderer = () => {
      state.renderer = new THREE.WebGLRenderer();
      state.renderer.setSize(window.innerWidth, window.innerHeight);
      let container = document.getElementById("container");
      state.renderer.setClearColor(0xb9d3ff, 1);
      container.appendChild(state.renderer.domElement);
      state.renderer.render(scene, state.camera);
    };
    // 加载控制器
    const loadController = () => {
      state.controls = new OrbitControls(
        state.camera,
        state.renderer.domElement
      );
      state.controls.addEventListener("change", render);
    };
    // 加载渲染实体
    const loadGeometry = () => {
        var texture = new THREE.TextureLoader().load( '/textures/crate.gif' );

        var geometry = new THREE.BoxBufferGeometry( 200, 200, 200 );
        var material = new THREE.MeshBasicMaterial( { map: texture } );

        mesh = new THREE.Mesh( geometry, material );
        scene.add( mesh );
    };
    // 实际要执行的渲染
    const render = () => {
      mesh.rotation.x += 0.01;
      mesh.rotation.y += 0.01;
      state.renderer.render(scene, state.camera);
    };
    // 执行动画
    const animation = () => {
      state.requestID = requestAnimationFrame(animation);
      render();
    };
    // 光源设置
    const loadPoint = () => {
      /**
       * 光源设置
       */
      //点光源
      var point = new THREE.PointLight(0xffffff);
      point.position.set(400, 200, 300); //点光源位置
      scene.add(point); //点光源添加到场景中
      //环境光
      var ambient = new THREE.AmbientLight(0x444444);
      scene.add(ambient);
    };
    const init = () => {
      loadScene();
      loadCamera();
      loadRenderer();
      loadController();
      loadGeometry();
      loadPoint();
      animation();
    };
    onMounted(() => {
      init();
    });
    return {
      ...toRefs(state),
    };
  },
});
</script>

实现效果

image.png

边缘几何体

代码实现

<template>
  <section id="container"></section>
</template>
<script>
import { defineComponent, onMounted, reactive, toRefs } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
let scene, mesh;
export default defineComponent({
  setup() {
    const state = reactive({
      renderer: "",
      controls: "",
      camera: "",
      requestID: "",
    });
    // 加载场景
    const loadScene = () => {
      scene = new THREE.Scene();
    };
    // 加载相机
    const loadCamera = () => {
      state.camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      state.camera.position.set(200, 300, 200); //设置相机位置
      state.camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
    };
    // 加载渲染器
    const loadRenderer = () => {
      state.renderer = new THREE.WebGLRenderer();
      state.renderer.setSize(window.innerWidth, window.innerHeight);
      let container = document.getElementById("container");
      state.renderer.setClearColor(0xb9d3ff, 1);
      container.appendChild(state.renderer.domElement);
      state.renderer.render(scene, state.camera);
    };
    // 加载控制器
    const loadController = () => {
      state.controls = new OrbitControls(
        state.camera,
        state.renderer.domElement
      );
      state.controls.addEventListener("change", render);
    };
    // 加载渲染实体
    const loadGeometry = () => {
      var geometry = new THREE.BoxGeometry(10, 10, 10); //创建一个立方体几何对象Geometry

      var edges = new THREE.EdgesGeometry(geometry, 1);
      mesh = new THREE.LineSegments(
        edges,
        new THREE.LineBasicMaterial({ color: 0xffffff })
      );
      scene.add(mesh);
    };
    // 实际要执行的渲染
    const render = () => {
      mesh.rotation.x += 0.01;
      mesh.rotation.y += 0.01;
      state.renderer.render(scene, state.camera);
    };
    // 执行动画
    const animation = () => {
      state.requestID = requestAnimationFrame(animation);
      render();
    };
    // 光源设置
    const loadPoint = () => {
      /**
       * 光源设置
       */
      //点光源
      var point = new THREE.PointLight(0xffffff);
      point.position.set(400, 200, 300); //点光源位置
      scene.add(point); //点光源添加到场景中
      //环境光
      var ambient = new THREE.AmbientLight(0x444444);
      scene.add(ambient);
    };
    const init = () => {
      loadScene();
      loadCamera();
      loadRenderer();
      loadController();
      loadGeometry();
      loadPoint();
      animation();
    };
    onMounted(() => {
      init();
    });
    return {
      ...toRefs(state),
    };
  },
});
</script>

实现效果

image.png

海克斯科技传送门

参考文献