欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

00 - three.js 笔记 - 开始记录学习进度

程序员文章站 2022-05-23 14:04:13
...

首先,本人不是专业的WebGl开发工程师,也没有任何计算机图形学和WebGl基础,更不是开发前端的,只是一个刚刚参加工作的小白菜。
从今天开始起,我将把自己对 ThreeJs 的学习记录分享给大家。虽然并不专业,但也是自己的一些见解,也许以后再来回顾,或许会有一些不同的体会。
这个系列没有结束,但凡自己以后在工作中遇到问题都会分享在此博客,谈一下自己的思路和寻找的资料的总结!
本篇demo的浏览地址:http://ithanmang.com/threeJs/home/01-helloworld.html
00 - three.js 笔记 - 开始记录学习进度
然后我们来看示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>three hello world!</title>
    <style>
        body{
            margin: 0;
            overflow: hidden;
        }
    </style>
    <script src="../libs/build/three.js"></script>
    <script src="../libs/jquery-1.9.1.js"></script>
    <script src="../libs/examples/js/controls/TrackballControls.js"></script>
    <script src="../libs/examples/js/libs/dat.gui.min.js"></script>
    <script src="../libs/examples/js/libs/stats.min.js"></script>
</head>
<body>

<div id="WebGL-output"></div>
<div id="Stats-output"></div>

<script>

    $(function () {
        // 初始化性能插件
        var stats = initStats();
        // 创建场景
        var scene = new THREE.Scene();
        // 创建相机--透视相机
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
        // 创建WebGl渲染器
        var webGlRenderer = new THREE.WebGLRenderer();

        // 配置渲染器
        webGlRenderer.antialias = true;// 抗锯齿
        webGlRenderer.autoClear = true;// 自动清除
        webGlRenderer.setClearColor( 0x050505 );// 渲染背景色
        webGlRenderer.setSize( window.innerWidth, window.innerHeight);// 渲染范围

        // 配置相机
        camera.position.set(0, 400, 800);// 相机在三维空间的位置
        camera.lookAt(new THREE.Vector3(0, 0, 0));// 相机看向空间坐标原点

        // 创建立方体
        var cubeGeometry = new THREE.CubeGeometry(100, 100, 100);// 立方体模型
        var cubeMaterial = new THREE.MeshLambertMaterial({color : Math.random() * 0xffffff});// 立方体材质,颜色为随机色
        var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);// 创建网格实例
        cube.position.y = 90;// 立方体的 y 坐标 +90
        // 将立方体加入场景
        scene.add(cube);

        // 创建光源 ambientLight:环境光、directionalLight:平行光
        var ambientLight = new THREE.AmbientLight( 0x404040 );
        var directionalLight1 = new THREE.DirectionalLight( 0xC0C090 );
        var directionalLight2 = new THREE.DirectionalLight( 0xC0C090 );
        // 设置光源的位置
        directionalLight1.position.set(-300, -400, 300);
        directionalLight2.position.set(300, 400, -300);
        // 将光源加入场景
        scene.add(ambientLight);
        scene.add(directionalLight1);
        scene.add(directionalLight2);

        // 创建网格辅助
        var gridHelper = new THREE.GridHelper(1200, 50, 0xFF4444, 0x404040 );
        scene.add( gridHelper );

        // 创建轨迹球控件
        var trackballControls = new THREE.TrackballControls(camera, webGlRenderer.domElement);

        // 加入图形调试控件中的组件 gui
        var controls = new function () {
            this.rotationSpeed = 0.02;
            this.wireframe = cubeMaterial.wireframe;
            this.color = cubeMaterial.color.getStyle();
            this.gridHelper = false;
            this.backGround = webGlRenderer.getClearColor().getHex();
        }
        // 创建图形调试控件
        var gui = new dat.GUI();

        // 创建 helperGui 目录,下面包含 gridHelper 组件
        var helperGui = gui.addFolder('Helper');
        helperGui.add(controls,'gridHelper').onChange(function (e) {
            console.log(e);
            if (e){
                scene.remove(gridHelper);
            }else{
                scene.add(gridHelper);
            }
        });

        // 创建 meshGui 目录, 目录下包含组件 wireframe、color、rotationSpeed
        var meshGui = gui.addFolder("Mesh");
        meshGui.add(controls,'wireframe').onChange(function (e) {
            cubeMaterial.wireframe = e;
        });
        meshGui.addColor(controls,'color').onChange(function (e) {
            cubeMaterial.color.setStyle(e);
        })
        meshGui.add(controls,'rotationSpeed',0, 0.1);

        // 创建 sceneGui 目录, 目录下包含 backGround 组件
        var sceneGui = gui.addFolder('Scene');
        sceneGui.addColor(controls,'backGround').onChange(function (e) {
            webGlRenderer.setClearColor(e)
        })

        // 将渲染器添加到画布
        $("#WebGL-output").append(webGlRenderer.domElement);

        // 窗口大小改变触发的方法
        function onWindowResize() {
            // 改变相机的 aspect 为窗口的宽和长度之比
            camera.aspect = window.innerWidth / window.innerHeight;
            // 更新相机的投影矩阵
            camera.updateProjectionMatrix();
            // 重新设置渲染器的大小
            webGlRenderer.setSize(window.innerWidth, window.innerHeight);
        }
        // 添加事件监听
        window.addEventListener('resize',onWindowResize,false);

        // 初始化性能插件方法
        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';
            // 加入画布
            $("#Stats-output").append(stats.domElement);
            // 将初始化的插件返回
            return stats;
        }

        // 渲染方法
        function render() {
            // 更新性能插件
            stats.update();

            // 立方体绕 x、y、z 轴旋转,速度由调试插件控制
            cube.rotation.x += controls.rotationSpeed;
            cube.rotation.y += controls.rotationSpeed;
            cube.rotation.z += controls.rotationSpeed;

            // 更新轨迹球控件的操作范围
            trackballControls.handleResize();
            // 更新物体位置
            trackballControls.update();
            // 开始渲染
            webGlRenderer.render(scene, camera);
        }
        // 实现动画效果
        function animate() {
            requestAnimationFrame(animate);
            render();
        }
        animate();
    });

