# threeJsDemo **Repository Path**: wayward_man/threeJsDemo ## Basic Information - **Project Name**: threeJsDemo - **Description**: js的demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-02-20 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 关于WebGL WebGL 是基于 OpenGL ES 2.0 的 Web 标准,可以通过 HTML5 Canvas 元素作为 DOM 接 口访问。 ## 什么是 THREE.js Three.js是JavaScript编写的WebGL第三方库。 Three.js 封装了WebGL底层的图形接口,开发人员无需掌握图形学知识即可以用简单的js代码实现三维场景的渲染。 渲染3d模型的时候,其实 是 很多个三角形拼接出来的。如下: ![](https://user-gold-cdn.xitu.io/2019/11/14/16e684d2eab3e324?w=2172&h=1232&f=png&s=471723) 左边是 一个 立方体,右侧的是 一个 球体(球体的话,切分出的 三角形 越多 网格模型更加 贴近 球体); > 核心代码如下 ```javascript //立方体 (x轴宽度,y轴高度,z轴深度,沿宽面分段数,沿高度面分段数,沿深度面分段数) object = new THREE.Mesh( new THREE.BoxGeometry(100, 100, 100, 1, 1, 1), material ); object.position.set(-200, 0, -200); scene.add(object); //立方体 (x轴宽度,y轴高度,z轴深度,沿宽面分段数,沿高度面分段数,沿深度面分段数) object = new THREE.Mesh( new THREE.BoxGeometry(100, 100, 100, 2, 2, 2), material ); object.position.set(-200, 0, 200); scene.add(object); //球形网格 (半径长度,水平块的密度,垂直块的密度) var object = new THREE.Mesh(new THREE.SphereGeometry(75, 5, 5), material); object.position.set(200, 0, -200); scene.add(object); //球形网格 (半径长度,水平块的密度,垂直块的密度) var object = new THREE.Mesh(new THREE.SphereGeometry(75, 50, 50), material); object.position.set(200, 0, 200); scene.add(object); ``` ## 一个three.js 项目中 三个重要对象,场景`scene`、`camera` 、 `renderer` 一个典型的 Three.js 程序至少要包括渲染器(Renderer)、场景(Scene)、照相机 (Camera),以及你在场景中创建的物体。 - scene: 在 Three.js 中添加的物体都是添加到场景中的,因此它相当于一个大容器。 - renderer: 渲染器,挂载了一个canvas 标签,用来最终渲染的 - camera:摄像机,相当于在三维场景中人的眼睛。 设置好各个对象的配置信息后,最终渲染的方法就是: `renderer.render(scene, camera);` ### 看这三个对象前,需要先了解一下 `three.js`的坐标系 > WebGL 和 Three.js 使用的坐标系是右手坐标系,看起来就是这样的: ![](https://user-gold-cdn.xitu.io/2019/11/9/16e4f7e7926a277c?w=520&h=522&f=png&s=19350) ![](https://user-gold-cdn.xitu.io/2019/11/9/16e4f886f7dcdd93?w=796&h=494&f=png&s=132538) ## 创建一个three.js 项目的基本步骤如下 ### 1.创建场景 `scene` ```javascript var scene; function initScene(){ scene = new THREE.Scene(); } ``` ### 2.创建相机 `camera` > 两种相机,`正交相机` 和 `透视投影相机` 一般说来,对于制图、建模软件通常使用正交投影,这样不会因为投影而改变物体比例; 而对于其他大多数应用,通常使用透视投影,因为这更接近人眼的观察效果。 下面图a是透视投影(近大远小,更真实),图b是正交投影 ![](https://user-gold-cdn.xitu.io/2019/11/9/16e4e8927aa34510?w=510&h=244&f=png&s=27282) > 正交相机 构造函数: `THREE.OrthographicCamera(left, right, top, bottom, near, far)` - 这六个参数分别代表正交投影照相机拍摄到的空间的六个面的位置,这两个面围成一个长 方体,我们称其为视景体(Frustum)。只有在视景体内部(下图中的灰色部分)的物体才 可能显示在屏幕上,而视景体外的物体会在显示之前被裁减掉。 - 为了保持照相机的横竖比例,需要保证 (right - left) 与 (top - bottom) 的比例与 Canvas 宽度与高度的比例一致。 ![](https://user-gold-cdn.xitu.io/2019/11/9/16e4f40d2bcec95e?w=787&h=673&f=png&s=93121) 创建方式如下: ```javascript var camera = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 1, 10); camera.position.set(0, 0, 5); scene.add(camera); ``` > 透视投影相机 构造函数:`THREE.PerspectiveCamera(fov, aspect, near, far)` - fov 是视景体竖直方向上的张角(是角度制而非弧度制),如侧视图所示。 - aspect 等于 width / height ,是照相机水平方向和竖直方向长度的比值,通常设为 Canvas 的横纵比例。 - near 和 far 分别是照相机到视景体最近、最远的距离,均为正值,且 far 应大于 near 。 透视图中,灰色的部分是视景体,是可能被渲染的物体所在的区域。 ![](https://user-gold-cdn.xitu.io/2019/11/9/16e4f5a3be9205b6?w=1015&h=437&f=png&s=147651) 创建方式如下 ```javascript var camera; function initCamera(){ camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); } ``` ### 3.创建渲染器 `renderer` ```javascript var renderer function initScene(){ renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); } ``` ### 4.创建网格模型 ```javascript var geometry = new THREE.BoxGeometry(1,1,1); var matrial = new THREE.MeshBasicMaterial({color:0x00ff00}); var cube = new THREE.Mesh(geometry, material); scene.add(cube); ``` ### 5.一个简单的demo 知道了以上关键的即可以动手渲染一个简单的 3d场景 ``` var windowWidth = window.innerWidth; var windowHeight = window.innerHeight; // 创建一个渲染器 var renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(windowWidth, windowHeight); document.body.appendChild(renderer.domElement); // 创建一个场景,所有的 3d模型都需要添加到 场景中才可以渲染 var scene = new THREE.Scene(); // 创建一个 camera var camera = new THREE.PerspectiveCamera( 45, windowWidth / windowHeight, 1, 4000 ); camera.position.set(0, 0, 3); // 创建一个光找模型 var light = new THREE.AmbientLight("#ffffff"); light.position.set(0, 0, 1); scene.add(light); var map = new THREE.TextureLoader().load("./img/lala.jpg"); // 创建材质,在模型上需要用到 var material = new THREE.MeshPhongMaterial({ map: map }); // 一个立方体 var geometry = new THREE.CubeGeometry(1, 1, 1); // 创建一个 由 3d模型 和 材质组成 的 网格模型 var cube = new THREE.Mesh(geometry, material); scene.add(cube); (function animate() { // 渲染 renderer.render(scene, camera); cube.rotation.x += 0.02; cube.rotation.y += 0.02; requestAnimationFrame(animate); })(); ``` ## 常用的几个 配置对象: ### 材质 #### 基本的材质`MeshBasicMaterial` > 使用基本材质的物体,渲染后的颜色始终为设置的颜色,也就是说不受光照影响,例如 设置太阳就很有用,当没有设置颜色的,颜色将是随机的 构造函数是:THREE.MeshBasicMaterial(opt); ```javascript //例子(创建一个 颜色为黄色,不透明度为0.75的材质) var geometry = new THREE.SphereGeometry(3,30,30); var map = new THREE.TextureLoader().load('./img/lala.jpg'); var material = new THREE.MeshBasicMaterial({ color:0xffff00, opacity:0.75 }) var cube = new THREE.Mesh(geometry, material) ``` #### `MeshLambert`材质 和 `MeshPhong`材质 THREE.MeshLambertMaterial(opt) 与 THREE.MeshPhongMaterial(opt) 和 基本材质 THREE.MeshBasicMaterial(opt) 的是用方法一致。 他们的几个常用属性(逗号后面的为默认值) * visible:是否可见 ,true * side: 渲染面片 正面还是反面,默认为 THREE.FrontSide * wireframe: 是否渲染为 线 而不是 面,默认为 false, * color: 十六进制颜色(0xffffff) * map: 使用纹理贴图,如(new THREE.TextureLoader().load('./img/lala.jpg'); 区别就是 在光照下,Lambert 模型 是漫反射,更加适合用生活中的大部分情况,而Phong模型则适合用在 金属 和镜面 相对反射率 比较高的物体上 - Phong材质 ![](https://user-gold-cdn.xitu.io/2019/11/14/16e685cef4ed98b2?w=2408&h=1544&f=png&s=295252) - Lambert材质 ![](https://user-gold-cdn.xitu.io/2019/11/14/16e686021b098c70?w=2576&h=1628&f=png&s=274086) ### Light(光源) ```javascript // 环境光,各个角度观察无差别 var light = new THREE.AmbientLight("#0000ff"); light.position.set(0, 0, 1); scene.add(light); // 点光源,顾名思义 spotLight = new THREE.PointLight("#ffffff"); spotLight.position.set(0, 0, 0); scene.add(spotLight); // 平行光,顾名思义 directionalLight = new THREE.DirectionalLight("#ffffff"); directionalLight.position.set(-40, 60, -10); //告诉平行光需要开启阴影投射 directionalLight.castShadow = true; scene.add(directionalLight); ``` ### 加载外部模型,比如 `stl`格式 ```html ``` ```javascript function initModel() { //辅助工具 var loader = new THREE.STLLoader(); loader.load("./model/aixiaofei.stl", function(geometry) { //创建纹理 new THREE.TextureLoader().load("./img/lala.jpg", function(map) { var material = new THREE.MeshPhongMaterial({ color:0xffffff, map: map }); var mesh = new THREE.Mesh(geometry, material); mesh.rotation.x = -0.5 * Math.PI; //将模型摆正 mesh.scale.set(0.1, 0.1, 0.1); //缩放 geometry.center(); //居中显示 scene.add(mesh); }); }); } ``` ![stlModel](https://user-gold-cdn.xitu.io/2019/11/14/16e6883a08b39149?w=974&h=562&f=png&s=33372) #### 例子 1. [太阳系](http://wap_front.dev.sina.cn/xiaofei/threeDemo/solar.html) ![](https://user-gold-cdn.xitu.io/2019/11/9/16e4f935d035ee97?w=1132&h=531&f=png&s=842398) 2. [VR](http://wap_front.dev.sina.cn/xiaofei/threeDemo/VR.html) ![](https://user-gold-cdn.xitu.io/2019/11/14/16e686fb59e73d1b?w=1410&h=873&f=png&s=1858416)