看了饿了么小小倩老师的canvas做品,心血来潮,学着作了个3D地球,也算是入坑WebGL了吧。以前有用过原生的canvas画2D的图形,此次则是用了Three.js和stats.js的3D框架,边学边练手,效果还算比较满意...毕竟第一次接触WebGLcss
Talk is cheap show the code!html
github项目源码地址:github.com/FightingHao…
项目演示地址:fightinghao.github.io/codingDream…前端
代码还有不少不足,求大神review..git
随着近几年前端的飞速发展,网页的表现能力愈来愈强大,浏览器提供了WebGL(Web图形处理库)接口,能够经过调用对应API进行3D图形的绘制,Three.js
则是在此基础接口之上又作了一层封装。Three.js
是当下最流行的网页3D渲染JS引擎。github
<!-- 做为Three.js渲染器输出3D图形 -->
<canvas id="webglcanvas"></canvas>
复制代码
<!-- Three.js库 -->
<script src="./libs/three.min.js"></script>
复制代码
<script src='http://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js'></script>
复制代码
let canvas, //画布标签
stats, //性能检测器
camera, //相机
scene, //场景
renderer, //渲染器
group, //物体组
mouseX = 0, //鼠标横向位置
mouseY = 0, //鼠标纵向位置
windowHalfX = window.innerWidth / 2, //视口大小的通常
windowHalfY = window.innerHeight / 2; //视口大小的一半
复制代码
为了让three.js显示,须要三件事情:场景、相机和渲染器web
THREE.Scene()
scene = new THREE.Scene() //建立场景
复制代码
THREE.PerspectiveCamera(fov, aspect, near, far)
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000)
camera.position.z = 500 //相机的远近
复制代码
THREE.WebGLRenderer({})
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true, // true/false表示是否开启反锯齿,
/*
alpha: false, // true/false 表示是否能够设置背景色透明,
precision: 'highp', // highp/mediump/lowp 表示着色精度选择,
premultipliedAlpha: false, // true/false 表示是否能够设置像素深度(用来度量图像的分辨率),
maxLights: 3, // 最大灯光数,
stencil: false // false/true 表示是否使用模板字体或图案
*/
})
复制代码
指定渲染器宽高 API:renderer.setSize(window.innerWidth, window.innerHeight)
参数分别为宽和高ajax
咱们已经用相机在场景中拍摄出了素材,但这些素在毕竟还只是2D,如今咱们要将该素材由2D变为3D,这时这就须要用一个3D图形做为2D素材的载体。用个吃货的简单理解,就是鸡肉卷外面的卷,开始卷是2D的平面的,把鸡肉到卷面上,用卷包裹起鸡肉,则就由2D的卷变成了3D的鸡肉卷对吧。emmm...解释的好尬。
API:canvas
THREE.SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
THREE.MeshBasicMaterial({})
THREE.Mesh(geometry, material)
let geometry = new THREE.SphereGeometry(200, 20, 20) //形状
let material = new THREE.MeshBasicMaterial({ map: texture }) //材质
let mesh = new THREE.Mesh(geometry, material) //物体
复制代码
new THREE.TextureLoader().load(img, callback)
loader.load('./img/land_ocean_ice_cloud_2048.jpg', function (texture) {
let geometry = new THREE.SphereGeometry(200, 20, 20) //形状
let material = new THREE.MeshBasicMaterial({ map: texture }) //材质
let mesh = new THREE.Mesh(geometry, material) //物体
})
复制代码
咱们有了3D图形,接下来就是将这些图形组合在一块儿,变成多样的3D界面
建立组合 API:THREE.Group()
浏览器
//建立一个组合
group = new THREE.Group()
scene.add(group) //将组合添加进场景中渲染
复制代码
API:group.add(mesh)
bash
3D图形已经在场景中渲染出来了,如今,则须要让它们动起来!
function animate() {
// 请求运动帧
requestAnimationFrame(animate)
render()
}
// 地球旋转逻辑函数
function render() {
// 更新性能监视器
stats.update();
camera.position.x += (mouseX - camera.position.x) * 0.05
camera.position.y += (mouseX - camera.position.y) * 0.05
// 拍摄角度, 可改变地球视角
camera.lookAt(scene.position)
// 地球自转速度
group.rotation.y -= 0.005
// 运动核心 递归调用
renderer.render(scene, camera)
}
复制代码
如今已经基本完成了3D地球的自转
说了Three.js,如今聊聊什么是stats.js吧。stats.js 是一个 Three.js 开发的辅助库,经过检测动画运行时的帧数,来测试WebGL代码的运行性能
相似于Three.js,stats.js也须要引入框架库,并由div来渲染显示性能测试界面
<!-- 用于显示和统计图形的性能 -->
<div id="stats-output"></div>
<!-- 引入stats.js库 -->
<script src="./libs/stats.min.js"></script>
复制代码
首先须要初始化stats
// stats性能检测器初始化
stats = initStats();
function initStats() {
stats = new Stats();
//设置统计模式
stats.setMode(0); // 0: fps, 1: ms
//统计信息显示在左上角
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '10px';
stats.domElement.style.top = '10px';
//将统计对象添加到对应的<div>元素中
document.getElementById("stats-output").appendChild(stats.domElement);
return stats;
}
复制代码
当场景变换时,也就是3D运动时,须要实时更新stats检测器 API:stats.update();
用于地球旋转时,动态更新检测状况
// 地球旋转逻辑函数
function render() {
// 更新性能监视器
stats.update();
camera.position.x += (mouseX - camera.position.x) * 0.05
camera.position.y += (mouseX - camera.position.y) * 0.05
// 拍摄角度, 可改变地球视角
camera.lookAt(scene.position)
// 地球自转速度
group.rotation.y -= 0.005
// 核心 递归调用
renderer.render(scene, camera)
}
复制代码
能够看到两种检测状况 stats.setMode(0); // 0: fps, 1: ms
参数0 显示FPS
// 绑定鼠标移动事件
document.addEventListener('mousemove', onDocumentMouseMove, false)
// 监听鼠标移动方向, 从而肯定地球南北半球
function onDocumentMouseMove(ev) {
ev = ev || event
mouseX = ev.clientX - windowHalfX
mouseY = ev.clientY - windowHalfY
}
复制代码
可根据窗口大小自动改变渲染图形大小
// 窗口大小改变监听
window.addEventListener('resize', onWindowResize, false)
// 监听窗口大小, 从而根据窗口大小改变地球大小, 相似响应式
function onWindowResize() {
windowHalfX = window.innerWidth / 2
windowHalfY = window.innerHeight / 2
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
复制代码
好了,如今3D地球就基本上完成了~
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>WebGL之3D地球</title>
<style>
/* 禁止系统默认滚动条 */
html,
body {
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<!-- 用于显示和统计图形的性能 -->
<div id="stats-output"></div>
<!-- 做为Three.js渲染器输出3D图形 -->
<canvas id="webglcanvas"></canvas>
<!-- webgl库 -->
<script src="./libs/three.min.js"></script>
<script src="./libs/stats.min.js"></script>
<script>
let canvas, //画布标签 绘图API
stats, //性能检测器
camera, //相机
scene, //场景
renderer, //渲染器
group, //物体组
mouseX = 0, //鼠标横向位置
mouseY = 0, //鼠标纵向位置
windowHalfX = window.innerWidth / 2, //视口大小的通常
windowHalfY = window.innerHeight / 2; //视口大小的一半
init() //构建地球
animate() //使地球旋转起来
function init() {
// 获取canvas画布
canvas = document.getElementById('webglcanvas')
// stats性能检测器初始化
stats = initStats();
// 3D绘制
// 相机
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000)
camera.position.z = 500 //相机的远近
// 场景
scene = new THREE.Scene()
// 建立一个组合
group = new THREE.Group()
scene.add(group) //将组合添加进场景中渲染
// 地球 数学形状 贴图
let loader = new THREE.TextureLoader()
loader.load('./img/land_ocean_ice_cloud_2048.jpg', function (texture) {
// console.log(texture)
let geometry = new THREE.SphereGeometry(200, 20, 20) //形状
let material = new THREE.MeshBasicMaterial({ map: texture }) //材质
let mesh = new THREE.Mesh(geometry, material) //物体
group.add(mesh)
})
// 渲染器
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true, // true/false表示是否开启反锯齿,
/*
alpha: false, // true/false 表示是否能够设置背景色透明,
precision: 'highp', // highp/mediump/lowp 表示着色精度选择,
premultipliedAlpha: false, // true/false 表示是否能够设置像素深度(用来度量图像的分辨率),
maxLights: 3, // 最大灯光数,
stencil: false // false/true 表示是否使用模板字体或图案
*/
})
// 指定渲染器宽高
renderer.setSize(window.innerWidth, window.innerHeight)
// 绑定鼠标移动事件
document.addEventListener('mousemove', onDocumentMouseMove, false)
// 窗口大小改变监听
window.addEventListener('resize', onWindowResize, false)
}
// 监听鼠标移动方向, 从而肯定地球南北半球
function onDocumentMouseMove(ev) {
ev = ev || event
mouseX = ev.clientX - windowHalfX
mouseY = ev.clientY - windowHalfY
}
// 监听窗口大小, 从而根据窗口大小改变地球大小, 相似响应式
function onWindowResize() {
windowHalfX = window.innerWidth / 2
windowHalfY = window.innerHeight / 2
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
function animate() {
// 请求运动帧
requestAnimationFrame(animate)
render()
}
// 地球旋转逻辑函数
function render() {
// 更新性能监视器
stats.update();
camera.position.x += (mouseX - camera.position.x) * 0.05
camera.position.y += (mouseX - camera.position.y) * 0.05
// 拍摄角度, 可改变地球视角
camera.lookAt(scene.position)
// 地球自转速度
group.rotation.y -= 0.005
// 核心 递归调用
renderer.render(scene, camera)
}
function initStats() {
stats = new Stats();
//设置统计模式
stats.setMode(0); // 0: fps, 1: ms
//统计信息显示在左上角
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '10px';
stats.domElement.style.top = '10px';
//将统计对象添加到对应的<div>元素中
document.getElementById("stats-output").appendChild(stats.domElement);
return stats;
}
// ecchat 数据可视化
// 平面的世界是错误的, css perspective:1000px transform-style:perserve-3d
// Camera Scene render(渲染容器) Light -> canvas
</script>
</body>
</html>
复制代码
图片素材和js库能够到个人github上下载:
github.com/FightingHao…
第一次在掘金上发文章,但愿你们能够点点赞哈哈~