</script>

</body>
</html>
代码分析

这个简单的demo实现了三维场景 scene 的创建,加入了灯光和鼠标控制,并且添加了性能插件 stats 和 调试插件 datGUI 来控制立方体的颜色和材质的变换,以及加入了网格辅助对象,来清晰的看到立方体所处的位置。

threejs主要元素
  • 场景 (scene)
  • 相机(camera)
  • 渲染器(render)
    以上三个元素 是最主要的部分,然后来看实例中的部分代码。
  • 场景
// 创建场景
var scene = new THREE.Scene();
  • 相机
// 创建相机--透视相机
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);

并且对相机进行了配置

// 配置相机
camera.position.set(0, 400, 800);// 相机在三维空间的位置
camera.lookAt(new THREE.Vector3(0, 0, 0));// 相机看向空间坐标原点
  • 渲染器
 // 创建WebGl渲染器
 var webGlRenderer = new THREE.WebGLRenderer();

此处创建的是WebGL渲染器,threejs还有别的渲染器,可以根据具体情况来选择
对渲染器进行配置

// 配置渲染器
webGlRenderer.antialias = true;// 抗锯齿
webGlRenderer.autoClear = true;// 自动清除
webGlRenderer.setClearColor( 0x050505 );// 渲染背景色
webGlRenderer.setSize( window.innerWidth, window.innerHeight);// 渲染范围

此时三维场景就已经搭建完成,但是当我们运行代码的时候发现发现浏览器一片黑白,因为我们只是创建了三维场景所必须的组件,并没有开始渲染。

  • 开始渲染
// 首先需要将渲染器添加到画布
$("#WebGL-output").append(webGlRenderer.domElement);
// 开始渲染
webGlRenderer.render(scene, camera);

上面webGlRenderer.render(scene, camera);是渲染器将场景和相机结合到一起,然后开始进行执行渲染操作。
当我们再次执行的时候发现浏览器还是什么都没有,因为并没有向场景中添加对象,下面创建一个立方体,并将其加入到场景之中。

  • 创建立方体
// 创建立方体
var cubeGeometry = new THREE.CubeGeometry(100, 100, 100);// 立方体模型
var cubeMaterial = new THREE.MeshLambertMaterial({color : Math.random() * 0xffffff});// 立方体材质,颜色为随机色
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);// 创建网格实例
cube.position.y = 90;// 立方体的 y 坐标 +90
// 将立方体加入场景
scene.add(cube);
  • 还需要创建灯光
// 创建光源 ambientLight:环境光、directionalLight:平行光
var ambientLight = new THREE.AmbientLight( 0x404040 );
var directionalLight1 = new THREE.DirectionalLight( 0xC0C090 );
var directionalLight2 = new THREE.DirectionalLight( 0xC0C090 );
// 设置光源的位置
directionalLight1.position.set(-300, -400, 300);
directionalLight2.position.set(300, 400, -300);
// 将光源加入场景
scene.add(ambientLight);
scene.add(directionalLight1);
scene.add(directionalLight2);

