threejs学习笔记

Posted weixin_43739821

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了threejs学习笔记相关的知识,希望对你有一定的参考价值。

开源库下载地址:https://github.com/mrdoob/three.js

官方文档的src目录里面包括了threejs的所有实现代码和接口文件,实际项目中可以只拷贝这个,docs目录里包含了threejs的所有帮助文档,example包含了threejs的所有样例程序,editor包含了threejs自带的场景编辑工具,其他文档暂时用不到先不介绍。

scene–场景是一个容器,主要用于保存、跟踪所要渲染的物体和使用的光源,如果没有场景就无法渲染任何物体。

camera–摄像机决定了我们在场景中能看到什么,我们基于摄像机的角度来计算场景对象在浏览器中会渲染成什么样子,摄像机有透视摄像机和正交投影摄像机。

render–渲染器有很多种,下面先用一种最简单的试试。

1.渲染第一个三维对象

<!DOCTYPE html>//渲染第一个三维对象
<html>

<head>
    <title>threejs-002</title>
    <meta charset="UTF-8" />
    <style>
        body 
            margin: 0;
            overflow: hidden;
        
    </style>
</head>

<body>
    <!--我们将把threejs渲染的效果显示在这个div中-->
    <div id="webgl-output">

    </div>

    <script type="module">//javascript加type="module"的作用是允许执行导入导出操作export import
        //我们需要使用哪个对象类型关键词,就在代码最上面将该关键词导入,比如这里就是导入场景、透视摄像机、渲染器、正方形几何体、网格基础材质、网格关键词,from后面写上threejs的路径,我们可以将官方文档的src目录拷贝到自己的工程依赖库,这里的路径就是src目录下的three.js
        import  Scene, PerspectiveCamera, WebGLRenderer, BoxGeometry, MeshBasicMaterial, Mesh  from "../../libs/three.js/three.js";

        var scene = new Scene();//创建一个场景对象
        var camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);//创建一个透视的摄像机,初始化参数中第二个参数为宽高比,整个页面的宽度除以高度

        var render = new WebGLRenderer();//WebGLRenderer只是其中一种渲染器
        render.setSize(window.innerWidth, window.innerHeight);//将渲染器大小设置为页面窗口大小 window.innerWidth,window.innerHeight是整个页面的宽度和高度
        document.getElementById("webgl-output").appendChild(render.domElement);//我们需要把这个渲染器中的DOM元素对象添加到指定的div中去,通过id号获取div,然后再往这个dom元素中追加渲染器的dom元素作为他的儿子

        var geometry = new BoxGeometry();//创建一个立方几何体,threejs中有很多内置的几何体,也可以自己定义几何体
        var material = new MeshBasicMaterial( color: 0x0000ff );//创造一个网格基础材质,并设置颜色,这里是最基础的材质,颜色的格式为RGB16进制,一共是6位,前面2位代表红色,中间俩位代表绿色,最后俩位代表蓝色
        var cube = new Mesh(geometry, material);//把立方几何体与基础材质进行组合后创建出一个新的网格对象
        scene.add(cube);//把立方体网格添加到场景中

        camera.position.z = 5;//设置透视摄像机在Z轴上的距离,也就是它与我们屏幕的距离

        //再让立方体旋转一下,感受一下它是一个立体,沿着x轴y轴旋转一定的角度
        cube.rotation.x += 0.50;
        cube.rotation.y += 0.50;

        render.render(scene, camera);//最后一步,将场景和摄像机俩个对象传入渲染器中

    </script>

</body>

</html>

渲染效果图:

2.添加threejs坐标轴、光源、阴影效果

<!DOCTYPE html>
<html>

<head>
    <title>threejs-004</title>
    <meta charset="UTF-8" />
    <style>
        body 
            margin: 0;
            overflow: hidden;
        
    </style>
</head>

