个人博客地址: https://cxx001.gitee.io
前言
随着HTML5
的发布,我们可以通过WebGL
在浏览器上直接使用显卡资源来创建高性能的二维和三维图形,但是直接使用WebGL
编程来创建三维场景十分复杂而且还容易出问题。而使用Three.js
库可以简化这个过程,它对WebGL
做了进一步封装,降低了使用门槛。WebGL整体原理其实和OpenGL差不多,只是运行平台不同而已,它是基于OpenGL-ES绑定javascript而来。
底层关系: OpenGL ES => WebGL => Threejs
注: 本系列教程并不是用的threejs最新版,threejs官方一直在持续优化更新,可能有些API使用有些许变化。本系列教程重在学习理论,只有掌握这些后,后续关注官网最新版使用才能如鱼得水。
threejs官网: 点击这里
环境搭建
three.js
是一个javascript
库,所以环境搭建和我们web开发环境搭建一样。
-
编码IDE,我用的vscode, 下载地址。
-
搭建web服务器。
方式有很多,我用的nodejs的http-server模块。
// 全局安装 npm install -g http-server// 启动服务 http-server
-
访问示例程序。
对应示例程序目录下启动
http-server
,默认端口是8080。浏览器访问url,如:localhost:8080/chapter-01.html
入门示例程序
入门示例我们将使用three.js
库创建如下主要内容:
- html页面如何引入three.js库。
- 使用three.js创建场景、摄像机、光源、渲染物体。
- 给场景添加阴影和动画效果。
- 添加辅助库dat.GUI和stats.js 创建用户控制界面(动态调渲染参数)和场景渲染时的帧数。
本系列所有示例程序引用的外部库和资源下载地址:示例程序资源下载
<!-- chapter-01.html -->
<!DOCTYPE html><html><head><title>入门示例</title><!-- 引入three.js等相关库,three.js库引入有两个版本,这里为了学习引入的是没有压缩的版本,还一个three.min.js是压缩过的,只有three.js的四分之一大小,一般用于发布版本 --><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><style>body {margin: 0;overflow: hidden;}</style>
</head><body><div id="Stats-output">
</div><div id="WebGL-output">
</div><script type="text/javascript">var camera;var scene;var renderer;function init() {// 初始化fps显示var stats = initStats();// 创建场景scene = new THREE.Scene();// 创建摄像机camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);// 创建渲染器renderer = new THREE.WebGLRenderer();renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0)); // 设置背景颜色renderer.setSize(window.innerWidth, window.innerHeight);renderer.shadowMapEnabled = true; // 开启阴影(默认是关闭的,因为它比较耗计算资源)// 创建平面var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff}); // 物体受光源影响和使用的材质有关系,如果是MeshBasicMaterial基本材质,不会对光源有任何反应,只会使用指定的颜色来渲染物体。MeshLambertMaterial和MeshPhongMaterial材质在渲染时会对光源产生反应,没有光它就是漆黑的。var plane = new THREE.Mesh(planeGeometry, planeMaterial);plane.receiveShadow = true; // 开启接受阴影// 旋转和设置平面位置plane.rotation.x = -0.5 * Math.PI; // 绕x轴旋转90度plane.position.x = 15;plane.position.y = 0;plane.position.z = 0;// 将平面添加到场景容器中scene.add(plane);// 创建立方体var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000}); var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);cube.castShadow = true; // 开启投射阴影// 同上cube.position.x = -4;cube.position.y = 3;cube.position.z = 0;scene.add(cube);// 创建球体var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);sphere.castShadow = true;// 同上sphere.position.x = 20;sphere.position.y = 0;sphere.position.z = 2;scene.add(sphere);// 设置摄像机位置camera.position.x = -30;camera.position.y = 40;camera.position.z = 30;camera.lookAt(scene.position);// 添加光源var spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(-40, 60, -10);spotLight.castShadow = true; // 开启投射阴影scene.add(spotLight);// 把渲染器对象添加到div中显示document.getElementById("WebGL-output").appendChild(renderer.domElement);// 设置需要动态调参的参数var controls = new function () {this.rotationSpeed = 0.02;this.bouncingSpeed = 0.03;};// dat.GUI是Google员工开发的动态调参数的插件,这样就大大方便了我们调整相关渲染参数的操作了var gui = new dat.GUI();gui.add(controls, 'rotationSpeed', 0, 0.5); // 参数对象、参数名、参数的取值范围gui.add(controls, 'bouncingSpeed', 0, 0.5);render();// 渲染场景var step = 0;function render() {// 更新fpsstats.update();// 旋转立方体的各个面cube.rotation.x += controls.rotationSpeed;cube.rotation.y += controls.rotationSpeed;cube.rotation.z += controls.rotationSpeed;// 球体跳动step += controls.bouncingSpeed;sphere.position.x = 20 + ( 10 * (Math.cos(step)));sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));// 重新渲染场景requestAnimationFrame(render);renderer.render(scene, camera);}// 初始化fps显示function initStats() {var stats = new Stats();stats.setMode(0); // 0: fps, 1: 渲染时间msstats.domElement.style.position = 'absolute';stats.domElement.style.left = '0px';stats.domElement.style.top = '0px';document.getElementById("Stats-output").appendChild(stats.domElement);return stats;}}// 窗口大小发生变化后重新适配渲染场景function onResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);}// 监听窗口大小发生变化事件window.addEventListener('resize', onResize, false);// 页面加载完成后调用init方法window.onload = init;</script>
</body>
</html>
显示效果: