视点!three.js 入门学习(二)

来源: 博客园

上一个小案例

import * as THREE from "three";===================================   const width = 960;        const height = 540;        // 渲染器        const renderer = new THREE.WebGLRenderer();        //设置像素        renderer.setPixelRatio(window.devicePixelRatio);        // 设置大小        renderer.setSize(width, height);        document.body.appendChild(renderer.domElement);        // 视口        const scene = new THREE.Scene();        // 摄像头        const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);        camera.position.set(0, 0, 1000);        // 一个网格物体        const geometry = new THREE.BoxGeometry(400, 400, 400);        // 材质 法线网格材质(种把法向量射到RGB颜色的材质)        const material = new THREE.MeshNormalMaterial();        const box = new THREE.Mesh(geometry, material);        scene.add(box);        function animate() {            box.rotation.y += 0.01;            // 渲染到页面上, 视口,摄像头            renderer.render(scene, camera);            requestAnimationFrame(animate);        }        animate()

准备一个画布元素

WebGL 渲染的渲染器

const renderer = new THREE.WebGLRenderer({  canvas: document.querySelector("#myCanvas")});

在方法中setSize()设置大小

renderer.setSize(960, 540);

创造一个场景

创建一个场景。场景是放置 3D 对象、光源等的 3D 空间。

const scene = new THREE.Scene();

相机

此功能称为“视点”或“相机”


(资料图片)

在 Three.js 中,THREE.PerspectiveCamera通过传递四个信息来创建相机:视角、纵横比、绘制开始距离和绘制结束距离,作为类构造函数中的参数。

const camera = new THREE.PerspectiveCamera(45, 960 / 540,1,1000);

##  立方体

网格的显示对象创建的。要创建网格,我们需要准备两种类型:几何体(shape)和材料(material)。

BoxGeometry使用一种来生成像立方体和长方体这样的类似盒子的形状

const geometry = new THREE.BoxGeometry(500, 500, 500);

材质

法线网格材质(种把法向量射到RGB颜色的材质)MeshNormalMaterial

const material = new THREE.MeshNormalMaterial();

使用创建的几何体和材料创建网格。让我们将创建的网格添加到场景中。

const box = new THREE.Mesh(geometry, material);scene.add(box);

为了用 JavaScript 制作动画,有必要随着时间的推移不断调用该函数。为此,requestAnimationFrame()请使用名为 requestAnimationFrame()将运行每帧作为参数传递的函数。

tick();function tick() {  requestAnimationFrame(tick);}

Three.js不会自动将屏幕切换到最新,所以需要写一个指令来显式更新屏幕。renderer.render()您可以使用命令指定更新

tick();function tick() {  requestAnimationFrame(tick);  box.rotation.y += 0.01;  renderer.render(scene, camera); }

创建 Three.js 对象分为三个步骤:(1) 创建材质,(2) 创建几何体,(3) 创建网格

// 球体const geometry = new THREE.SphereGeometry(300, 30, 30);// 物理材质的制作const material = new THREE.MeshStandardMaterial({color: 0xFF0000});// 网格物体const mesh = new THREE.Mesh(geometry, material);// 添加到场景中scene.add(mesh);

光源

THREE.DirectionalLight  平行光const directionalLight = new THREE.DirectionalLight(0xFFFFFF);directionalLight.position.set(1, 1, 1)scene.add(directionalLight);

环境光

它可以作为一盏灯来照亮整个空间。

THREE.AmbientLight

var pointLight = new THREE.PointLight("#ccffcc");pointLight.position.set(0,10,10);scene.add(pointLight);

材质中使用图像

THREE.TextureLoader使用类指定文件路径

// 图像加载器        const loader = new THREE.TextureLoader();        const texture=loader.load("/assets/img/单人主体 (3).jpg")        // 物理材质        const material2=new THREE.MeshStandardMaterial({            map: texture,        })

球体集合

const geometry = new THREE.SphereGeometry( 5, 32, 32 );const material = new THREE.MeshBasicMaterial( {color: 0xFF0000} );const sphere = new THREE.Mesh( geometry, material );scene.add( sphere );