<body>
    <!--我们将把threejs渲染的效果显示在这个div中-->
    <div id="webgl-output">

    </div>

    <script type="module">
        import 
            Scene, PerspectiveCamera, WebGLRenderer, BoxGeometry,
            MeshBasicMaterial, Mesh, PlaneGeometry, MeshLambertMaterial,
            AmbientLight, SpotLight, Vector2, AxesHelper, Color
         from "../../libs/three.js/three.js";//若有新关键词,别忘记加入导入列表中
        var scene = new Scene();
        var camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

        var render = new WebGLRenderer();
        render.setClearColor(new Color(0x000000));
        render.setSize(window.innerWidth, window.innerHeight);
        render.shadowMap.enabled = true;//为了实现阴影效果,必须在渲染器的shadowMap中的enabled属性设置为真
        document.getElementById("webgl-output").appendChild(render.domElement);

        //上来先添加一个三维坐标轴以方便后面对具体位置属性的理解
        var axes = new AxesHelper(50);//参数代表轴的长度,默认为1,我们设置为50 默认红绿蓝色线条对应XYZ轴
        //这个轴对象还可以自定义轴的颜色 三个参数格式为16进制RGB值 比如:
        //axes.setColors(0x000000,0xFFFFFF,0xAAAAAA);
        scene.add(axes);//把轴添加到场景对象中去

        var geometry = new BoxGeometry(8, 8, 8);//把立方体放大一些方便我们观察
        var material = new MeshLambertMaterial( color: 0xff2288 );//为了实现阴影效果,要将立方体的材质修改为Lmbert
        var cube = new Mesh(geometry, material);
        cube.castShadow = true;//为了实现阴影效果,必须把立方体的castShadow属性设置为真

        //把立方体挪一下位置
        cube.position.x = 10;//往X轴右侧移动10个单位
        cube.position.y = 8;//沿Y轴向上移动8个单位
        cube.position.z = -10;//沿Z轴向后移动10个单位

        scene.add(cube);

        //为了实现阴影效果,我们要创建一个地面来接受立方体的投影
        var planeGeometry = new PlaneGeometry(100, 100);//新建一个平面几何体,宽高为100
        var planeMaterial = new MeshLambertMaterial( color: 0xcccccc );//新建一个Lambert材质,指定它的颜色,这个材质可以接受并反射场景中各种光源发射出来的光线,之前声明立方体的基础材质与它不同,各种光源对基础材质是没有作用的,所以我们需要把前面的声明立方体的基础材质也修改为Lambert材质
        var plane = new Mesh(planeGeometry, planeMaterial);//通过平面几何体对象和Lambert材质来新建一个地面网格对象

        plane.rotation.x = -0.5 * Math.PI;//将地面网格沿着X轴往里旋转90度
        plane.position.set(15, 0, 0);//设地面网格的位置为X轴向右15个单位,Y与Z轴保持初始值
        plane.receiveShadow = true;//作为地面接受阴影的对象,必须设置receiveShadow的属性为真

        scene.add(plane);//把地面网格对象添加到场景中

        //因为我们摄像机的机位正好在Z轴上,所以我们需要把摄像机的机位移动一下
        //否则我们只能看到X和Y轴 
        camera.position.x = 30;//ps:坐标轴值也可以赋成负数,代表数轴负数方向
        camera.position.y = 40;
        camera.position.z = 30;
        //X轴向右代表正数,向左代表负数 
        //Y轴向上代表正数,向下代表负数
        //Z轴从我们的视角看向电脑屏幕,靠近我们的代表正数,远离我们的代表负数
        camera.lookAt(scene.position);//把摄像机的方向对准场景的中心点,这样我们就可以看到XYZ轴了

        cube.rotation.x += 0.80;
        cube.rotation.y += 0.80;

        //要生成投影的效果,我们要有产生投影的光源,这里我们生成一个聚光灯光源对象
        var spotLight = new SpotLight(0xFFFFFF);//设置聚光灯的颜色为白色,这样会让它射到的空间显得更亮一些
        spotLight.position.set(-60, 40, -65);//设置聚光灯的位置在离我们更远的左上角
        spotLight.castShadow = true;//要想让聚光灯产生阴影,必须设置castShadow属性为真
        spotLight.shadow.mapSize = new Vector2(1024, 1024);//下面的三行代码用于设置阴影效果
        spotLight.shadow.camera.far = 130;
        spotLight.shadow.camera.near = 40;

        // If you want a more detailled shadow you can increase the 
        // mapSize used to draw the shadows.
        // spotLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
        scene.add(spotLight);//将聚光灯添加到场景

        //我们采用Lambert材质创建的地面网格,所以必须要有一个Lambert光源才能看得到,之前的立方体可以正常显示是因为它采用的是基础材质,不需要光源就能显示
        var ambienLight = new AmbientLight(0xcccccc);//新建一个AmbientLight光源对象,颜色设置为灰色,它的颜色会应用到整个场景中的对象,该光源并没有特殊来源方向,并且不会生成阴影,所以它不能作为唯一光源来使用
        scene.add(ambienLight);//将光源添加到场景

        render.render(scene, camera);

    </script>

