Three.js——第一篇:部署以及基础代码创建场景、GUI调整样式

news2024/12/27 1:17:03

three.js官网:three.js docs

中文技术文档1:| 麒跃科技

中文技术文档2:3. 开发和学习环境,引入threejs | Three.js中文网

很多教程一开始要大家自己部署three.js的中文本地部署,我就不弄了,我弄了半天也没弄出来烦了,反正我也不爱看官方文档我就不弄了,直接开干。

另外本人学的前端,习惯用vue就以vue的开发框架做基础引入three.js了

一、基础部署

1、先创建新文件夹,然后终端打开,按以下步骤一步一步输入命令(别管为什么,我没了解)

npm init vite@latest

然后写你的项目名称,随便就行

选择vue

选JS

然后根据最后的提示把这三句执行

然后成功搭建vue的框架

最后再导入three.js就好了

二、先尝试一个vue的小demo

先把app.vue的js、html、css部分先删去,然后用app.vue的全局样式来展示three.js的效果

1、样式,先初始化,因为three.js是基于canva画布来成像的,所以要设置一下canvas样式,先不用管那么多直接复制就好

<style>
  *{
    margin: 0;
    padding: 0;
  }

  canvas{
    width: 100vw;
    height: 100vh;
    display: block;
    position: fixed;
    top: 0;
    left: 0;
  }
</style>

2、js部分

首先前面我们已经下载了three.js,那么就要在js部分导入

// 导入three.js
import * as THREE from "three"

然后要呈现下面这种基本的three3D场景,首先要创建一个【场景】和一个【相机】,你就创就行了别问那么多

创建场景

//创建场景
const scene = new THREE.Scene();

创建相机

这里需要配置这么几个参数:

//创建相机
const Camera = new THREE.PerspectiveCamera(
  45, //相机视角
  window.innerWidth / window.innerHeight, //宽高比
  0.1, //近平面,最近能看到多近
  1000 //远平面,最远能看多远
);

这里的视角可以根据下面的图来理解

创建渲染器

要有渲染器才能把场景渲染到屏幕

//创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth , window.innerHeight);//渲染器的大小就是屏幕的大小
document.body.appendChild(renderer.domElement);//这样就是是把canvas添加进去

现在还是一片空白

那么现在就加一个【几何体】看看,并设置它的【材质】(暂时用绿色)

//创建几何体
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)

然后初始化相机的位置

//设置相机位置
camera.position.z = 5
camera.lookAt(0,0,0)

现在还不能看到方块、场景,因为还没有渲染,这里就需要调用渲染器的render函数来进行渲染,把我们的【场景】和【相机】放进去

//渲染
renderer.render(scene,camera);

但是现在只能看到一个静态的一面的正方形,想看动起来的正方体就要调用动画函数

创建一个动画函数,然后调用requestAnimationFrame();函数来实现补帧,比如你的函数名叫xxx,那么requestAnimationFrame(xxx);就是自动来调用你这个xxx函数进行动画补帧,然后旋转的逻辑就是这三:cube.rotation.x、cube.rotation.y、cube.rotation.z,让它们不停自增就可以达到旋转了(其实选两个就够了)

最后别忘了:1、把渲染函数放进动画函数里  2、在下面调用动画函数

//渲染函数
function animate(){
  requestAnimationFrame(animate);
  //旋转
  cube.rotation.x += 0.01
  cube.rotation.y += 0.01
  //渲染
  renderer.render(scene,camera);
}
animate();

然后就爽了

三、开始正式认识3D

1、三维坐标系

这是啥我就不讲了,学过空间几何的都懂,那么像我们刚刚创建的场景里,只看到乌漆嘛黑的背景跟一个烂盒子在那转,根本没办法分清方向啊

【那么我们就要在【场景】里添加一个xyz轴辅助线】

//添加世界坐标辅助器
const axesHelpr = new THREE.AxesHelper(5) //5这个参数是指xyz轴的线段要多长
scene.add(axesHelpr)

红色X轴、绿色Y轴、蓝色Z轴,但是此时我们看不到Z轴是因为我们一开始设置了初始化相机的视角是正对着Z轴的,所以Z轴就是一个点

那么我们可以把相机位置偏移一点

但是光这么看不好操控,我们想要拖动坐标轴,或者说改变我们的视角方向怎么办

【创建轨道控制器】

先导入控制器

然后添加轨道控制器

//添加轨道控制器
const controls = new OrbitControls(camera,renderer.domElement);

还要再动画函数里更新轨道控制器

现在就可以任意拖动坐标轴了

另外还有这些