然后再次打开浏览器就会发现场景中出现了一个小立方体,00 - three.js 笔记 - 开始记录学习进度
并且颜色是随机的,因为var cubeMaterial = new THREE.MeshLambertMaterial({color : Math.random() * 0xffffff});// 立方体材质,颜色为随机色设置的为随机色。

  • 让立方体动起来
    想让立方体动起来就需要不断的改变立方体的三维坐标,并且改变一次浏览器就需要刷新一次,因此需要使用这个函数来实现方法的回调,实现动画的效果requestAnimationFrame();参数是一个函数名,也就是需要重复执行的函数体。
    所以若想让立方体动起来,就需要在重复执行的函数里不断改变立方体的位置坐标,即改变一次刷新一次,来实现动画的效果。
// 实现动画效果
function animate() {
    // 立方体绕 xyz 轴旋转,速度由调试插件控制
    cube.rotation.x += 0.02;
    cube.rotation.y += 0.02;
    cube.rotation.z += 0.02;
    requestAnimationFrame(animate);
    // 开始渲染
    webGlRenderer.render(scene, camera);
}
animate();

这样就实现了,动画的效果,如果在这个函数中添加一行代码 console.log(cube.rotation);就会回发现此时浏览器在疯狂的刷新,来改变立方体旋转的三维坐标。
00 - three.js 笔记 - 开始记录学习进度

  • 添加网格辅助
    当然这个不是必须的,它只是场景中的一个对象,而已,凭个人喜好而添加,js是面向对象的语言,可以很好的使用 通过Object3D 继承来的 add()方法 和 remove()方法为场景接口来添加和移除对象。
 // 创建网格辅助实例
var gridHelper = new THREE.GridHelper(1200, 50, 0xFF4444, 0x404040 );
scene.add( gridHelper );

00 - three.js 笔记 - 开始记录学习进度
这两个方法,scene并不具有,只是从基类Object3D 继承来的。

  • 添加轨迹球控件
    threeJs用来通过鼠标和键盘来和模型交互的控件很多,这里就不一一概述了。
 // 创建轨迹球控件
 var trackballControls = new THREE.TrackballControls(camera, webGlRenderer.domElement);

轨迹球控件是用来操控物体和相机的,因此需要将相机传给它,因为场景一直在刷新,所以也要重置轨迹球的控制范围。
00 - three.js 笔记 - 开始记录学习进度

下面两个控件也不是必须的,仅仅是为了调试。
因为在场景中要把灯光和别的物体 一次放在一个合适的位置,是不可能的,因此需要调试。

  • 添加性能插件
    00 - three.js 笔记 - 开始记录学习进度
    这是初始化性能插件的方法。
    00 - three.js 笔记 - 开始记录学习进度
    在页面加载的时候就初始化。

  • 添加图形调试控件
    threeJs_r93\three.js-master\examples\js\libs目录可以找到dat.gui.min.js这个js库,引入。
    1.首先通过构造函数 来创建它的实例
    00 - three.js 笔记 - 开始记录学习进度
    2.将需要调试的对象和属性放入一个函数中
    00 - three.js 笔记 - 开始记录学习进度
    3.然后将要实现的逻辑添加到控件中
    00 - three.js 笔记 - 开始记录学习进度
    4.举个例子
    在这个完整的示例中,可以看到当点击这个按钮的时候 网格 对象就会消失。
    00 - three.js 笔记 - 开始记录学习进度
    这是怎么实现的呢?

  • 首先,为controls函数添加一个属性 并初始化。
    00 - three.js 笔记 - 开始记录学习进度

  • 然后,将这个属性添加给图形控件
    00 - three.js 笔记 - 开始记录学习进度
    看控制台的输出 e的变化。
    00 - three.js 笔记 - 开始记录学习进度
    此时 e 的值默认为false
    00 - three.js 笔记 - 开始记录学习进度
    00 - three.js 笔记 - 开始记录学习进度
    取消之后e的值又变为了默认的 fasle。别的需要调试的属性也是类似方法来创建。
    本篇文章对于 threeJs新手来说,可能会有点难度,但是也并不是很复杂,从以上解释来看,threeJs是完全面向对象的,除了所必须的场景(Scene)、相机(Camera)以及渲染器(Renderer)之外其它的对象都是可插拔的,个人认为学习,threeJs没有一些捷径,也有人认为学习三维开发应该从底层的OpenGL开始学起,但是那样对于没有任何计算机图形学基础的人来说,是很挫败自信心的,因为有时候学习了一周也不会创建出来一个简单的图形。