</body>

</html>

渲染效果图:

3.添加threejs动态、简化试验、自适应场景

<!DOCTYPE html>
<html>

<head>
    <title>threejs-005</title>
    <meta charset="UTF-8" />
    <style>
        body 
            margin: 0;
            overflow: hidden;
        
    </style>

</head>

<body>
    <!--我们将把threejs渲染的效果显示在这个div中-->
    <div id="webgl-output">

    </div>
    <!--添加一个div用来显示动画运行时的效率-->
    <div id="myStats"></div>

    <script type="module">
        import 
            Scene, PerspectiveCamera, WebGLRenderer, BoxGeometry,
            MeshBasicMaterial, Mesh, PlaneGeometry, MeshLambertMaterial,
            AmbientLight, SpotLight, Vector2, AxesHelper, Color
         from "../../libs/three.js/three.js"

        //为了给接下来的动画显示运行时的效率,我们需要引入一个辅助库
        import  Stats  from "../../libs/Stats/Stats.js"//Stats库主要用于检测动画运行时的帧数

        import  dat  from "../../libs/dat.gui/dat.gui.js"//引入gui库更快的实现动画效果

        var scene = new Scene();
        var camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

        var render = new WebGLRenderer();
        render.setClearColor(new Color(0x000000));
        render.setSize(window.innerWidth, window.innerHeight);
        render.shadowMap.enabled = true;
        document.getElementById("webgl-output").appendChild(render.domElement);

        var axes = new AxesHelper(50);
        scene.add(axes);

        var geometry = new BoxGeometry(8, 8, 8);
        var material = new MeshLambertMaterial( color: 0xff2288 );
        var cube = new Mesh(geometry, material);
        cube.castShadow = true;

        cube.position.x = 4;
        cube.position.y = 8;
        cube.position.z = 10;

        scene.add(cube);

        var planeGeometry = new PlaneGeometry(100, 100);
        var planeMaterial = new MeshLambertMaterial( color: 0xAAAAAA );
        var plane = new Mesh(planeGeometry, planeMaterial);

        plane.rotation.x = -0.5 * Math.PI;
        plane.position.set(15, 0, 0);
        plane.receiveShadow = true;

        scene.add(plane);

        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 30;
        camera.lookAt(scene.position);

        cube.rotation.x += 0.80;
        cube.rotation.y += 0.80;

        var spotLight = new SpotLight(0xFFFFFF);
        spotLight.position.set(-60, 40, -65);
        spotLight.castShadow = true;
        spotLight.shadow.mapSize = new Vector2(1024, 1024);
        spotLight.shadow.camera.far = 130;
        spotLight.shadow.camera.near = 40;

        scene.add(spotLight);

        var ambienLight = new AmbientLight(0xcccccc);
        scene.add(ambienLight);

        var stats = addStats();//获取动画运行效率统计对象

        var ctrlObj =  rotationSpeed: 0.01, jumpSpeed: 0.01 ;//为实现运动效果,我们需要一个对象来保存我们修改的属性值,其有俩个属性值,一个保存正方体旋转速度,一个保存正方体跳跃速度
        var ctrl = new dat.GUI();//创造一个dat.gui对象,然后将前面用于保存属性的对象传入该对象中
        ctrl.add(ctrlObj, "rotationSpeed", 0, 1);//并且对速度属性进行范围设置,范围是0到1
        ctrl.add(ctrlObj, "jumpSpeed", 0, 1);

        // render.render(scene,camera);
        renderScene();//将render方法替换成renderScene方法

        var gap = 0;
        function renderScene() //为了刷新页面,我们封装一个刷新页面的方法 同时在该方法中实现立方体的旋转和跳跃
            // cube.rotation.x += 0.01;实现立方体的旋转效果很简单,只需要在每次调用renderScene方法时对rotation属性值进行修改
            // cube.rotation.y += 0.01;每次增加0.01,这是立方体旋转的速度,我们通过调试可以找到一个适合的速度
            // cube.rotation.z += 0.01;这里我们使用gui库实现,省的慢慢调试,同时固定速度也变成在页面上显示为可调节的一定范围内的速度,即速度可由用户修改

            cube.rotation.x += ctrlObj.rotationSpeed;
            cube.rotation.y += ctrlObj.rotationSpeed;
            cube.rotation.z += ctrlObj.rotationSpeed;

            //再来实现跳跃动态效果 gap存放跳跃的速度 通过position属性来实现立方体跳跃 在XY轴方向上进行位置变化 gap += 0.01
            gap += ctrlObj.jumpSpeed;//通过对gap值的修改实现位置的变化,这里同样使用更简单的gui库

            cube.position.x = 25 + (20 * (Math.sin(gap)));//通过Math.sin()方法创建正方体在x轴方向上的运行轨迹
            cube.position.y = 6 + (20 * Math.abs(Math.cos(gap)));//通过Math.cos()方法创建正方体在y轴方向上的运行轨迹

            stats.update();//为了通知stats统计对象需要被刷新,我们需要在该方法中调用stats的update方法
            requestAnimationFrame(renderScene);//这个方法能使得浏览器平滑高效的进行绘制
            render.render(scene, camera);//为了在场景创建完毕后不在调用render方法,而是调用renderSence方法,我们需要在renderSence中调用render方法
        

        function addStats() //引入Stats库我们需要添加统计对象,因此封装一个addStats方法
            var stats = new Stats();//创建一个统计对象
            stats.domElement.style.position = 'absolute';//初始化统计对象的位置
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';
            stats.setMode(0);//因为我们想要检测画面每秒传输帧数,所以我们需要调用setMode方法,参数设置为0;我们也可以通过修改setMode方法的参数直接显示渲染时间

            document.getElementById("myStats").appendChild(stats.domElement);//将创建的统计对象元素放置在我们的div上
            return stats;//返回该统计对象 方便后序调用
        

        //下面我们要求通过浏览器的缩放来查看三维场景时,场景要能根据浏览器窗口大小进行自动适应功能.首先要监听浏览器的resize事件,并且通过回调函数来重写事件
        window.addEventListener('resize', onWindowResize, false);

        function onWindowResize() //我们需要在回调函数中重新设置camera的aspect属性,用于重新确定浏览器需要渲染的长宽比
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();//修改完aspect,我们要调用这个方法以此来手动更新相机的投影矩阵
            render.setSize(window.innerWidth, window.innerHeight);//最后通过render的setSize方法重置场景的渲染尺寸
        

    </script>

</body>

</html>

渲染效果图:

以上是关于threejs学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

threejs学习day6:外部模型

ThreeJS - 错误的光线旋转

threejs学习day5:网格

THREEJS案例(11)- 光照阴影

一大波ThreeJS学习笔记来啦

ThreeJS-几何体