我用threejs制作了一个梯形体(小白快速入门)

1,203 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第十九天,点击查看活动详情

我用threejs制作了一个梯形(小白快速入门)

本章主要涉及的知识点

  1. BufferGeometry
  2. OrbitControls
  3. axesHelper

如何搭建一个梯形

首先个人在做项目前会先搭建一些东西再开始,例如我需要先搭建一下一个最基础的立方体来测试当前环境是否配置成功

下载配置文件

首先我们需要去下载three.js和一些标准库,例如OrbitControls,将three.js复制到该HTML文件所在的目录下的js/目录下,然后在你的浏览器中打开这个HTML文件。然后将下载OrbitControls.js,这里可以直接复制里面的代码或者下载整个three.js包

初始化index.html

下载好配置文件后,我们需要初始化一下index.html

image.png

开始写main.js

首先我们要明确threejs三要素,第一要素创建场景,创建相机,初始化渲染器

创建场景

image.png

创建相机

image.png 这里需要设置一下相机的位置方便打开页面后可以直接看物体位置,不过对于小白来说,相机的位置怎么去设必然会是比较头大的一点,但是在后面添加OrbitControlsaxesHelper后相机的位置的怎么去设计就会很简单很明白

初始化渲染器

image.png

渲染器的作用可能小白一时看不懂,但是当你看到document.body.appendChild这个函数或许就明白了,给页面添加元素,所以你可以初步认为渲染器的作用就是会生成一个节点,什么样的节点?我们用代码生成的3D图像或者页面就是所谓的节点,将节点切入到html中

三部曲做完了,基本上你已经入门了,但是这个并不是你的第一个项目

接下来 将物体添加到场景中

接下来我们则需要将生成一个立方体到3D图像中,然后通过渲染器渲染出来

image.png

一定要记住每当我们生成一个物体就需要通过scene.add这个函数将物体添加到场景中,如果不添加则物体无法展示

image.png

这样我们就制作了一个入门项目,但是这个项目还是不完整,因为我还想有没有那种标明x,y,z的坐标轴呢?因为有坐标轴我就能很清晰的知道物体处于那个位置,在添加其他物体时避免重叠问题

axesHelper

image.png 用于简单模拟3个坐标轴的对象.
红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴. 我们可以看到这个代码很简单就能很快满足我刚刚提出的需求

image.png

image.png

很好,通过辅助线我们发现一个东西,也就是物体的顶点并不在原点,而是物体的中心在原点,所以我们在移动时要明白它是以中心来移动的,接下来我还有一个需求就是3D不应该是能随意旋转放大的吗?为什么这个完全不行,这里其实你就需要用到OrbitControls

OrbitControls

首先我们需要将其进行导入,文件位置要根据自己的文件路径来写

image.png

image.png

实现代码就这么一行即可,简单吧,写完后还需要添加下面这些代码,这个你可以定义成js所谓的定时器即可,因为这个东西和游戏一样要实时渲染才能看到其变化

image.png

效果图

这个我就不放图了,自己通过代码即可去试试

初始化项目的完整代码

//创建场景
var scene = new THREE.Scene({ color: "#ffffff" });
//创建基础相机
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
//设置相机位置,小白一般都可以这样设置
camera.position.set(0, 0, 10)
scene.add(camera)
const geometry = new THREE.BoxGeometry(1, 1, 1);
//创建几何体的纹理贴图
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
//生成一个立方体
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
//辅助线的大小一定要大于物体的长宽高
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
//初始化渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true });
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
//重置窗口大小
window.addEventListener("resize", () => {
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
    renderer.setSize(window.innerWidth, window.innerHeight)
})

const controls = new THREE.OrbitControls(camera, renderer.domElement)

function animate(time) {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);

}
animate()

实现梯形体

我在想很多物体都是官方提供的,我自己能不能实现一个3D图形,BufferGeometry能满足你的需求

image.png

点生线,线生面,面生图形,BufferGeometry则就是根据你自己定义的点去生成面,在通过面与面的组合生成一个图形,废话不多说,直接开始

