前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Three.js教程(1):初识three.js

Three.js教程(1):初识three.js

作者头像
kai666666
发布2020-10-19 16:24:07
23.1K0
发布2020-10-19 16:24:07
举报
文章被收录于专栏:橙光笔记橙光笔记橙光笔记

今天开始我们进入一个新的世界,那就是3D世界。由于我自己也是刚接触到这块内容,所以如果文章中有问题,请尽快在文章最后的留言板中请指出。本教程有配套代码仓库,请点击https://github.com/KaiOrange/three.js-demo


为什么要使用three.js

要回答为什么要使用three.js?首先我们想想什么是three.js?官方给的简绍很简单:JavaScript 3D library,就是一个JavaScript的3D库。前端实现3D效果无非这么几种方式:

1.CSS 3D技术; 2.SVG; 3.WebGL技术; 4.Canvas或者图片等来模拟3D。

其中最后一种是用其他技术或方法去模拟3D效果,前3种才是浏览器真正意义上支持的3D技术。而three.js直接支持前3种渲染方式,可以看出three.js的强大。 大多情况下如果是为了展示3D的效果,那么直接用图片就可以了,如果要动起来的话,视频往往可以胜任。但是如果有交互视频就不行了,前端的3D技术弥补了视频的交互部分。而交互中逻辑性比较多,这样的话JavaScript来做会更有优势,所以WebGL技术就脱颖而出,首先我们来看一个WebGL的例子:

<!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>Document</title>
</head>
<body>
  <canvas id="webgl" width="500" height="500"></canvas>
  <script>
    var projectionMatrix = new Float32Array(
      [2.41421, 0, 0, 0,
      0, 2.41421, 0, 0,
      0, 0, -1.002002, -1,
      0, 0, -0.2002002, 0]);
    var modelViewMatrix = new Float32Array(
      [1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, -3.333, 1]);

    // Create the vertex data for a square to be drawn
    function createSquare(gl) {
      var vertexBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
      var verts = [
        .5,  .5,  0.0,
        -.5,  .5,  0.0,
        .5, -.5,  0.0,
        -.5, -.5,  0.0
      ];
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
      var square = {buffer:vertexBuffer, vertSize:3, nVerts:4, primtype:gl.TRIANGLE_STRIP};
      return square;
    }
    function createShader(gl, str, type) {
      var shader;
      if (type == "fragment") {
          shader = gl.createShader(gl.FRAGMENT_SHADER);
      } else if (type == "vertex") {
          shader = gl.createShader(gl.VERTEX_SHADER);
      } else {
          return null;
      }
      gl.shaderSource(shader, str);
      gl.compileShader(shader);
      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
          alert(gl.getShaderInfoLog(shader));
          return null;
      }
      return shader;
    }

  var vertexShaderSource =
    "attribute vec3 vertexPos;\n" +
    "uniform mat4 modelViewMatrix;\n" +
    "uniform mat4 projectionMatrix;\n" +
    "void main(void) {\n" +
    "   // Return the transformed and projected vertex value\n" +
    "   gl_Position = projectionMatrix * modelViewMatrix * \n" +
    "   vec4(vertexPos, 1.0);\n" +
    "}\n";

  var fragmentShaderSource =
    "void main(void) {\n" +
    "   // Return the pixel color: always output white\n" +
    "   gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n" +
    "}\n";

    var shaderProgram, shaderVertexPositionAttribute, shaderProjectionMatrixUniform, shaderModelViewMatrixUniform;
    function initShader(gl) {
      // load and compile the fragment and vertex shader
      var fragmentShader = createShader(gl, fragmentShaderSource, "fragment");
      var vertexShader = createShader(gl, vertexShaderSource, "vertex");
      // link them together into a new program
      shaderProgram = gl.createProgram();
      gl.attachShader(shaderProgram, vertexShader);
      gl.attachShader(shaderProgram, fragmentShader);
      gl.linkProgram(shaderProgram);
      // get pointers to the shader params
      shaderVertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vertexPos");
      gl.enableVertexAttribArray(shaderVertexPositionAttribute);

      shaderProjectionMatrixUniform = gl.getUniformLocation(shaderProgram, "projectionMatrix");
      shaderModelViewMatrixUniform = gl.getUniformLocation(shaderProgram, "modelViewMatrix");

      if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
          alert("Could not initialise shaders");
      }
    }
    function draw(gl, obj) {
      // clear the background (with black)
      gl.clearColor(0.0, 0.0, 0.0, 1.0);
      gl.clear(gl.COLOR_BUFFER_BIT);
      // set the vertex buffer to be drawn
      gl.bindBuffer(gl.ARRAY_BUFFER, obj.buffer);
      // set the shader to use
      gl.useProgram(shaderProgram);
      // connect up the shader parameters: vertex position and projection/model matrices
      gl.vertexAttribPointer(shaderVertexPositionAttribute, obj.vertSize, gl.FLOAT, false, 0, 0);
      gl.uniformMatrix4fv(shaderProjectionMatrixUniform, false, projectionMatrix);
      gl.uniformMatrix4fv(shaderModelViewMatrixUniform, false, modelViewMatrix);
      // draw the object
      gl.drawArrays(obj.primtype, 0, obj.nVerts);
    }

    window.onload = function (){
      var canvas = document.getElementById("webgl");
      var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
      gl.viewport(0, 0, canvas.width, canvas.height);
      var square = createSquare(gl);
      initShader(gl);
      draw(gl, square);
    }
  </script>
