《Learning Three.js》学习(1)使用Three.js创建三维场景
前言:
WebGL可以直接使用显卡资源从JS创建三维场景;可用Three.js优化该复杂过程。
"构建骨架"
理论知识:
我们分别需要Three.js和TrackballControls.js两个文件作为支持库。
THREE中需要场景、相机和渲染器来构建三维元素。
scene - 场景是一个容器,用于保存、跟踪所要渲染的物体和使用的光源。
scene是渲染物体的基础。
对于物体对象的基本操作个人概括为:1、创建物体的尺寸 2、添加样式-颜色/透明度/材质 3、将尺寸和样式融合并“覆盖原物体” 4、设置物体的“外方位元素” 5、添加到场景中
camera - 相机将决定哪些东西会被渲染(可视)
renderer - 渲染场景和相机视角, renderer.render(scene, camera);
最后将渲染挂载到div标签上,告诉渲染器使用指定摄像机渲染场景
接下来我们实现下表中的对象
sphereGeometry方法
效果:
由于设置了旋转角,默认z轴垂直平面向外。
代码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>/* 确保容器填满整个视口 */#webgl-output {width: 100%;height: 100%;display: block;}</style><script type="text/javascript" charset="UTF-8" src="./libs/three.js"></script><script type="text/javascript" charset="UTF-8" src="./libs/TrackballControls.js"></script>
</head>
<body><!-- 添加一个容器元素 --><div id="webgl-output"></div><script> const init = () =>{var scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);var renderer = new THREE.WebGLRenderer();// 设置背景颜色renderer.setClearColor(new THREE.Color(0xEEEEEE));// 设置渲染区域的大小renderer.setSize(window.innerWidth, window.innerHeight);// 创建坐标轴var axes = new THREE.AxisHelper(20);scene.add(axes);// 创建平面var planeGeometry = new THREE.PlaneGeometry(60, 20);// 创建材质var planeMaterial = new THREE.MeshBasicMaterial({color: 0xcccccc});// 组装var plane = new THREE.Mesh(planeGeometry, planeMaterial);// 平面旋转plane.rotation.x = -0.5 * Math.PI;// 平面位置plane.position.x = 15;// plane.position.set(15, 0, 0);// 添加到场景scene.add(plane);// 创建立方体var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);var cubeMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true});var cube = new THREE.Mesh(cubeGeometry, cubeMaterial); cube.position.set(-4, 3, 0);scene.add(cube)// 创建球体var sphereGeometry = new THREE.SphereGeometry(4,20,20)var sphereMaterial = new THREE.MeshBasicMaterial({color: 0x7777ff, wireframe: true});var sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);sphereMesh.position.set(20, 4, 2);scene.add(sphereMesh);// 设置相机位置camera.position.set(-30, 40, 30);camera.lookAt(scene.position);// 将渲染器添加到domdocument.getElementById('webgl-output').appendChild(renderer.domElement);// 渲染renderer.render(scene, camera);}init();</script>
</body>
</html>
"添加羽毛"
添加材质、光源、阴影效果。
理论知识:
通过castShadow属性设置,THREE.js阴影效果被开启,并使用shadow.mapSize,shadow.camera.far,shadow.camera.near来控制阴影精细化程度。
注意光源的显示必须要合适的材料,效果才能显示,eg:MeshBasicMaterial材料无效果
可以改为MeshLambertMaterial、MeshPhysicalMaterial、MeshStandardMaterial、MeshPhongMaterail(已废弃)
阴影贴图大小仅为512x512,阴影贴图必须是2的幂
告诉渲染器需要阴影效果 renderer.shadowMap.enabled = true;
此时仍然无光影效果,需要明确指定谁投射阴影,谁接受阴影。
注意需要对光源的强度和随距离衰减情况进行修改,否则材质显示为黑色。
plane.recieveShadow = true
cube.castShadow = true
sphere.castShadow = true
定义产生阴影的光源,并不是所有的光源可以产生阴影,通过Three.SpotLight定义的光源能够产生阴影。下述代码渲染阴影。
spotLight.castShadow = true
效果:
代码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>/* 确保容器填满整个视口 */#webgl-output {width: 100%;height: 100%;display: block;overflow: hidden;}</style><script type="module">import * as THREE from '../build/three.module.js';window.THREE = THREE;</script>
</head>
<body><!-- 添加一个容器元素 --><div id="Stats-output"><div id="webgl-output"></div></div><script type="module"> const init = () => {var scene = new THREE.Scene();var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);var renderer = new THREE.WebGLRenderer();// 设置背景颜色renderer.setClearColor(new THREE.Color(0x000000));// 设置渲染区域的大小renderer.setSize(window.innerWidth, window.innerHeight);// 创建坐标轴var axes = new THREE.AxesHelper(20);scene.add(axes);// 创建平面var planeGeometry = new THREE.PlaneGeometry(60, 20);// 创建材质var planeMaterial = new THREE.MeshBasicMaterial({color: 0xecb514, side: THREE.DoubleSide});// 组装-创建格网var plane = new THREE.Mesh(planeGeometry, planeMaterial);// 平面旋转plane.rotation.x = -0.5 * Math.PI;// 平面位置plane.position.set(15, 0, 0);plane.receiveShadow = true;// 添加到场景scene.add(plane);// 创建立方体var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000, emissive: 0x000000, side: THREE.DoubleSide});var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);cube.position.set(-4, 3, 0);cube.castShadow = true;scene.add(cube);// 创建球体var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff, emissive: 0x000000, side: THREE.DoubleSide});var sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);sphereMesh.position.set(20, 4, 2);sphereMesh.castShadow = true;scene.add(sphereMesh);// 设置相机位置camera.position.set(-30, 40, 30);camera.lookAt(cube.position); // 改为看向立方体// 光照// 聚光灯const spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(-40, 60, -10);spotLight.shadow.mapSize.width = 1024;spotLight.shadow.mapSize.height = 1024;spotLight.shadow.camera.visible = false; // 不显示阴影相机spotLight.castShadow = true;spotLight.shadow.camera.far = 130;spotLight.shadow.camera.near = 40;spotLight.shadow.camera.fov = 50; // 设置阴影相机的视场角spotLight.intensity = 5; // 聚光灯强度spotLight.target = cube; // 设置聚光灯的目标spotLight.decay = 0.06; // 衰减scene.add(spotLight);// 添加阴影效果renderer.setClearColor(new THREE.Color(0x000000));renderer.setSize(window.innerWidth, window.innerHeight);renderer.shadowMap.type = THREE.PCFSoftShadowMap;// 设置更高的阴影贴图分辨率renderer.shadowMap.width = 2048; // 减少锯齿效果renderer.shadowMap.height = 2048;renderer.shadowMap.enabled = true;// 将渲染器添加到domdocument.getElementById('webgl-output').appendChild(renderer.domElement);}window.onload = init;</script>
</body>
</html>
"初试羽翼"
让场景中的物体动起来。
requestAnimationFrame()
稳定而连续的渲染场景
// 将渲染器添加到domdocument.getElementById('webgl-output').appendChild(renderer.domElement);// 渲染循环function renderScene() {requestAnimationFrame(renderScene);renderer.render(scene, camera);}renderScene()
添加帧数显示
代码:
// 添加帧数显示function initStates(type){var panelType = (typeof type !== 'undefined' && type) && (!isNaN(parseInt(type))) ? parseInt(type) : 0;var stats = new Stats();stats.showPanel(panelType);document.getElementById('Stats-output').appendChild(stats.dom);return stats;}
旋转立方体
效果:
代码:
// 立方体旋转
cube.rotation.x += 0.02;
cube.rotation.y += 0.02;
cube.rotation.z += 0.02;
弹跳球
效果:
代码:
const step = 0// 小球运动step += 0.04;sphereMesh.position.x = 20 + (10 * (Math.cos(step)));sphereMesh.position.y = 2 + (10 * Math.abs(Math.sin(step)));
GUI简化调试流程
代码:
// 创建待GUI控制的对象var controls = new function(){this.rotationSpeed = 0.02;this.bouncingSpeed = 0.03;}// 创建控制器var gui = new dat.GUI();gui.add(controls, 'rotationSpeed', 0, 0.5);gui.add(controls, 'bouncingSpeed', 0, 0.5);