three.js + suncalc 在vue中模拟太阳运行轨迹以及光照效果

近期项目需要实现一个在three中出现 一个光照的效果,遂开始学习three.js,中间遇到了一些坑这边记录一下。
一开始想的比较简单,觉得只要熟悉three.js 的api后,就直接模拟了一个光照环形运转的效果出来。可惜等我做出来直接就被否定了,实际的太阳运行阶段要按当地经纬度来计算太阳位置。遂查询资料,发现有库suncalc可以计算太阳的角度。废话不多说直接贴代码吧:

<template>
	<canvas id="canvas">
	</canvas>
</template>
<script>

// 引入three
import * as THREE from 'three';

// 引入轨道控制器扩展库OrbitControls.js  

// 网上的方案是这种 but 
// import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//   "suncalc": "^1.9.0",引入方式
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';


let scene, camera, renderer

export default {
	data() {
		return {}
	},
	onLoad() {



	},
	onReady() {
		// 默认经纬度
		const latitude = 116.40400, longitude = 39.92800;

		const currentTime = new Date();
		// 凌晨时分
		currentTime.setHours(0, 0, 0, 0);

		let timeInterval = 120000; // 2分钟的时间间隔(毫秒)

		// 太阳高度
		const distance = 10;
		let sunX = 200;  // 初始x坐标
		let sunY = 200;  // 初始y坐标


		// 更新太阳位置和高度
		function updateSunPosition() {
			const sunPosition = SunCalc.getPosition(currentTime, latitude, longitude);
			const sunAltitude = sunPosition.altitude * (180 / Math.PI);
			const sunAzimuth = sunPosition.azimuth * (180 / Math.PI);


			const theta = (90 - sunAltitude) * (Math.PI / 180);
			const phi = (-sunAzimuth + 180) * (Math.PI / 180); // 考虑太阳在地平线以下的情况

			const x = 10 * Math.sin(theta) * Math.cos(phi);
			const y = 10 * Math.cos(theta);
			const z = 10 * Math.sin(theta) * Math.sin(phi);

			// 时间推移
			currentTime.setTime(currentTime.getTime() + timeInterval);

			// 因为太阳是从底部出来,所以数据去反就好了;
			directionalLight.position.set(-x, -y, -z);
		}



		// 创建场景
		scene = new THREE.Scene();

		// 创建相机
		camera = new THREE.PerspectiveCamera(
			45,// 视角
			window.innerWidth / window.innerHeight, //宽高比
			0.1, //近平面
			1000 // 远平面
		)

		// 创建渲染器
		renderer = new THREE.WebGLRenderer({ antialias: true })
		renderer.setSize(window.innerWidth, window.innerHeight)
		renderer.shadowMap.enabled = true;// 可以产生阴影

		// 将渲染器绑定到dom节点上
		const myCanvas = document.getElementById("canvas")
		myCanvas.appendChild(renderer.domElement)

		// 创建几何体
		const geometry = new THREE.BoxGeometry(1, 1, 5);

		// 创建材质
		const material = new THREE.MeshStandardMaterial(); // 基础材质

		// 父元素
		const cube = new THREE.Mesh(geometry, material);
		// 设置物体投射阴影
		cube.castShadow = true;

		// 设置立方体和平面接收阴影
		cube.receiveShadow = true;
		cube.position.set(0, 0, 0.5);

		scene.add(cube)



		// 创建平面
		const planeGeometry = new THREE.PlaneGeometry(10, 10);
		const plane = new THREE.Mesh(planeGeometry, material);
		plane.position.set(0, -1, 0);
		plane.receiveShadow = true;
		// plane.setRotationFromMatrix(-Math.PI / 2);
		scene.add(plane);



		// 设置相机位置
		camera.position.z = 10;
		camera.position.y = -14;
		camera.position.x = 0;
		camera.lookAt(0, 0, 0)


		// 添加世界坐标辅助器
		// const axesHelper = new THREE.AxesHelper(5);
		// scene.add(axesHelper);


		// 灯光
		// 环境光
		const light = new THREE.AmbientLight(0xffffff, 0.5);
		scene.add(light);

		// 直线光源
		const directionalLight = new THREE.DirectionalLight(0xffffff, 10);
		directionalLight.position.set(sunX, sunY, distance);
		// 开启光照投射阴影
		directionalLight.castShadow = true;

		scene.add(directionalLight);




		// 添加轨道控制器
		const controls = new OrbitControls(camera, renderer.domElement);
		// 设置带阻尼惯性
		controls.enableDamping = true;
		// 设置带阻尼系数
		controls.dampingFactor = 0.01;
		// 设置自动旋转
		// controls.autoRotate = true;


		// 渲染函数
		function animate() {
			controls.update();

			updateSunPosition();

			// // 调用getSunXY函数来计算太阳的位置
			// const [sunX, sunY, sunZ] = getSunXYZ();

			// // console.log("sunY",sunY)
			// directionalLight.position.set(sunX, sunY, sunZ);

			// 旋转
			// cube.rotation.x += 0.01;
			// cube.rotation.y += 0.01;

			// 渲染
			renderer.render(scene, camera);

			requestAnimationFrame(animate);

		}
		animate();


		// 页面监听窗口大小变化
		window.addEventListener('resize', () => {
			// 重置渲染器宽高比
			renderer.setSize(window.innerWidth, window.innerHeight);

			// 重置相机宽高比
			camera.aspect = window.innerWidth / window.innerHeight;

			// 更新相机投影矩阵
			camera.updateProjectionMatrix();
		})

	},
	methods: {


	}
}
</script>
<style></style>

思考,这里可以做一个优化就是把每个频率的xyz记录到一个Array中,等一天跑完了,这边就可以直接使用array中的值。不用再复杂的去计算了。

以上就是全部内容。

你可能感兴趣的:(three.js,javascript,vue.js,three.js)