WebGL之3D地球

栏目: 编程工具 · 发布时间: 6年前

内容简介:看了饿了么小小倩老师的canvas作品,心血来潮,学着做了个3D地球,也算是入坑WebGL了吧。之前有用过原生的canvas画2D的图形,这次则是用了Three.js和stats.js的3D框架,边学边练手,效果还算比较满意...毕竟第一次接触WebGLTalk is cheap show the code!github项目源码地址:

看了饿了么小小倩老师的canvas作品,心血来潮,学着做了个3D地球,也算是入坑WebGL了吧。之前有用过原生的canvas画2D的图形,这次则是用了Three.js和stats.js的3D框架,边学边练手,效果还算比较满意...毕竟第一次接触WebGL

Talk is cheap show the code!

github项目源码地址: github.com/FightingHao…

项目演示地址: fightinghao.github.io/codingDream…

代码还有很多不足,求大神review..

什么是Three.js

随着近几年前端的飞速发展,网页的表现能力越来越强大,浏览器提供了WebGL(Web图形处理库)接口,可以通过调用对应API进行3D图形的绘制, Three.js 则是在此基础接口之上又做了一层封装。 Three.js 是当下最流行的网页3D渲染JS引擎。

Three.js使用方法

准备阶段

  1. 页面添加canvas元素
<!-- 作为Three.js渲染器输出3D图形 -->
  <canvas id="webglcanvas"></canvas>
复制代码
  1. 引入Three.js库文件
  • 本地引入
<!-- Three.js库 -->
  <script src="./libs/three.min.js"></script>
复制代码
  • CDN远程引入
<script src='http://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js'></script>
复制代码
  1. 项目中用到的变量
let canvas,  //画布标签
  stats,     //性能检测器
  camera,    //相机
  scene,     //场景
  renderer,  //渲染器
  group,     //物体组
  mouseX = 0,  //鼠标横向位置
  mouseY = 0,  //鼠标纵向位置
  windowHalfX = window.innerWidth / 2,  //视口大小的一般
  windowHalfY = window.innerHeight / 2; //视口大小的一半
复制代码

创建场景素材

为了让three.js显示,需要三件事情:场景、相机和渲染器

  1. 场景,可以理解为舞台。因为要绘制3D效果,必须要有一个舞台来演示效果

    创建场景 API: THREE.Scene()

scene = new THREE.Scene() //创建场景
复制代码
  1. 有了场景,现在需要相机来拍摄出素材出来,所以第二步则是需要创建一个"相机"

    创建相机 API: THREE.PerspectiveCamera(fov, aspect, near, far)
    • fov 可视角度,可理解为视野,是在显示器上看到的场景的范围,以度为单位。
    • aspect 为width/height,通常设置为canvas元素的高宽比。
    • near近端距离
    • far远端距离
    • 只有离相机的距离大于near值,小于far值,且在相机的可视角度之内,才能被相机投影到。
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000)
camera.position.z = 500  //相机的远近
复制代码
  1. 场景和相机都有了,也就是能够拍摄出素材了,但素材需要经过一些PS、美颜等才能变得好看。这时候就需要渲染器。

    创建渲染器 API: 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) 参数分别为宽和高

创建3D图形

我们已经用相机在场景中拍摄出了素材,但这些素在毕竟还只是2D,现在我们要将该素材由2D变为3D,这时这就需要用一个3D图形作为2D素材的载体。用个吃货的简单理解,就是鸡肉卷外面的卷,开始卷是2D的平面的,把鸡肉到卷面上,用卷包裹起鸡肉,则就由2D的卷变成了3D的鸡肉卷对吧。emmm...解释的好尬。

API:

  • 图形形状 THREE.SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
    • radius:球体半径
    • widthSegments, heightSegments:水平方向和垂直方向上分段数。widthSegments最小值为3,默认值为8。heightSegments最小值为2,默认值为6。
    • phiStart:水平方向上的起始角,默认值0
    • phiLenght:水平方向上球体曲面覆盖的弧度,默认Math.PI * 2
    • thetaStart : 垂直方向上的起始角, 默认0
    • thetaLength: 垂直方向是球体曲面覆盖的弧度,默认值为Math.PI
  • 图形材质 THREE.MeshBasicMaterial({})
    图形材质有很多种,网上查了查资料,觉得这个总结的挺好,大家可以看一看 blog.csdn.net/qq_30100043…
  • 图形构成 THREE.Mesh(geometry, material)
    • 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)  //将组合添加进场景中渲染
复制代码

将创建好的3D图形添加进组合中

API: group.add(mesh)

  • mesh 3D物体

运动起来

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地球的自转

什么是stats.js

说了Three.js,现在聊聊什么是stats.js吧。stats.js 是一个 Three.js 开发的辅助库,通过检测动画运行时的帧数,来测试WebGL代码的运行性能

stats.js使用

准备阶段

类似于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

WebGL之3D地球

stats.setMode(1); // 0: fps, 1: ms

参数1 显示MS

WebGL之3D地球

最后优化

1.通过鼠标可控制地球角度

// 绑定鼠标移动事件
document.addEventListener('mousemove', onDocumentMouseMove, false)

// 监听鼠标移动方向, 从而确定地球南北半球
function onDocumentMouseMove(ev) {
  ev = ev || event
  mouseX = ev.clientX - windowHalfX
  mouseY = ev.clientY - windowHalfY
}
复制代码

2.响应式

可根据窗口大小自动改变渲染图形大小

// 窗口大小改变监听
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…

第一次在掘金上发文章,希望大家可以点点赞哈哈~


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

浪潮之巅(第2版)(套装上下册)

浪潮之巅(第2版)(套装上下册)

吴军 / 人民邮电出版社 / 2013-7 / 80.00元

一个企业的发展与崛起,绝非只是空有领导强人即可达成。任何的决策、同期的商业环境,都在都影响着企业的兴衰。《浪潮之巅》不只是一本历史书,除了讲述科技顶尖企业的发展规律,对于华尔街如何左右科技公司,以及金融风暴对科技产业的冲击,也多有着墨。此外,这本书也着力讲述很多尚在普及或将要发生的,比如微博和云计算,以及对下一代互联网科技产业浪潮的判断和预测。因为在极度商业化的今天,科技的进步和商机是分不开的。 ......一起来看看 《浪潮之巅(第2版)(套装上下册)》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具