我们先看一下梯形体是什么样的

image.png

ok!知道了样子我们开始第一步,了解BufferGeometry基本使用

BufferGeometry基本使用

image.png

定义好顶点数组后,我们将顶点设置到 BufferGeometry 中,代码如下。其中,THREE.BufferAttribute(positions, 3) 中第二个参数 3 指的是数组 positions 中每三个元素构成一个点,分别表示x y z 值。

这样我们就简单的绘制出了一个三角形

image.png

这里为什么绘制三角形不直接绘制成四边形或者五边形什么的,主要是还是我不会,或者api不允许吧,所以梯形体只能是由很多个三角形来拼接而成😒😒😒🤣🤣,

绘制底部的矩形

知道怎么使用后,我们先一步一步来,我们先绘制出最简单的底边矩形,这里我们定义底边矩形为长为6m宽为4m

image.png

image.png

image.png

绘制顶部的矩形

同理,我们设置高为2

image.png

image.png

二侧面

侧面一个就很好画了,有了顶点的话就很好画侧面

image.png

image.png

对面和正面和底面

接下来就是绘制对面和正面,还有底面来完成整个梯形体

image.png

image.png

这样我们就简单的实现了一个梯形体了

完整源码

/*

 * @Description:

 * @Version: 1.0

 * @Autor: solid

 * @Date: 2022-12-07 15:58:51

 * @LastEditors: solid

 * @LastEditTime: 2022-12-10 21:52:03

 */

//创建场景

var scene = new THREE.Scene({ color: "#ffffff" });

//创建基础相机

var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

//设置相机位置,小白一般都可以这样设置

camera.position.set(0, 10, 10)

scene.add(camera)

//初始化BufferGeometry

const geometry = new THREE.BufferGeometry()

//设置顶点坐标

const vertices = new Float32Array([

    //底部的矩形

    -3, 0, 0,

    -3, 0, 2,

    3, 0, 2,

  


    -3, 0, 0,

    3, 0, 2,

    3, 0, 0,

    //顶部的矩形

    -1.5, 2, 0.5,

    -1.5, 2, 1.5,

    1.5, 2, 1.5,

  


    -1.5, 2, 0.5,

    1.5, 2, 1.5,

    1.5, 2, 0.5,

  


    //侧面

   -3,0,0,

   -1.5,2,1.5,

   -1.5,2,0.5,

   

    -3,0,0,

    -3,0,2,

    -1.5,2,1.5,

  


    1.5,2,1.5,

    3,0,0,

    1.5,2,0.5,

  


    3,0,2,

    3,0,0,

    1.5,2,1.5,

  


    //正面

    3,0,2,

    -1.5,2,1.5,

    -3,0,2,

  


    3,0,2,

    1.5,2,1.5,

    -1.5,2,1.5,

  


    //背面

    -1.5,2,0.5,

    3,0,0,

    -3,0,0.5,

  


    1.5,2,0.5,

    3,0,0,

    -1.5,2,0.5,

   

    //底面

    -3,0,2,

    -3,0,0,

    3,0,2,

  


    3,0,0,

    3,0,2,

    -3,0,0


])

  


///连接顶点

geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3))

//材质

const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });

//生成物体

const cube = new THREE.Mesh(geometry, material);

scene.add(cube);

//辅助线的大小一定要大于物体的长宽高

const axesHelper = new THREE.AxesHelper(5);

scene.add(axesHelper);

//初始化渲染器

const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true });

renderer.setSize(window.innerWidth, window.innerHeight)

document.body.appendChild(renderer.domElement)

//重置窗口大小

window.addEventListener("resize", () => {

    camera.aspect = window.innerWidth / window.innerHeight

    camera.updateProjectionMatrix()

    renderer.setSize(window.innerWidth, window.innerHeight)

})

  


const controls = new THREE.OrbitControls(camera, renderer.domElement)

  


function animate(time) {

    requestAnimationFrame(animate);

    renderer.render(scene, camera);

  


}

animate()