ThreeJS 官方案例学习(webgl_camera)

ThreeJS 官方案例学习(webgl_camera)

1.效果图

在这里插入图片描述

2.源码

<template>
	<div>
		<div id="container"></div>
	</div>
</template>
<script>
import * as THREE from 'three';
// 导入控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// 引入房间环境,创建一个室内环境
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
// 导入性能监视器
import Stats from 'three/examples/jsm/libs/stats.module.js';
// 导入gltf载入库、模型加载器
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
// 引入模型解压器
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
//GUI界面
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
import gsap from 'gsap';
export default {
	data() {
		return {
			container: null, //界面需要渲染的容器
			scene: null,	// 场景对象
			camera: null, //相机对象
			renderer: null, //渲染器对象
			controller: null,	// 相机控件对象
			stats: null,// 性能监听器
			mixer: null,//动画混合器
			model: null,//导入的模型
			cameraRig: null,//相机组、相机集合
			activeCamera: null,// 当前活跃的相机
			activeHelper: null,// 当前活跃的相机辅助对象
			cameraPerspective: null, //透视相机
			cameraOrtho: null, //正交相机
			cameraPerspectiveHelper: null,	// 透视相机辅助对象
			cameraOrthoHelper: null,// 正交相机辅助对象
			mesh: null, //
			frustumSize: 600,
			clock: new THREE.Clock()// 创建一个clock对象,用于跟踪时间
		};
	},
	mounted() {
		this.init()
		this.animate()  //如果引入了模型并存在动画,可在模型引入成功后加载动画
		window.addEventListener("resize", this.onWindowSize)
		document.addEventListener('keydown', this.onKeyDown);
	},
	beforeUnmount() {
		console.log('beforeUnmount===============');
		// 组件销毁时置空
		this.container = null
		this.scene = null
		this.camera = null
		this.renderer = null
		this.controller = null
		this.stats = null
		this.mixer = null
		this.model = null//导入的模型
	},
	methods: {
		/**
		* @description 初始化
		 */
		init() {
			this.container = document.getElementById('container')
			this.setScene()
			this.setCamera()
			this.setRenderer()
			this.setController()
			this.addHelper()
			this.setPMREMGenerator()
			this.setLight()
			// this.setGltfLoader()
			this.addStatus()
			this.setMesh()
		},
		/**
		 * @description 创建场景
		 */
		setScene() {
			// 创建场景对象Scene
			this.scene = new THREE.Scene()
			// 设置场景背景
			// this.scene.background = new THREE.Color(0xbfe3dd);
		},
		/**
		 * @description 创建相机
		*/
		setCamera() {
			const aspect = this.container.clientWidth / this.container.clientHeight
			// 第二参数就是 长度和宽度比 默认采用浏览器  返回以像素为单位的窗口的内部宽度和高度
			this.camera = new THREE.PerspectiveCamera(50, 0.5 * aspect, 1, 10000)
			// 设置相机位置
			this.camera.position.set(0, 0, 2500)
			// 设置相机宽高比例
			this.camera.aspect = aspect;
			// 设置相机投影矩阵
			this.camera.updateProjectionMatrix();
			// 设置相机视线方向
			this.camera.lookAt(new THREE.Vector3(0, 0, 0))// 0, 0, 0 this.scene.position
			// 将相机加入场景
			this.scene.add(this.camera)

			// 透视相机的视角
			this.cameraPerspective = new THREE.PerspectiveCamera(50, 0.5 * aspect, 150, 1000);
			// 透视相机辅助对象
			this.cameraPerspectiveHelper = new THREE.CameraHelper(this.cameraPerspective);
			this.scene.add(this.cameraPerspectiveHelper);
			// 正交相机的视角
			this.cameraOrtho = new THREE.OrthographicCamera(0.5 * this.frustumSize * aspect / - 2, 0.5 * this.frustumSize * aspect / 2, this.frustumSize / 2, this.frustumSize / - 2, 150, 1000);
			// 正交相机辅助对象
			this.cameraOrthoHelper = new THREE.CameraHelper(this.cameraOrtho);
			this.scene.add(this.cameraOrthoHelper);

			// 当前活跃的相机和相机辅助对象,初始化为透视相机的视角
			this.activeCamera = this.cameraPerspective;
			this.activeHelper = this.cameraPerspectiveHelper;


			// counteract different front orientation of cameras vs rig
			// 抵消不同的相机与钻机的正面方向
			this.cameraOrtho.rotation.y = Math.PI;
			this.cameraPerspective.rotation.y = Math.PI;

			this.cameraRig = new THREE.Group();
			//相机组、相机平台集合
			this.cameraRig.add(this.cameraPerspective);
			this.cameraRig.add(this.cameraOrtho);

			this.scene.add(this.cameraRig);


		},
		/**
		 * @description 创建渲染器
		 */
		setRenderer() {
			// 初始化渲染器
			this.renderer = new THREE.WebGLRenderer({
				antialias: true,// 设置抗锯齿
				logarithmicDepthBuffer: true,  // 是否使用对数深度缓存
			})
			// 设置渲染器宽高
			this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
			// 设置渲染器的像素比
			this.renderer.setPixelRatio(window.devicePixelRatio);
			// 定义渲染器是否在渲染每一帧之前自动清除其输出
			this.renderer.autoClear = false;
			// 是否需要对对象排序
			// this.renderer.sortObjects = false;
			// 将渲染器添加到页面
			this.container.appendChild(this.renderer.domElement);

		},
		/**
		 * @description 添加创建控制器
		 */
		setController() {
			this.controller = new OrbitControls(this.camera, this.renderer.domElement);
			// 控制缩放范围
			// this.controller.minDistance = 1;
			// this.controller.maxDistance = 5;
			//是否开启右键拖拽
			// this.controller.enablePan = false;
			// 阻尼(惯性)
			this.controller.enableDamping = true; //启用阻尼(惯性)
			this.controller.dampingFactor = 0.04; //阻尼惯性有多大
			// this.controller.autoRotate = true; //自动围绕目标旋转
			// this.controller.minAzimuthAngle = -Math.PI / 3; //能够水平旋转的角度下限。如果设置,其有效值范围为[-2 * Math.PI,2 * Math.PI],且旋转角度的上限和下限差值小于2 * Math.PI。默认值为无穷大。
			// this.controller.maxAzimuthAngle = Math.PI / 3;//水平旋转的角度上限,其有效值范围为[-2 * Math.PI,2 * Math.PI],默认值为无穷大
			// this.controller.minPolarAngle = 1; //能够垂直旋转的角度的下限,范围是0到Math.PI,其默认值为0。
			// this.controller.maxPolarAngle = Math.PI - 0.1; //能够垂直旋转的角度的上限,范围是0到Math.PI,其默认值为Math.PI。
			// 修改相机的lookAt是不会影响THREE.OrbitControls的target的
			// 由于设置了控制器,因此只能改变控制器的target以改变相机的lookAt方向
			this.controller.target.set(0, 0, 0); //控制器的焦点
		},
		/**
		* @description 添加创建模型
		*/
		setGltfLoader() {
			let that = this
			// 添加模型
			// 实例化gltf载入库
			const loader = new GLTFLoader();
			// 实例化draco载入库
			const dracoLoader = new DRACOLoader();
			// 添加draco载入库
			dracoLoader.setDecoderPath("./draco/gltf/");
			// 添加draco载入库
			loader.setDRACOLoader(dracoLoader);
			loader.load('./model/gltf/LittlestTokyo.glb', (gltf) => {
				that.model = gltf.scene
				that.model.position.set(1, 1, 0)
				that.model.scale.set(0.01, 0.01, 0.01)
				that.scene.add(that.model)
				// 绑定model的动画混合器
				// 动画混合器(AnimationMixer)是用于场景中特定对象的动画的播放器。
				// 当场景中的多个对象独立动画时,每个对象都可以使用同一个动画混合器。
				that.mixer = new THREE.AnimationMixer(that.model)
				//设置剪辑动画
				that.mixer.clipAction(gltf.animations[0]).play()
				// 执行动画
				that.animate();
			}, undefined, (err => {
				console.error(err)
			}))
		},
		/**
		 * @description 创建辅助坐标轴
		 */
		addHelper() {
			// 模拟相机视锥体的辅助对象
			let helper = new THREE.CameraHelper(this.camera);
			// this.scene.add(helper);
			//创建辅助坐标轴、轴辅助 (每一个轴的长度)
			let axisHelper = new THREE.AxesHelper(150);  // 红线是X轴,绿线是Y轴,蓝线是Z轴
			this.scene.add(axisHelper)
			// 坐标格辅助对象
			let gridHelper = new THREE.GridHelper(100, 30, 0x2C2C2C, 0x888888);
			this.scene.add(gridHelper);
		},
		/**
		 * @description 给场景添加环境光效果
		 */
		setPMREMGenerator() {
			// 预过滤的Mipmapped辐射环境贴图
			const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
			this.scene.environment = pmremGenerator.fromScene(new RoomEnvironment(this.renderer), 0.04).texture;
		},
		/**
		 * @description 设置光源
		 */
		setLight() {
			// 环境光
			const ambientLight = new THREE.AmbientLight(0x404040, 4);
			// this.scene.add(ambientLight);
			// 平行光
			const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0);
			this.scene.add(directionalLight);
			// 点光源 - 照模型
			const test = new THREE.PointLight("#ffffff", 10, 2);
			// test.position.set(0, 0, 0);
			// this.scene.add(test);
			//点光源 - 辅助对象
			const testHelperMap = new THREE.PointLightHelper(test);
			// this.scene.add(testHelperMap);
		},
		/**
		 * @description 创建性能监听器
		*/
		addStatus() {
			// 创建一个性能监听器
			this.stats = new Stats();
			// 将性能监听器添加到容器中
			this.container.appendChild(this.stats.dom);
		},

		/**
		 * @description 设置球体
		 */
		setMesh() {
			// 设置球体
			// 将wireframe属性设置为true时会被渲染成线框,默认为false渲染为实心物体
			this.mesh = new THREE.Mesh(
				new THREE.SphereGeometry(100, 16, 8),
				new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true })
			)
			this.scene.add(this.mesh)

			const mesh2 = new THREE.Mesh(
				new THREE.SphereGeometry(50, 16, 8),
				new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true })
			)
			mesh2.position.y = 150;
			this.mesh.add(mesh2);

			const mesh3 = new THREE.Mesh(
				new THREE.SphereGeometry(5, 16, 8),
				new THREE.MeshBasicMaterial({ color: 0x0000ff, wireframe: true })
			)
			mesh3.position.z = 150;
			this.cameraRig.add(mesh3);

			// 创建10000个随机点
			const geometry = new THREE.BufferGeometry(); // 缓冲几何体
			const vertices = []; // 坐标点 x、y、z
			const colorList = []; // 颜色值
			for (let i = 0; i < 10000; i++) {
				// THREE.MathUtils.randFloatSpread( 2000 )生成-1000~1000之间的随机数
				vertices.push(THREE.MathUtils.randFloatSpread(2000)); // x
				vertices.push(THREE.MathUtils.randFloatSpread(2000)); // y
				vertices.push(THREE.MathUtils.randFloatSpread(2000)); // z
				// 颜色是0~1,不是0~255
				colorList.push(THREE.MathUtils.randInt(0, 1)); // r
				colorList.push(THREE.MathUtils.randInt(0, 1)); // g
				colorList.push(THREE.MathUtils.randInt(0, 1)); // b
			}
			// 设置位置,itemSize = 3 因为每个顶点都是一个三元组。
			geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
			// 设置颜色
			geometry.setAttribute('color', new THREE.Float32BufferAttribute(colorList, 3));
			const particles = new THREE.Points(
				geometry,
				new THREE.PointsMaterial({
					//  color: 0x888888
					size: 2,
					// vertexColors: true, // 使用点设置的颜色,旧版本值是THREE.VertexColors
					// sizeAttenuation: false // 设置为false后,点的大小一致
				}));
			this.scene.add(particles);
		},
		/**
		 * @description 监听屏幕的大小改变,修改渲染器的宽高,相机的比例
		*/
		// 窗口变化
		onWindowSize() {
			// 更新相机视锥体的长宽比
			const aspect = this.container.clientWidth / this.container.clientHeight
			this.camera.aspect = aspect;
			// 更新摄像机的投影矩阵
			this.camera.updateProjectionMatrix();

			this.cameraPerspective.aspect = 0.5 * aspect;
			this.cameraPerspective.updateProjectionMatrix();
			// left — 摄像机视锥体左侧面。
			// right — 摄像机视锥体右侧面。
			// top — 摄像机视锥体上侧面。
			// bottom — 摄像机视锥体下侧面。
			this.cameraOrtho.left = - 0.5 * this.frustumSize * aspect / 2;
			this.cameraOrtho.right = 0.5 * this.frustumSize * aspect / 2;
			this.cameraOrtho.top = this.frustumSize / 2;
			this.cameraOrtho.bottom = - this.frustumSize / 2;
			this.cameraOrtho.updateProjectionMatrix();

			//更新渲染器
			this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
			// 设置渲染器的像素比
			this.renderer.setPixelRatio(window.devicePixelRatio)
		},
		/**
		* @description 动画执行函数
		*/
		animate() {
			const delta = this.clock.getDelta();
			// mixer 动画更新
			if (this.mixer) {
				this.mixer.update(delta);
			}
			// 引擎自动更新渲染器
			requestAnimationFrame(this.animate);
			//update()函数内会执行camera.lookAt(x, y, z)
			// this.controller.update(delta);

			this.render()
			// 更新性能监听器
			this.stats.update();
			// 重新渲染场景
			this.renderer.render(this.scene, this.camera);
		},
		render() {
			const SCREEN_WIDTH = this.container.clientWidth
			const SCREEN_HEIGHT = this.container.clientHeight

			// 让球体旋转起来
			const r = Date.now() * 0.0005;
			this.mesh.position.x = 700 * Math.cos(r);
			this.mesh.position.z = 700 * Math.sin(r);
			this.mesh.position.y = 700 * Math.sin(r);

			this.mesh.children[0].position.x = 70 * Math.cos(2 * r);
			this.mesh.children[0].position.z = 70 * Math.sin(r);

			// 切换视角
			if (this.activeCamera === this.cameraPerspective) {
				// 相机更新
				this.cameraPerspective.fov = 35 + 30 * Math.sin(0.5 * r);//摄像机视锥体垂直视野角度,从视图的底部到顶部,以角度来表示
				this.cameraPerspective.far = this.mesh.position.length();//摄像机的远端面
				this.cameraPerspective.updateProjectionMatrix(); //相机投影更新

				// 相机辅助对象更新
				this.cameraPerspectiveHelper.update();  ///相机辅助投影更新
				this.cameraPerspectiveHelper.visible = true; //显示透视相机辅助

				this.cameraOrthoHelper.visible = false; //影藏正交相机辅助

			} else {
				// 相机更新
				this.cameraOrtho.far = this.mesh.position.length();//摄像机视锥体远端面
				this.cameraOrtho.updateProjectionMatrix(); //相机投影更新

				// 相机辅助对象更新
				this.cameraOrthoHelper.update();///相机辅助投影更新
				this.cameraOrthoHelper.visible = true; //显示正交相机辅助

				this.cameraPerspectiveHelper.visible = false;//影藏透视相机辅助
			}

			// 相机聚焦
			this.cameraRig.lookAt(this.mesh.position);

			this.renderer.clear();


			// 设置视口大小,左侧为活跃视角,右侧为窗口视角

			// .setViewport(x : Integer, y : Integer, width : Integer, height : Integer)
			// 将视口大小设置为(x, y)到(x + width, y + height).
			this.activeHelper.visible = false;
			this.renderer.setViewport(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT);
			this.renderer.render(this.scene, this.activeCamera);


			this.activeHelper.visible = true;
			this.renderer.setViewport(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT);
			this.renderer.render(this.scene, this.camera);
		},
		onKeyDown(event) {

			switch (event.keyCode) {

				case 79: /*O*/

					this.activeCamera = this.cameraOrtho;
					this.activeHelper = this.cameraOrthoHelper;

					break;

				case 80: /*P*/

					this.activeCamera = this.cameraPerspective;
					this.activeHelper = this.cameraPerspectiveHelper;

					break;

			}

		},
	},
};
</script>
<style>
#container {
	position: absolute;
	width: 100%;
	height: 100%;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值