近期项目需要实现一个在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, rendererexport 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中的值。不用再复杂的去计算了。
以上就是全部内容。