个人博客地址: 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() {
// 更新fps
stats.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: 渲染时间ms
stats.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>
显示效果: