本段内容会写在0篇以外所有的,本人所编写的Threejs教程中
对,学习ThreeJS有捷径
当你有哪个函数不懂的时候,第一时间去翻一翻文档
当你有哪个效果不会做的时候,第一时间去翻一翻所有的案例,也许就能找到你想要的效果
最重要的一点,就是,绝对不要怕问问题,越怕找找别人问题,你的问题就会被拖的越久
如果你确定要走WebGL/ThreeJS的开发者路线的话,以下行为可以让你更快的学习ThreeJS
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<style>
canvas{
display: block;
}
body {
margin: 0;
overscroll-behavior: none;
}
#btns{
position: absolute;
top:10%;
width: 500px;
height: 100px;
left: 50%;
transform:translateX(-50%);
}
style>
head>
<body>
<div id="btns">div>
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js">script>
<script type="importmap">
{
"imports": {
"three": "../three.js-master/build/three.module.js"
}
}
script>
<script type="module">
import * as THREE from '../three.js-master/build/three.module.js';
import {OrbitControls} from "../three.js-master/examples/jsm/controls/OrbitControls.js";
import {GUI} from "../three.js-master/examples/jsm/libs/lil-gui.module.min.js"
import Stats from "../three.js-master/examples/jsm/libs/stats.module.js"
let scene,renderer,camera,orbitControls;
let stats,gui;
let mesh;
function init(){
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({
alpha:true,
antialias:true
});
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,1,1000);
camera.position.set(10,10,10);
orbitControls = new OrbitControls(camera,renderer.domElement);
let helper = new THREE.GridHelper(50,10);
scene.add(helper);
stats= new Stats();
document.body.appendChild(stats.dom);
gui = new GUI();
}
function addMesh(){
let geometry = new THREE.BoxGeometry(1,1,1);
let material = new THREE.MeshBasicMaterial({
color:"#ff0000"
});
mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
}
function addGUI() {
gui.add(mesh.position,"x",-10,10).step(0.5).name("位置x");
gui.add(mesh.position,"y",-10,10).step(0.5).name("位置y");
gui.add(mesh.position,"z",-10,10).step(0.5).name("位置z");
let params = {
rx:0,
ry:0,
rz:0
};
gui.add(params,"rx",-Math.PI,Math.PI).step(Math.PI/180).name("旋转x").onChange((value)=>{
mesh.rotation.x = params.rx;
});
gui.add(params,"ry",-Math.PI,Math.PI).step(Math.PI/180).name("旋转y").onChange((value)=>{
mesh.rotation.y = params.ry;
});
gui.add(params,"rz",-Math.PI,Math.PI).step(Math.PI/180).name("旋转z").onChange((value)=>{
mesh.rotation.z = params.rz;
});
}
function render(){
renderer.render(scene,camera);
requestAnimationFrame(render);
stats.update();
}
init();
addMesh();
addGUI();
render();
script>
body>
html>
当你运行案例时,你发现,左上角有了一个小窗口,并且显示着你的当前刷新率,笔者这里使用的是144hz的屏幕,所以显示的刷新率为144,常见刷新率为60,75,120,144等
刷新率会直接反馈给你你的项目运行是否卡顿,如果你的显示器常驻刷新率是60帧,当你加载了过于复杂的模型后,你的帧率降到了60以下,甚至低于30的时候,那么,这个模型在你的设备和同等级及以下水平的设备下运行,会降低用户的实际使用体验
一般来说,做WebGL项目要考虑最低的设备等级,你的用户群如果是全民,那么你在开发时,就应考虑【如何使最低配置的设备能流畅运行你的程序】,左上角的这个插件,就是一个很好的检测程序运行流畅度的插件
右上角多了一个比较大的窗口,你发现,拖动中间的蓝色小杆,让它左右移动,可以让中间的模型发生变化,比如位置x,可以让模型沿着x轴移动,旋转x可以让模型沿着x轴旋转等
其他功能均与上一个案例一致
多数代码已经在上一篇解析完毕,本篇仅针对新出现的代码进行解析
文件位置如图所示,对应的ES5版本在examples/js/libs/下
import {GUI} from "../three.js-master/examples/jsm/libs/lil-gui.module.min.js"
import Stats from "../three.js-master/examples/jsm/libs/stats.module.js"
let stats,gui;
function init(){
......
stats= new Stats();
document.body.appendChild( stats.dom );
gui = new GUI();
}
...
render(){
......
stats.update();
}
按上述写法引入对应的这两个文件后,先全局创建对应的变量,然后在初始化阶段初始化它们即可
构造器:new Stats() 没啥说的,就是一个new,创建完记得把dom添加到你的html中,上述代码是添加到了body中 ,添加后,你的左上角就会出现上面效果图中的刷新率检测方块
函数:stats.update(); 因为这个东西要时刻检测帧率,所以需要放到requestAnimationFrame绑定的函数中,否则不会生效
左上角的方块有三种模式,使用鼠标点击方块即可切换,目前我们仅使用蓝色的即可
构造器 new GUI() lil-gui,升级于Dat.GUI,如果想要找一套系统的使用教程,请搜索Dat.GUI,或者去lil-gui的github地址或官网,其中也有相关的文档和教程
lil-gui的官方网站 https://lil-gui.georgealways.com/
这个构造器相对来说就只new一下就ok了,右上角会直接出现一个黑色的小方框,但是,仅仅只new一个它,它什么也不会显示,会显示成这样
function addGUI() {
gui.add(mesh.position,"x",-10,10).step(0.5).name("位置x");
gui.add(mesh.position,"y",-10,10).step(0.5).name("位置y");
gui.add(mesh.position,"z",-10,10).step(0.5).name("位置z");
let params = {
rx:0,
ry:0,
rz:0
};
gui.add(params,"rx",-Math.PI,Math.PI).step(Math.PI/180).name("旋转x").onChange((value)=>{
mesh.rotation.x = params.rx;
});
gui.add(params,"ry",-Math.PI,Math.PI).step(Math.PI/180).name("旋转y").onChange((value)=>{
mesh.rotation.y = params.ry;
});
gui.add(params,"rz",-Math.PI,Math.PI).step(Math.PI/180).name("旋转z").onChange((value)=>{
mesh.rotation.z = params.rz;
});
}
以上展示了两种,向GUI添加控制块的方法
方法1:直接操作物体的对应属性
gui.add(对象,属性名,属性值下限,属性值上限) 用于添加一个控制器
比如我这里,想要控制小方块的位置,那么:
我们的对象是:mesh.position ,positions一个THREE.Vector3对象,这里我们要拿到最小的对象
属性是:x,Vector3对象有三个基础属性,分别是x,y,z
属性值下限:默认为负无限大,但是我们做项目调试的话,尽量设置一个下限值和上限值
属性值上限:同上
接下来,我需要为它添加步长,拖动一下让物体移动多少距离
gui.add().step(0.5) 这样,我们在gui上拖动,每触发一次mousemove,方块会移动0.5个单位
英文的看不懂或者不会翻译怎么办?那我们添加一个中文的标签名
gui.add().name(“位置x”)
完整的写法下来就是
gui.add( mesh.position,“x”, -10, 10 ).step(0.5).name(“位置x”)
翻译下来为:
向GUI添加一组控制器,控制mesh(小方块)位置对象的x属性,使控制器可以将x这个属性值,修改为-10~10的数字,每一次操作会改变0.5的值,为控制块设定名称为:“位置x”
将x,y,z三个属性都添加了控制器之后,我们就可以使用GUI控制方块了
但是我们还有第二种写法
首先创建一个对象
let params = {
x:0,
y:0,
z:0
};
同样的方式,我们控制这个对象,而非直接控制模型的属性
gui.add( params,“x”, -10, 10 ).step(0.5).name(“位置x”)
这样我们并不能直接控制模型,我们还需要下一步操作
gui.add().onChange( function(value){} )
使用onChange函数,可以让我们的属性变的更实用
上面同样的段落,我们可以这样写
gui.add( params,"x", -10, 10 ).step(0.5).name("位置x").onChange( value=>{
mesh.position.x = value;
} );
这样写的好处是,你可以用一个params控制多个属性,且逻辑上比较清晰
上面代码块中,关于mesh的旋转的控制,就是由gui控制param,进一步控制mesh.rotation来进行旋转的
ThreeJS中使用的旋转角度均为弧度制,代码中设置了下限为180度,在js中使用Math.PI来表示弧度制的180度,弧度制忘了的同学可以去搜一下做个基本复习
这样,添加旋转的那一部分代码就讲完了