controls.enableDamping = true//设置阻尼惯性
controls.dampingFactor = 0.05//设置阻尼系数
controls.autoRotate = true//开启坐标轴自动旋转

提示:

const controls = new OrbitControls(camera,监听对象);

这里控制器的第二个参数是要监听的对象

const controls = new OrbitControls(camera,renderer.domElement);
// const controls = new OrbitControls(camera,document.body); //这里控制器的第二个参数是要监听的对象,我们也可以换成document.body或者别的都可以
// (不过留意一下,监听对象要换成document.body的话,记得要在css那给body加上宽高才能看得到)

2、位移

我们默认几何体的位置是在坐标轴的中心点(x,y,z)-(0,0,0)

那么用“网格.position.x”、“网格.position.y”、“网格.position.z”

或者“网格.position.set(x值,y值,z值)”

这两种方法都可以让物体位置偏移

//位置偏移
// cube.position.x = 2
cube.position.set(2,0,0)

那么注意,跟平面的前端布局一样,当没有父盒子元素时平面的元素里最大的就是body,所有外边距、内边距啥的都是以body为参照;这里也一样,没有父元素时,最大就是场景,元素位置以世界坐标轴为参照;但是当有了父盒子之后,子盒子的参照物就成了父盒子

这里讲一下怎么添加父盒子

//创建几何体
const geometry = new THREE.BoxGeometry(1,1,1);

//设置材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const father_material = new THREE.MeshBasicMaterial({ color: 0xff9800 })//这里为了区分父子,给父亲也提供一个新的材质

// 创建网格
const cube = new THREE.Mesh(geometry, material);
const fatherCube = new THREE.Mesh(geometry, father_material);//创建父元素,还是由原来的几何体组成,只不过变了新材质

//位置偏移
cube.position.x = 2
fatherCube.position.x = 0 //这里初始化父元素在x轴0的位置

//将网格添加到场景
// scene.add(cube)
scene.add(fatherCube) //现在在场景里添加的是父元素
fatherCube.add(cube) //在父元素里添加子元素

3、缩放大小

用“网格.scale(x值,y值,z值)”可以放大缩小物体

//放大缩小
cube.scale.set(2,2,2)

跟位移一样,它也是相对的,如果把父元素变大或缩小,那么子元素也会参照父元素的大小与之改变

cube.scale.set(2,2,2)
fatherCube.scale.set(0.5,0.5,0.5)

4、旋转

用“网格.ratation.x”、“网格.ratation.y”、“网格.ratation.z”就可以旋转了,在前面的demo里我们也利用这个进行了自动旋转

cube.rotation.x = Math.PI / 4 // Π/4 就是旋转 45°

同样,相对父元素的

cube.rotation.x = Math.PI / 4 // Π/4 就是旋转 45°
fatherCube.rotation.x = - (Math.PI / 4)

5、响应式画布以及全屏控制

当我们要缩放浏览器窗口、或者不同的浏览器有不同的尺寸时,我们就需要及时调整场景以及相机的大小宽高比

物品们只需要设置一个对window的监听器,当window窗口出发了“resize”更改大小的事件时,及时的更新场景以及相机大小

//监听窗口已完成响应式画布
window.addEventListener('resize', ()=>{
  //更新变化场景大小
  scene.setSize(window.innerWidth , innerHeight);
  //更新相机宽高比
  camera.aspect = window.innerWidth / window.innerHeight;
  //更新相机投影矩阵
  camera.updateProjectionMatrix();
})

然后我们还可以设置一个按钮来实现全屏的效果

//全屏控制
//新建一个按钮
var btn = document.createElement("button")
//设置一下按钮的样式
btn.innerHTML = '点击全屏'
btn.style.width = '180px'
btn.style.height = '80px'
btn.style.position = 'absolute'
btn.style.top = '10px'
btn.style.left = '10px'
btn.style.zIndex = '999'
btn.onclick = function(){
  //请求全屏
  document.body.requestFullscreen();
}
//最后追加到body中
document.body.appendChild(btn);

还有退出全屏(其实摁Esc就够了,有也行没有也行)

//这是退出全屏
var btn2 = document.createElement("button")
//设置一下按钮的样式
btn2.innerHTML = '退出全屏'
btn2.style.width = '180px'
btn2.style.height = '80px'
btn2.style.position = 'absolute'
btn2.style.top = '10px'
btn2.style.left = '300px'
btn2.style.zIndex = '999'
btn2.onclick = function(){
  //退出全屏(当然其实摁Esc就够了)
  document.exitFullscreen()
}
//最后追加到body中
document.body.appendChild(btn2);