</body>
</html>

这段代码比较长,出来的效果很简单,只是一个小方块(只是一个正方形,还不是正方体)。具体效果如下:

WebGL实现小方块
WebGL实现小方块

我们来简单的看一下代码吧,代码中通过canvas.getContext("webgl")来获取到WebGL的上下文,对于稍微低版本的浏览器可以使用experimental-webgl来获取,还记得之前canvas吗?当时是通过2d来获取canvas的上下文对象。上述代码中,核心代码是initShader()方法,它初始化了着色器,我们这里用到了2个着色器:fragmentShader和vertexShader。正如他们的命名一样,第一个是片元着色器,第二个是顶点着色器。上述中还有2段String类型的代码,也就是字符串vertexShaderSource和字符串fragmentShaderSource的值,这两段代码是一种被称作GLSL ES的着色器语言(Shading Language),其实WebGL这一套都是来自于一种叫做OpenGL的技术,完全可以理解为WebGL提供了一层API来调用系统底层的OpenGL。也就是说WebGL把字符串的GLSL ES代码变成系统可以执行的OpenGL的代码,期间经过compileShader(),shaderSource(),compileShader(),attachShader()linkProgram()等多个JS方法。

到这里估计你也头大了,什么着色器,什么GLSL ES,什么Shader,把人搞的痛苦地!!!别急,正因为原生的WebGL这么晦涩难懂,所以才有了我们的主角three.js。现在你可以忘掉上面的内容,因为three.js封装的特别好,根本看不到任何WebGL的影子,你再也不需要去了解什么着色器,更不用写GLSL ES,我们先来看一个更高级且更简单的three.js的例子。

初识three.js

three.js可以使用模块化引入,当然也可以直接用script标签来引入。在具体项目中完全可以搭配React和Vue这样的MVVM框架,这里图简单就直接用script标签来引入。当写这篇文章的时候three.js的最新版本是r111,你可以在这里查看最新的版本。

我们首先来运行一下官方给的例子,源代码点击这里

<!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>Document</title>
  <style>
    *{
      margin: 0;
      padding: 0;
    }
  </style>
</head>
<body>
  <script type="text/javascript" src="../node_modules/three/build/three.js"></script>
  <script type="text/javascript">
    var camera, scene, renderer;
    var geometry, material, mesh;

    function init() {
      camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
      camera.position.z = 1;

      scene = new THREE.Scene();

      geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
      material = new THREE.MeshNormalMaterial();
      mesh = new THREE.Mesh( geometry, material );
      scene.add( mesh );

      renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.setSize( window.innerWidth, window.innerHeight );
      document.body.appendChild( renderer.domElement );
    }

    function animate() {
      requestAnimationFrame( animate );
      mesh.rotation.x += 0.01;
      mesh.rotation.y += 0.02;
      renderer.render( scene, camera );
    }

    init();
    animate();
  </script>
</body>
</html>

代码非常少,出来的效果却是很惊艳:

初识three.js
初识three.js

我们来分析一下官方例子的代码,首先创建了一个相机(THREE.PerspectiveCamera),它描述了眼睛看的方向;然后创建了一个场景(THREE.Scene);再之后把小物块(THREE.Mesh)放在场景上;最后渲染出来(THREE.WebGLRenderer)的DOM元素(canvas)追加到body上。animate函数的作用是启动动画,动画的原理就是每次改变一点点,然后重新渲染,这跟Canvas是一模一样的,不了解这块的同学可以看看这篇。 最后我们给出一张图来结束本章,这个就是three.js的基本模式,其中光线我们还没有涉及到:

对象架构
对象架构
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-12-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么要使用three.js
  • 初识three.js
相关产品与服务
图像处理
图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档