长方体几何

const geometry = new THREE.BoxGeometry( 1, 1, 1 );const material = new THREE.MeshBasicMaterial( {color: 0xFF0000} );const cube = new THREE.Mesh( geometry, material );scene.add( cube );

平面几何

const geometry = new THREE.PlaneGeometry( 5, 20, 32 );const material = new THREE.MeshBasicMaterial( {color: 0xFF0000, side: THREE.DoubleSide} );const plane = new THREE.Mesh( geometry, material );scene.add( plane );

圆锥几何

// 半径, 高度, 管道横截面的分段数const geometry = new THREE.ConeGeometry( 5, 20, 32 );const material = new THREE.MeshBasicMaterial( {color: 0xFF0000} );const cone = new THREE.Mesh( geometry, material );scene.add( cone );

圆柱几何

// 上圆半径,下圆半径,高度,管道横截面的分段数const geometry = new THREE.CylinderGeometry( 5, 5, 20, 32 );const material = new THREE.MeshBasicMaterial( {color: 0xFF0000} );const cylinder = new THREE.Mesh( geometry, material );scene.add( cylinder );

甜甜圈形几何(圆环缓冲几何体)

radius- 环面的半径,从环面的中心到管道横截面的中心。默认值是1。tube— 管道的半径,默认值为0.4。radialSegments— 管道横截面的分段数,默认值为12。tubularSegments— 管道的分段数,默认值为48。arc— 圆环的圆心角(单位是弧度),默认值为Math.PI * 2

const geometry = new THREE.TorusGeometry( 10, 3, 16, 100 );const material = new THREE.MeshBasicMaterial( { color: 0xFF0000 } );const torus = new THREE.Mesh( geometry, material );scene.add( torus );

相机控制

  • 自动旋转
  • 定位鼠标
  • 鼠标拖放

相机自动绕地球圆周移动

设置相机位置会为camera对象的属性position分配一个数值。

const radian = rot * Math.PI / 180;camera.position.x = 1000 * Math.sin(radian);camera.position.z = 1000 * Math.cos(radian);

使用cameraobject方法来指定原点坐标。方法是可以强制从任意位置指向指定坐标的指令。lookAt()(0, 0, 0)

camera.lookAt(new THREE.Vector3(0, 0, 0));

围绕地球旋转相机