当然你如果觉得太麻烦懒得记的话,也可以先复制保存,以后要用到的时候直接复制粘贴就好了,反正就是一个响应式画布以及全屏而已嘛

四、lil-GUI便捷可视化操作

前面我们学的内容里,位移、旋转、全屏按钮......都贼几把麻烦,要写一堆代码,那么用了lil-GUI就可以实现可视化操作

1、先导入

//导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";

2、然后创建一个对象,里面包含我们想要执行的一些函数方法

比如我们把前面全屏的操作删掉,用一个叫eventObj的对象装起来

let eventObj = {
  FullScreean(){
    document.body.requestFullscreen()
  },
  exitFullScreen(){
    document.exitFullscreen()
  }
}

3、然后创建GUI对象

然后调用GUI对象的add方法,把对象传入第一个参数,要执行的方法传到第二个参数(注意是字符串)

const gui = new GUI()
gui.add(eventObj , "FullScreean")
gui.add(eventObj , "exitFullScreen")

这里就多了可视化按钮

还可以换成中文的按钮

gui.add(eventObj , "FullScreean").name('全屏')
gui.add(eventObj , "exitFullScreen").name('退出全屏')

4、改变样式的方法

还可以操控位移、缩放、旋转等等,不过注意,因为【网格.位移】、【网格.缩放】、【网格.旋转】这些都是对象,所以就不用单独创建一个对象来给gui添加,直接添加

第一种写法:直接在GUI.add()方法里传四个参数

第一个参数是【网格.位移】、【网格.缩放】、【网格.旋转】这些操作

第二个参数是“x”、“y”、“z”

第三个参数和第四个参数是可变化的范围界限

例子:

gui.add(cube.position, "x", -5, 5).name('绿色盒子x轴平移')

第二种写法:在GUI.add()方法里传两个参数,然后.最小值().最大值().变化频数()......

例子:

gui.add(cube.position, "x").min(-5).max(5).step(1).name('绿色盒子x轴平移')
gui.add(cube.position, "y").min(-5).max(5).step(1).name('绿色盒子y轴平移')
gui.add(cube.position, "z").min(-5).max(5).step(1).name('绿色盒子z轴平移')

然后有的时候我们需要把缩放、平移......这些操作都包装一下,就可以调用gui.addFolder()来创建一个文件夹,然后用【文件夹.add()】的方式来操作

//第一个文件夹分类
let folder1 = gui.addFolder('绿色立方体的变化')
folder1.add(cube.position, "x").min(-5).max(5).step(1).name('绿色盒子x轴平移')
folder1.add(cube.position, "y").min(-5).max(5).step(1).name('绿色盒子y轴平移')
folder1.add(cube.position, "z").min(-5).max(5).step(1).name('绿色盒子z轴平移')
folder1.add(cube.rotation, "x").min(0).max(2*Math.PI).step(0.01).name('绿色盒子x轴旋转')
folder1.add(cube.rotation, "y").min(0).max(2*Math.PI).step(0.01).name('绿色盒子y轴旋转')
folder1.add(cube.rotation, "z").min(0).max(2*Math.PI).step(0.01).name('绿色盒子z轴旋转')
folder1.add(cube.scale, "x").min(-5).max(5).step(1).name('绿色盒子x轴缩放')
folder1.add(cube.scale, "y").min(-5).max(5).step(1).name('绿色盒子y轴缩放')
folder1.add(cube.scale, "z").min(-5).max(5).step(1).name('绿色盒子z轴缩放')


//第二个文件夹分类
let folder2 = gui.addFolder('橙色立方体的变化')
folder2.add(fatherCube.position, "x").min(-5).max(5).step(1).name('橙色盒子x轴平移')
folder2.add(fatherCube.position, "y").min(-5).max(5).step(1).name('橙色盒子y轴平移')
folder2.add(fatherCube.position, "z").min(-5).max(5).step(1).name('橙色盒子z轴平移')
folder2.add(fatherCube.rotation, "x").min(0).max(2*Math.PI).step(0.01).name('橙色盒子x轴旋转')
folder2.add(fatherCube.rotation, "y").min(0).max(2*Math.PI).step(0.01).name('橙色盒子y轴旋转')
folder2.add(fatherCube.rotation, "z").min(0).max(2*Math.PI).step(0.01).name('橙色盒子z轴旋转')
folder2.add(fatherCube.scale, "x").min(-5).max(5).step(1).name('橙色盒子x轴缩放')
folder2.add(fatherCube.scale, "y").min(-5).max(5).step(1).name('橙色盒子y轴缩放')
folder2.add(fatherCube.scale, "z").min(-5).max(5).step(1).name('橙色盒子z轴缩放')
folder2.add(father_material, "wireframe").name('橙盒子材质为线框模式')

