Address:Three.js中文网 (webgl3d.cn)
Author:方越 50041588
Date:2024-07-19
第一个3D案例—创建3D场景
创建3D场景对象Scene:
const scene = new THREE.Scene();
创建一个长方体几何对象Geometry:
const geometry = new THREE.BoxGeometry(100, 100, 100);
常见几何体对象如下:
创建一个材质对象Material:
const material = new THREE.MeshBasicMaterial({ color: 0xff0000,//0xff0000设置材质颜色为红色 });
常见材质对象如下:
创建一个网格模型对象Mesh,两个参数分别为几何体geometry、材质material:
const mesh = new THREE.Mesh(geometry, material);
设置网格模型在三维空间中的位置坐标,默认是坐标原点:
mesh.position.set(0,10,0);
把网格模型mesh添加到三维场景scene中:
scene.add(mesh);
第一个3D案例—透视投影相机
实例化一个透视投影相机对象:
const camera = new THREE.PerspectiveCamera();
相机在Three.js三维坐标系中的位置,根据需要设置相机位置具体值:
camera.position.set(200, 200, 200);
相机观察目标指向Threejs 3D空间中某个位置:
camera.lookAt(0, 0, 0); //坐标原点 camera.lookAt(0, 10, 0); //y轴上位置10 camera.lookAt(mesh.position);//指向mesh对应的位置
透视投影相机的四个参数fov, aspect, near, far构成一个四棱台3D空间,被称为视锥体,只有视锥体之内的物体,才会渲染出来,视锥体范围之外的物体不会显示在Canvas画布上:
// 30:视场角度(fov) // width / height:Canvas画布宽高比(aspect) // 1:近裁截面(near) // 3000:远裁截面(far) camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
第一个3D案例—渲染器
创建渲染器对象:
const renderer = new THREE.WebGLRenderer();
定义threejs输出画布的尺寸(单位:像素px):
const width = 800; //宽度 const height = 500; //高度 renderer.setSize(width, height); //设置three.js渲染区域的尺寸
执行渲染操作:
renderer.render(scene, camera);
Canvas画布插入到任意HTML元素中:
<div id="webgl" style="margin-top: 200px;margin-left: 100px;"></div>
document.getElementById('webgl').appendChild(renderer.domElement);
三维坐标系-加强三维空间认识
辅助观察的坐标系AxesHelper:
const axesHelper = new THREE.AxesHelper(150); scene.add(axesHelper);
设置材质半透明,这样可以看到坐标系的坐标原点:
const material = new THREE.MeshBasicMaterial({ color: 0x0000ff, //设置材质颜色 transparent:true,//开启透明 opacity:0.5,//设置透明度 });
光源对物体表面影响
MeshBasicMaterial不受光照影响:
const material = new THREE.MeshBasicMaterial();
MeshLambertMaterial受光照影响:
const material = new THREE.MeshLambertMaterial();
光源对物体表面影响关系如下:
常见光源分类如下:
点光源:两个参数分别表示光源颜色和光照强度:
// 参数1:0xffffff是纯白光,表示光源颜色 // 参数2:1.0,表示光照强度,可以根据需要调整 const pointLight = new THREE.PointLight(0xffffff, 1.0);
光源衰减属性decay默认值是2.0,如果不希望衰减可以设置为0.0:
pointLight.decay = 0.0;//设置光源不随距离衰减
设置点光源位置:
pointLight.position.set(400, 0, 0);//点光源放在x轴上
点光源添加到场景中:
scene.add(pointLight);
相机控件OrbitControls
引入轨道控制器扩展库OrbitControls.js:
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
设置相机控件轨道控制器OrbitControls:
const controls = new OrbitControls(camera, renderer.domElement); // 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景 controls.addEventListener('change', function () { renderer.render(scene, camera); //执行渲染操作 });//监听鼠标、键盘事件
平行光与环境光
点光源辅助观察:
const pointLightHelper = new THREE.PointLightHelper(pointLight, 10); scene.add(pointLightHelper);
环境光:没有特定方向,整体改变场景的光照明暗:
const ambient = new THREE.AmbientLight(0xffffff, 0.4); scene.add(ambient);
平行光:沿着特定方向发射:
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // 设置光源的方向:通过光源position属性和目标指向对象的position属性计算 directionalLight.position.set(80, 100, 50); // 方向光指向对象网格模型mesh,可以不设置,默认的位置是0,0,0 directionalLight.target = mesh; scene.add(directionalLight);
可视化平行光DirectionalLightHelper:
const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5, 0xff0000); scene.add(dirLightHelper);
动作渲染循环
requestAnimationFrame实现周期性循环执行,用其实现threejs旋转动画:
const renderer = new THREE.WebGLRenderer(); renderer.setSize(width, height); document.body.appendChild(renderer.domElement); function render() { renderer.render(scene, camera); //执行渲染操作 mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度 requestAnimationFrame(render);//请求再次执行渲染函数render,渲染下一帧 } render();
Canvas画布布局和全屏
全屏渲染canvas:
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px) const width = window.innerWidth; //窗口文档显示区的宽度作为画布宽度 const height = window.innerHeight; //窗口文档显示区的高度作为画布高度 const renderer = new THREE.WebGLRenderer(); document.body.appendChild(renderer.domElement);
onresize 事件会在窗口被调整大小时发生,实现宽高度动态变化:
window.onresize = function () { // 重置渲染器输出画布canvas尺寸 renderer.setSize(window.innerWidth, window.innerHeight); // 全屏情况下:设置观察范围长宽比aspect为窗口宽高比 camera.aspect = window.innerWidth / window.innerHeight; // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix // 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源) // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵 camera.updateProjectionMatrix(); };
stats查看threejs渲染帧率
引入性能监视器stats.js:
import Stats from 'three/addons/libs/stats.module.js';
创建stats对象:
const stats = new Stats(); //stats.domElement:web页面上输出计算结果,一个div元素, document.body.appendChild(stats.domElement); function render() { //requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间 stats.update(); renderer.render(scene, camera); //执行渲染操作 requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧 } render();
通过setMode()方法的参数mode的数值设置首次打开页面,测试结果的显示模式:
//stats.domElement显示:渲染帧率 刷新频率,一秒渲染次数 stats.setMode(0);//默认模式 //stats.domElement显示:渲染周期 渲染一帧多长时间(单位:毫秒ms) stats.setMode(1);
阵列立方体和相机适配体验
相机控件OrbitControls会影响lookAt设置,注意手动设置OrbitControls的目标参数:
const controls = new OrbitControls(camera, renderer.domElement); // 相机控件.target属性在OrbitControls.js内部表示相机目标观察点,默认0,0,0 // console.log('controls.target', controls.target); controls.target.set(1000, 0, 1000); controls.update();//update()函数内会执行camera.lookAt(controls.target)
Threejs常见几何体简介
常见几何体对象:
//BoxGeometry:长方体 const geometry = new THREE.BoxGeometry(100, 100, 100); // SphereGeometry:球体 const geometry = new THREE.SphereGeometry(50); // CylinderGeometry:圆柱 const geometry = new THREE.CylinderGeometry(50,50,100); // PlaneGeometry:矩形平面 const geometry = new THREE.PlaneGeometry(100,50); // CircleGeometry:圆形平面 const geometry = new THREE.CircleGeometry(50);
Three.js的材质默认正面可见,反面不可见,对于矩形平面、圆形平面如果你想看到两面,可以设置side:
new THREE.MeshBasicMaterial({ side: THREE.FrontSide, //默认只有正面可见 }); new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, //两面可见 });
高光网格材质Phong
MeshPhongMaterial 可以提供一个镜面反射效果,可以类比你生活中拿一面镜子,放在太阳光下,调整角度,可以把太阳光反射到其它地方,如果反射光对着眼睛,也就是反射光线和视线平行的时候,会非常刺眼。
MeshLambertMaterial 对应的Mesh受到光线照射,没有镜面反射的效果,只是一个漫反射,也就是光线向四周反射。
// 模拟镜面反射,产生一个高光效果 const material = new THREE.MeshPhongMaterial({ color: 0xff0000, shininess: 20, //高光部分的亮度,默认30 specular: 0x444444, //高光部分的颜色 });
WebGL渲染器设置(锯齿模糊)
设置锯齿属性:
renderer.antialias = true;
获取你屏幕对应的设备像素比devicePixelRatio,告诉threejs,以免渲染模糊问题:
renderer.setPixelRatio(window.devicePixelRatio);
设置背景颜色:
renderer.setClearColor(0x444444, 1);
gui.js库(可视化改变三维场景)
引入dat.gui.js的一个类GUI:
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
实例化一个gui对象:
const gui = new GUI();
改变交互界面style属性:
gui.domElement.style.right = '0px'; gui.domElement.style.width = '300px';
执行gui的add()方法可以快速创建一个UI交互界面:
//创建一个对象,对象属性的值可以被GUI库创建的交互界面改变 const obj = { x: 30, }; // gui增加交互界面,用来改变obj对应属性 gui.add(obj, 'x', 0, 100);
gui调试界面2-颜色命名等
通过name()方法改变gui生成交互界面显示的内容:
gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度'); gui.add(directionalLight, 'intensity', 0, 2.0).name('平行光强度');
通过step()方法设置步长:
gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度').step(0.1);
当gui界面某个值的时候,onChange()方法就会执行,可以根据需要通过onChange()执行某些代码:
const obj = { x: 30, }; // 当obj的x属性变化的时候,就把此时obj.x的值value赋值给mesh的x坐标 gui.add(obj, 'x', 0, 180).onChange(function(value){ mesh.position.x = value; // 你可以写任何你想跟着obj.x同步变化的代码 // 比如mesh.position.y = value; });
通过addColor()方法生成颜色值改变的交互界面:
const obj = { color:0x00ffff, }; // .addColor()生成颜色值改变的交互界面 gui.addColor(obj, 'color').onChange(function(value){ mesh.material.color.set(value); });
gui调试3-下拉菜单、单选框
参数3和参数4,分别是一个数字,交互界面是一个鼠标可以拖动的拖动条,可以在一个区间改变属性的值:
// 参数3、参数4数据类型:数字(拖动条) gui.add(obj, 'x', 0, 180).onChange(function (value) { mesh.position.x = value; });
参数3是一个数组,生成交互界面是下拉菜单:
const obj = { scale: 0, }; // 参数3数据类型:数组(下拉菜单) gui.add(obj, 'scale', [-100, 0, 100]).name('y坐标').onChange(function (value) { mesh.position.y = value; });
参数3是一个对象,生成交互界面是下拉菜单:
const obj = { scale: 0, }; // 参数3数据类型:对象(下拉菜单) gui.add(obj, 'scale', { left: -100, center: 0, right: 100 }).name('位置选择').onChange(function (value) { mesh.position.x = value; });
参数3是布尔值,生成交互界面是单选框:
const obj3 = { bool: false, }; // 改变的obj属性数据类型是布尔值,交互界面是单选框 gui.add(obj3, 'bool').name('是否旋转').onChange(function (value) { // 点击单选框,控制台打印obj.bool变化 console.log('obj.bool',value); });
gui.js库(分组)
new GUI() 实例化一个gui对象,默认创建一个总的菜单,通过gui对象的addFolder()方法可以创建一个子菜单,当你通过GUI控制的属性比较多的时候,可以使用addFolder()进行分组。addFolder()返回的子文件夹对象,同样具有gui对象的add()、onChange()、addColor()等等属性。
// 创建材质子菜单 const matFolder = gui.addFolder('材质'); matFolder.close(); // 材质颜色color matFolder.addColor(obj, 'color').onChange(function(value){ material.color.set(value); });
gui对象创建的总菜单或gui.addFolder()创建的子菜单都可以用代码控制交互界面关闭或开展状态:
gui.open(); // 展开菜单 gui.close(); // 关闭菜单