let rot = 0;        function animate() {            // 渲染到页面上, 视口,摄像头            renderer.render(scene, camera);            // 缓动公式            rot += 0.2;            const radian = rot * Math.PI / 180;            camera.position.x = 1000 * Math.sin(radian);            camera.position.z = 1000 * Math.cos(radian);            camera.lookAt(new THREE.Vector3(0, 0, 0));            requestAnimationFrame(animate);        }        animate()

自动控制相机移动的THREE.OrbitControls

  • 安排相机绘制圆形轨道
  • 使用指针操作更改相机位置和角度
const   controls = new OrbitControls( camera, renderer.domElement );  function animate() {            controls.update();    }

材质

THREE.MeshBasicMaterial类(网格基础材质)是一种不考虑光照的材质。由于没有阴影THREE.MeshNormalMaterial(网格法线材质)该类是一种可视化 RGB 中正常颜色的材质THREE.MeshLambertMaterial   (网状朗伯材质) 表现无光泽度的磨砂质感的材质。因为出现了阴影,所以可以表现出深度感。一种需要阴影的材料,所以它需要光THREE.MeshPhongMaterial     表现光泽纹理的材质THREE.MeshToonMaterial      (网状卡通材质) 可以实现类似动漫的卡通着色THREE.MeshStandardMaterial   (网格标准材料)基于物理的渲染材质

光源

环境光源

AmbientLight类是实现环境光源的类。均匀照亮整个 3D 空间。当你想要均匀地提亮时使用它是很好的。由于无法产生阴影和投射阴影,因此仅此光源无法表现出三维效果。通常与其他灯一起使用。

颜色, 光照强度const light = new THREE.AmbientLight(0xFFFFFF, 1.0);scene.add(light);

平行光源

DirectionalLight类别是在特定方向发射的光。假设光源无限远,从它发出的所有光线都是平行的。一个简单的例子是阳光。由于太阳离地球如此之远,它的位置可以被认为是无限的。从太阳到地球表面的光线是平行的。

半球形光源

HemisphereLightAmbientLight与类类似,但是你可以将来自上方的光的颜色和来自下方的光的颜色分开。来自下方的光是反射光,类似于室外的光

// 天空的颜色, 地面的颜色,光强度const light = new THREE.HemisphereLight(0x888888, 0x0000FF, 1.0);

点光源

PointLight类是从一个点向所有方向发射的光源。一个很好的例子是裸灯泡。裸露的灯泡照亮了周围的环境。

射灯光源

SpotLight一个类是一种光源,它从一个点沿着一个圆锥体向一个方向发射。一个很好的例子是想象舞台上的手电筒或聚光灯。您可以指定衰减率和光的方向,因此可以指定的参数很多。如果放置很多,就会产生三维效果和存在感。

颜色、光强度、距离、照明角度、背景虚化、衰减率const light = new THREE.SpotLight(0xFFFFFF, 4, 30, Math.PI / 4, 10, 0.5);

辅助类

// 射灯光源辅助类const lightHelper = new THREE.SpotLightHelper(light);

矩形光源

// 颜色、光强、宽度、高度const light = new THREE.RectAreaLight(0xFFFFFF, 5.0, 10, 10);scene.add(light);

阴影

允许物体在其他物体上投射阴影对着光源。投射阴影将改善现实。

使用此功能有四种设置。

  1. 在渲染器中启用阴影
  2. 启用光源阴影
  3. 设置要投射阴影的网格对象
  4. 设置 Mesh 对象以接收阴影

需要注意:“投射阴影”和“接收阴影”。

分别设置castShadow属性(投射阴影的属性)和属性(接收阴影的属性)。receiveShadow

在渲染器属性shadowMap中启用它

renderer.shadowMap.enabled = true;

启用光源的castShadow属性。光源使用定向SpotLight发光。PointLight

// 创建照明const light = new THREE.SpotLight(0xFFFFFF, 2, 100, Math.PI / 4, 1);// 在灯光上启用阴影light.castShadow = true;scene.add(light);

投射阴影的网格receiveShadow启用属性。

const meshKnot = new THREE.Mesh(  new THREE.TorusKnotGeometry(3, 1, 100, 16),  new THREE.MeshStandardMaterial());// 设置上了一层阴影meshKnot.castShadow = true;scene.add(meshKnot);

设置阴影大小

light.shadow.mapSize.width = 2048;light.shadow.mapSize.height = 2048;

相机

相机类型

  • THREE.PerspectiveCamera: 应用透视的相机
  • THREE.OrthographicCamera:应用平行投影的相机

正交相机

// 左,右,上,下,近截面,远截面// new THREE.OrthographicCamera(left, right, top, bottom, near, far)const camera = new THREE.OrthographicCamera(-480, +480, 270, -270, 1, 1000);

雾效

一种使远处的物体看起来朦胧的效果

通过设置与相机的起始和结束距离,中间的对象将以指定的颜色变暗。

// 颜色、开始距离、结束距离scene.fog = new THREE.Fog(0x000000, 50, 2000);

应用于雾效

material.fog = true;

创建嵌套结构,THREE.Object3D或使用THREE.Group类方法add()添加它。相反,如果要从嵌套结构中删除,remove()请使用 方法。

const wrap = new THREE.Object3D(); wrap.add(mesh); scene.add(wrap); 
const wrap = new THREE.Group(); wrap.add(mesh); scene.add(wrap); 

世界坐标

THREE.Object3D getWorldPosition()您可以使用类方法获取世界坐标。由于我们需要计算世界坐标,所以要计算的3D对象必须添加到场景中

const world = object3D.getWorldPosition(new THREE.Vector3());

加载模型数据

GLTF 文件

const loader = new THREE.GLTFLoader(); const gltf = loader.loadAsync("./models/gltf/glTF/ToyCar.gltf");const model = gltf.scene;  scene.add(model);

3ds 文件

const loader = new THREE.TDSLoader();// 指定纹理路径  loader.setResourcePath("models/3ds/portalgun/textures/");// 指定 3ds 文件的路径  const object = loader.loadAsync("models/3ds/portalgun/portalgun.3ds");  scene.add(object);

Collada 文件

const loader = new THREE.ColladaLoader(); const collada = await loader.loadAsync("./models/collada/elf/elf.dae");  const model = collada.scene;  scene.add(model);

调整大小

window.addEventListener("resize", ()=>{  const width = window.innerWidth;  const height = window.innerHeight;  renderer.setPixelRatio(window.devicePixelRatio);  renderer.setSize(width, height);  camera.aspect = width / height;  camera.updateProjectionMatrix();});

检查与对象的交叉点

鼠标坐标

const mouse = new THREE.Vector2();// 注册鼠标事件canvas.addEventListener("mousemove", handleMouseMove);//  鼠标移动事件function handleMouseMove(event) {  const element = event.currentTarget;  //画布元素上的 XY 坐标  const x = event.clientX - element.offsetLeft;  const y = event.clientY - element.offsetTop;  // 画布元素的宽度/高度  const w = element.offsetWidth;  const h = element.offsetHeight;  //在 -1 到 +1 范围内注册当前鼠标坐标  mouse.x = ( x / w ) * 2 - 1;  mouse.y = -( y / h ) * 2 + 1;}
射线const raycaster = new THREE.Raycaster();tick();// 每帧运行的循环事件function tick() {  // 直接从鼠标位置生成光线矢量  raycaster.setFromCamera(mouse, camera);  // 获取被射线击中的物体  const intersects = raycaster.intersectObjects(scene.children);  if(intersects.length > 0){    // 对碰撞的物体做某事  }  renderer.render(scene, camera);  requestAnimationFrame(tick);}

显示大量粒子

// 创建形状数据        const SIZE = 3000;        // 要放置的数字        const LENGTH = 1000;        const vertices = [];        for (let i = 0; i < LENGTH; i++) {            const x = SIZE * (Math.random() - 0.5);            const y = SIZE * (Math.random() - 0.5);            const z = SIZE * (Math.random() - 0.5);            vertices.push(x, y, z);        }        // 创建形状数据        const geometry7 = new THREE.BufferGeometry();        geometry7.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));        const material7 = new THREE.PointsMaterial({            size: 10,            color: 0xffffff,        });        const mesh7 = new THREE.Points(geometry7, material7);        scene.add(mesh7);

移动几何物体

THREE.BufferAttribute保存顶点坐标信息。

THREE.BufferAttribute是一个不是数组的对象,所以它的用法有点特殊。count由于您可以获得属性中的顶点数,因此for使用与顶点数一样多的循环语句会很好。 您可以使用 getX()方法获取每个顶点的位置信息getY()getZ()

const position = mesh.geometry.attributes.position;for (let i = 0; i < position.count; i++) {  // 各顶点的XYZ坐标  const x = position.getX(i);  const y = position.getY(i);  const z = position.getZ(i);}   

完整点的案例

// 平面几何        const geometry = new THREE.PlaneGeometry(400, 400, 20, 20);        // 骨架线性的        const material = new THREE.MeshBasicMaterial({ wireframe: true });        // 物体制作        const mesh = new THREE.Mesh(geometry, material);        mesh.rotation.x = Math.PI / 2; // x轴的角度        scene.add(mesh);        tick();        function tick() {            // 几何顶点坐标信息            const position:any = mesh.geometry.attributes["position"];            for (let i = 0; i < position.count; i++) {            //     // 各頂点的XYZ座標                const x = position.getX(i);                const y = position.getY(i);                const z = position.getZ(i);                // 计算高度(物体的Z 坐标)                const nextZ = Math.sin(x * 0.03 + y * 0.02 + Date.now() * 0.002) * 30;                // position.setX(i, x); // 您可以省略 x 和 y,因为它们不会改变                // position.setY(i, y);                position.setZ(i, nextZ);            }            // 需要更新顶点            position.needsUpdate = true;            renderer.render(scene, camera);            requestAnimationFrame(tick);        }

对平面的顶点应用波浪式运动

通过使用 SimplexNoise 生成噪声,您可以创建类似于地面的表达式。虽然 SimplexNoise 本身不包含在 Three.js 中,但它是作为附加组件提供的

使用时,THREE.SimplexNoise从类中创建一个实例并noise()使用该方法。noise()当传递一个数字作为参数时,该方法返回一个介于 -1 和 1 之间的数字。

// 初始化噪声// 使用实例const simplexNoise = new THREE.SimplexNoise();// x1和y1是任意数值const value = simplexNoise.noise(x1, y1); 

使用PlaneGeometry创建类似地面的例子

// 平行光源        const light1 = new THREE.DirectionalLight(0x3399ff, 1);        light1.position.set(1, 1, 1);        scene.add(light1);        // 平面几何        // width, height,宽度横截面, 高度横截面        const geometry = new THREE.PlaneGeometry(1000, 1000, 80, 80);        const material = new THREE.MeshLambertMaterial({            // 顶点着色            flatShading: true,            // 定义将要渲染哪一面, 两面            side: THREE.DoubleSide,        });        const mesh = new THREE.Mesh(geometry, material);        // 地面旋转角度        mesh.rotation.x = Math.PI / 2;        scene.add(mesh);        // 初始化噪声        const simplexNoise = new SimplexNoise();        tick();        function tick() {            // 几何顶点坐标信息            const position:any = mesh.geometry.attributes["position"];            for (let i = 0; i < position.count; i++) {                // 各頂点的XYZ座標                const x = position.getX(i);                const y = position.getY(i);                const time = Date.now() * 0.0001;                // 计算高度, 主要是z坐标                const nextZ = simplexNoise.noise(x * 0.002 + time, y * 0.001 + time) * 150;                position.setZ(i, nextZ);            }            //            // 需要更新顶点            position.needsUpdate = true;            renderer.render(scene, camera);            requestAnimationFrame(tick);        }

setXYZ()您可以使用单独的setX()setY()和方法setZ()来做同样的事情

烟花效果

const LENGTH = 1000;        const vertices = [];        /**         * 存储粒子速度的序列         * @type {THREE.Vector3[]}         */        const speeds:any = [];        for (let i = 0; i < LENGTH; i++) {            // 顶点 初期坐标            vertices.push(0, 0, 0);            // 定义粒子的速度            const x = 2 * (Math.random() - 0.5);            const y = 2 * (Math.random() - 0.5);            const z = 2 * (Math.random() - 0.5);            speeds.push(new THREE.Vector3(x, y, z));        }        // 缓冲存储器        const geometry = new THREE.BufferGeometry();        geometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));        const material = new THREE.PointsMaterial({            size: 2,            color: 0xffffff,        });        // 点        const mesh = new THREE.Points(geometry, material);        scene.add(mesh)        tick();        function tick() {            // 几何顶点坐标信息            const position:any = mesh.geometry.attributes["position"];            for (let i = 0; i < position.count; i++) {                // 各頂点のXYZ座標                const x = position.getX(i);                const y = position.getY(i);                const z = position.getZ(i);                // speeds序列是速度用序列。存储了各个顶点的速度                const nextX = x + speeds[i].x;                const nextY = y + speeds[i].y;                const nextZ = z + speeds[i].z;                // 新坐标                position.setXYZ(i, nextX, nextY, nextZ);                // 原点からの距離を計算                const length = new THREE.Vector3(x, y, z).length();                // 如果超过了一定的范围                if (length > 100) {                    // 回到原点                    position.setXYZ(i, 0, 0, 0);                }            }           // 需要更新顶点            position.needsUpdate = true;            renderer.render(scene, camera);            requestAnimationFrame(tick);        }

标签:

精彩放送

热文