5、操作完之后返回数据

那我们做这些可不是为了好玩就没了啊,我们通过可视化操作调好了位置之后,得知道这个位置在哪啊,那就直接调用它们的:onChange、onFinishChange两个触发事件

顾名思义,onChange就只要变化了就实时触发返回信息,而onFinishChange只有当最后停止了操作,才会触发返回值,看例子

onChange

folder1
  .add(cube.position, "x")
  .min(-5)
  .max(5)
  .step(1)
  .name('绿色盒子x轴平移')
  .onChange( value => {
    console.log(`现在绿盒子沿X轴平移到了:${value}`)
  })

onFinishChange

folder1
  .add(cube.scale, "z")
  .min(-5)
  .max(5)
  .step(1)
  .name('绿色盒子z轴缩放')
  .onFinishChange( value => {
    console.log(`最终确定绿盒子向y轴缩放到:${value}`)
  })

6、调整材质

我们还可以通过add方法传入material材质对象、以及"wireframe"属性来控制是否显示线框材质,(“wireframe”是布尔值,true就是开启)

folder1.add(material, "wireframe").name('绿盒子材质为线框模式')
folder2.add(father_material, "wireframe").name('橙盒子材质为线框模式')

7、调整颜色

调颜色有一丢丢麻烦,首先要定义装有颜色变量的一个对象,然后通过【addColor】(注意不是单单只是add了),传入对象参数以及颜色变量属性,就可以更改颜色了

而要想该完的颜色能显示出来,我们还得调整【材质】,【网格.材质.color.set()】才可以调整成功颜色(这里注意,直接用材质的固定名material,而不是相对于个别盒子设置的自定义材质的名字!!!)

比如我前面设置了父盒子的材质是father_material就不行,只能是material

let colorParams = {
  cubeColor: '0x00ff00',
  fatherCubeColor: '0xff9800'
}
folder1
  .addColor(colorParams, 'cubeColor')
  .name('子盒子的颜色调整')
  .onChange( value => {
    cube.material.color.set(value)
  })

folder2
  .addColor(colorParams, 'fatherCubeColor')
  .name('父盒子的颜色调整')
  .onChange( value => {
    fatherCube.material.color.set(value)
  })

下一篇深讲几何体

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1861627.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大厂薪资福利篇第四弹:字节跳动

欢迎来到绝命Coding&#xff01; 今天继续更新大家最关心的 大厂薪资福利系列&#xff01; 往期分享&#xff1a; 福利开水喝不完&#xff1f;大厂薪资福利篇&#xff01;美团 职场文化发源地&#xff1f;大厂薪资福利篇&#xff01;阿里巴巴 给这么多&#xff01;还能带宠物上…

Adams Flex模块功能介绍

通过该教程对Adams Flex模块有基本的认知&#xff0c;为以后使用柔性体进行刚柔耦合做好基础学习。 有需要购买的可以邮箱&#xff08;digitaltwins126.com&#xff09;或站内信联系&#xff0c;谢谢&#xff01;

机器学习之数学基础(七)~过拟合(over-fitting)和欠拟合(under-fitting)

目录 1. 过拟合与欠拟合 1.1 Preliminary concept 1.2 过拟合 over-fitting 1.3 欠拟合 under-fitting 1.4 案例解析&#xff1a;黑天鹅 1. 过拟合与欠拟合 1.1 Preliminary concept 误差 经验误差&#xff1a;模型对训练集数据的误差。泛化误差&#xff1a;模型对测试…

基于SpringBoot的“智慧食堂”管理系统设计与实现

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootVue 工具&#xff1a;IDEA/Eclipse、Navicat、Maven 系统展示 首页 用户管理界面 菜品…

超炫酷, 不用学前端也能自己做网页!这个Python库,3分钟内复刻GPT WEB应用

大家好&#xff0c;我是海鸽。 今天&#xff0c;我要和大家分享如何将请求 GPT 的案例&#xff0c;快速“复刻”成 GPT 网页版。这不仅简单&#xff0c;而且对于我们这些后端开发者来说&#xff0c;简直是福音&#xff01; 先睹为快 看看这个界面&#xff0c;是不是感觉很熟…

更适合敏感口腔的护理牙刷

最近在用一款清九野小红盾舒敏牙刷&#xff0c;感觉它很适合牙龈敏感的人&#xff0c;让刷牙体验有了显著的提升。这款牙刷的柔软刷毛和精细设计让我的刷牙过程变得轻松愉快。它的内外圈双重植毛技术&#xff0c;在清洁牙齿的同时&#xff0c;还能深入牙缝&#xff0c;温和地去…

js实现数据加密,jwt加密方式

一个简单的数据加密 const crypto require("crypto");// 普通的数据加密 function sign(msg,key){ // 原始信息&#xff0c;密钥 String// "sha256" :加密的算法&#xff0c;key :密钥&#xff0c;msg :要加密的信息&#xff0c;"hex" :转成16…

攻击者开始使用 XLL 文件进行攻击

近期&#xff0c;研究人员发现使用恶意 Microsoft Excel 加载项&#xff08;XLL&#xff09;文件发起攻击的行动有所增加&#xff0c;这项技术的 MITRE ATT&CK 技术项编号为 T1137.006。 这些加载项都是为了使用户能够利用高性能函数&#xff0c;为 Excel 工作表提供 API …

【Mac】XnViewMP for Mac(图片浏览查看器)及同类型软件介绍

软件介绍 XnViewMP 是一款多功能、跨平台的图像查看和管理软件&#xff0c;适用于 macOS、Windows 和 Linux 系统。它是经典 XnView 软件的增强版本&#xff0c;更加现代化且功能更强大。XnViewMP 支持数百种图像格式&#xff0c;并提供多种图像处理工具&#xff0c;使其成为摄…

基于Java微信小程序自驾游拼团设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

全省高等职业学校大数据技术专业建设暨专业质量监测研讨活动顺利开展

6月21日&#xff0c;省教育评估院在四川邮电职业技术学院组织开展全省高等职业学校大数据技术专业建设暨专业质量监测研讨活动。省教育评估院副院长赖长春&#xff0c;四川邮电职业技术学院党委副书记、校长冯远洪&#xff0c;四川邮电职业技术学院党委委员、副校长程德杰等出席…

集群分布式储存

硬件&#xff1a; 存储柜 软件 &#xff1a; software define storage 分布式存储 是一种独特的系统架构由一组能够通过网络连通&#xff0c;为了完成共同任务而协调任务的计算机节点组成分布式是为了使用廉价的普通的计算机完成复杂的计算和存储任务目的就是利用更多的机…

Sam Altman:从少儿奇才到OpenAI掌舵人

自2022年底发布了ChatGPT以来&#xff0c;OpenAI及其首席执行官Sam Altman迅速成为科技界的焦点人物。Altman的崛起并非偶然&#xff0c;而是长期以来不断追求权力和创新的结果。本文将回顾Altman的成长历程&#xff0c;探索他如何一步步走向OpenAI的顶峰。 童年与教育背景 S…

JavaWeb系列十九: jQuery的DOM操作 上

查找节点, 修改属性 查找属性节点: 查找到所需要的元素之后, 可以调用jQuery对象的attr()方法用来 设置/返回 它的各种属性值 设置属性值 $(“img”).attr(“width”, “300”);返回属性值 $(“img”).attr(“width”); 创建节点 创建节点: 使用jQuery的工厂函数$(): $(html标…

WPS复制后转置粘贴

1. WPS复制后转置粘贴 复制-》右键-》顶部第一行-》粘贴行列转置&#xff0c;如下图&#xff1a; 2. Excel office365 本地版 2. Excel office365 在线版

Linux下执行C++程序

编译 一步到位的编译&#xff1a; g cpp文件 # 此时默认生成一个a.out的可执行程序 g cpp文件 -o 可执行程序名称 # 此时可以自定义可执行程序的名称 分步编译&#xff1a; # 1.预处理(Pre_processing) # 功能&#xff1a;将源程序头文件展开、删除注释、宏替换 g -E cpp文件…

Redis 7.x 系列【6】数据类型之字符串(String)

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 前言2. 常用命令2.1 SET2.2 GET2.3 MSET2.4 MGET2.5 GETSET2.6 STRLEN2.7 SETEX2.8…

0625_ARM2

练习&#xff1a; 汇编实现1-100累加&#xff0c;结果保存在r0 .text .global _start start:mov r0,#0mov r1,#1b loop loop:add r0,r0,r1add r1,r1,#1cmp r1,#101bne loop .end思维导图&#xff1a;

EPLAN页属性批量修改

在使用EPLAN软件时&#xff0c;项目数据量大的时候&#xff0c;需要修改页属性&#xff0c;每一页单独修改会很繁琐&#xff0c;这里介绍如何批量修改页属性&#xff1a; 在EPLAN里点击“工具----从外部编辑属性------导出数据” 这里按图示可定义保存位置&#xff0c;和输出方…