liulinsp 2020-01-09
坑!
当项目决定要做宇宙星空效果的时候,我百度了一波;发现了threejs;然后就从0开始学习了;一路上坑坑洼洼的;网上确实有相关文章,但没有相对于新手的;尤其是我这种现看文档现做的;而且很多代码都是使用原生js或者jquery的;
这次是我把threejs引入angular8做的一个太阳系效果;至于后面怎么交互的;后面再补;首先把效果做出来;先上图~~~静态图,实际效果是可以动的
首先安装;
// 安装threejs npm i three -s // npm i -s 按版本安装 npm i @types/three -s // 安装控制器(可以移动相机) npm i three-orbitcontrols-ts // 这里安装的是orbicontorls的一个替代工具,因为发现 npm i three-full 然后引入orbitcontrols 不好使,其他的也不好用,最后我百度很久发现用这个可以解决; // 安装JavaScript性能监控器(这个酌情安装) npm i stats.js -s
目录结构
// base.ts 这里放 three.js 的重构 代码
import { Scene, AmbientLight, PointLight, WebGLRenderer, PerspectiveCamera, GridHelper, Color, WebGLShadowMap } from ‘three‘; import { OrbitControls } from ‘three-orbitcontrols-ts‘; import * as THREE from ‘three‘; import * as Stats from ‘stats.js‘; // import * as Stats from ‘stats.js‘; // import * as TWEEN from ‘@tweenjs/tween.js‘; // 渲染器 export const RENDERER = new WebGLRenderer({ antialias: true, alpha: true }); export function initRenderer(doc: any) { RENDERER.setSize(doc.clientWidth, doc.clientHeight); doc.appendChild(RENDERER.domElement); } // 相机 export let CAMERA: any; export let CONTROLS: any; export function initCamera(doc: any) { CAMERA = new THREE.PerspectiveCamera(20, doc.clientWidth / doc.clientHeight, 1, 1000); CAMERA.position.set(0, 0, 500); CONTROLS = new OrbitControls(CAMERA, RENDERER.domElement); // 控制镜头 } // 初始化场景 export const SCENE = new Scene(); export function initScene() {} // 灯光 export let LIGHT: any; export function initLight() { LIGHT = new THREE.DirectionalLight(0xff0000, 1.0); LIGHT.position.set(100, 100, 200); SCENE.add(LIGHT); } // 物体 export let CUBE: any; export let CENTERBALL: any; export function initObject() { // 太阳材质 const loader = new THREE.TextureLoader(); const sunTexture = loader.load(‘./assets/imgs/img-three/sun_bg.jpg‘); // const sunTexture = THREE.ImageUtils.loadTexture(‘./assets/imgs/img-three/sun_bg.jpg‘, null, () => { // RENDERER.render(SCENE, CAMERA); // }); // 设定太阳 CENTERBALL = new THREE.Mesh( new THREE.SphereGeometry(30, 30, 30), new THREE.MeshBasicMaterial({ map: sunTexture }) ); // 添加太阳发光效果 const centerBallLite = new THREE.Sprite( new THREE.SpriteMaterial({ map: new THREE.CanvasTexture(generateSprite(‘253,111,7‘)), blending: THREE.AdditiveBlending }) ); centerBallLite.scale.x = centerBallLite.scale.y = centerBallLite.scale.z = 90; // 添加太阳到场景 SCENE.add(centerBallLite); SCENE.add(CENTERBALL); // 添加各个行星 // 添加水星 STARLITES.push( initSatellite( 2, 34, { x: -Math.PI * 0.42, y: Math.PI * 0.02, z: 0 }, 0.02, ‘./assets/imgs/img-three/mercury_bg.png‘, SCENE ) ); // 添加金星 STARLITES.push( initSatellite( 3, 38, { x: -Math.PI * 0.42, y: Math.PI * 0.02, z: 0 }, 0.018, ‘./assets/imgs/img-three/venus_bg.png‘, SCENE ) ); // 添加地球 STARLITES.push( initSatellite( 3.2, 42.2, { x: -Math.PI * 0.42, y: Math.PI * 0.02, z: 0 }, 0.016, ‘./assets/imgs/img-three/earth_bg.png‘, SCENE ) ); // 添加火星 STARLITES.push( initSatellite( 2.5, 47.1, { x: -Math.PI * 0.42, y: Math.PI * 0.02, z: 0 }, 0.014, ‘./assets/imgs/img-three/spark_bg.png‘, SCENE ) ); // 添加木星 STARLITES.push( initSatellite( 35, 71, { x: -Math.PI * 0.42, y: Math.PI * 0.02, z: 0 }, 0.012, ‘./assets/imgs/img-three/jupiter_bg.png‘, SCENE ) ); // 添加土星 STARLITES.push( initSatellite( 45, 110, { x: -Math.PI * 0.42, y: Math.PI * 0.02, z: 0 }, 0.01, ‘./assets/imgs/img-three/saturn_bg.png‘, SCENE ) ); // 添加天王星 STARLITES.push( initSatellite( 17, 158, { x: -Math.PI * 0.42, y: Math.PI * 0.02, z: 0 }, 0.008, ‘./assets/imgs/img-three/uranus_bg.png‘, SCENE ) ); // 添加海王星 STARLITES.push( initSatellite( 15, 188, { x: -Math.PI * 0.42, y: Math.PI * 0.02, z: 0 }, 0.006, ‘./assets/imgs/img-three/neptune_bg.png‘, SCENE ) ); } // 动画混合器组(把模型的动画混合器都push到这里面,在canvas.ts里面更新动画 ) export const MIXER = []; // 性能检测 export const STATS = new Stats(); export function initStats(doc) { STATS.setMode(0); STATS.domElement.style.position = ‘absolute‘; STATS.domElement.left = ‘0px‘; STATS.domElement.top = ‘0px‘; doc.appendChild(STATS.domElement); } // 网格 export function initGrid() { const gridHelper = new GridHelper(100, 50); SCENE.add(gridHelper); } // * 返回行星轨道的组合体 // * @param starLiteSize 行星的大小 // * @param starLiteRadius 行星的旋转半径 // * @param rotation 行星组合体的x,y,z三个方向的旋转角度 // * @param speed 行星运动速度 // * @param imgUrl 行星的贴图 // * @param scene 场景 // * @returns {{satellite: THREE.Mesh, speed: *}} 卫星组合对象;速度 export let STARLITES = []; export function initSatellite( starLiteSize: any, starLiteRadius: any, rotation: any, speed: any, imgUrl: any, scene: any ) { const ring = new THREE.RingGeometry(starLiteRadius, starLiteRadius + 0.05, 50, 1); const baseMaterial = new THREE.MeshBasicMaterial(); const track = new THREE.Mesh(ring, baseMaterial); const centerMesh = new THREE.Mesh(new THREE.SphereGeometry(1, 1, 1), new THREE.MeshLambertMaterial()); // 材质设定 const starLite = new THREE.Sprite( new THREE.SpriteMaterial({ map: THREE.ImageUtils.loadTexture(imgUrl) }) ); starLite.scale.x = starLite.scale.y = starLite.scale.z = starLiteSize; starLite.position.set(starLiteRadius, 0, 0); const pivotPoint = new THREE.Object3D(); pivotPoint.add(starLite); pivotPoint.add(track); centerMesh.add(pivotPoint); centerMesh.rotation.set(rotation.x, rotation.y, rotation.z); scene.add(centerMesh); return { starLite: centerMesh, speed }; } // 实现球体发光 // @param color 颜色的r,g和b值,比如:“123,123,123”; // @returns {Element} 返回canvas对象 export function generateSprite(color: any) { const canvas = document.createElement(‘canvas‘); canvas.width = 16; canvas.height = 16; const context = canvas.getContext(‘2d‘); const gradient = context.createRadialGradient( canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, canvas.width / 2 ); gradient.addColorStop(0, ‘rgba(‘ + color + ‘,1)‘); gradient.addColorStop(0.2, ‘rgba(‘ + color + ‘,1)‘); gradient.addColorStop(0.4, ‘rgba(‘ + color + ‘,.6)‘); gradient.addColorStop(1, ‘rgba(0,0,0,0)‘); context.fillStyle = gradient; context.fillRect(0, 0, canvas.width, canvas.height); return canvas; } // 动画渲染 export function render() { RENDERER.render(SCENE, CAMERA); CENTERBALL.rotation.y -= 0.01; for (let i = 0; i < STARLITES.length; i++) { STARLITES[i].starLite.rotation.z -= STARLITES[i].speed; } CONTROLS.update(); STATS.update(); requestAnimationFrame(render); } // 窗口resize事件 export function resizeRender(doc: any) { // 重新初始化尺寸 CAMERA.aspect = doc.clientWidth / doc.clientHeight; CAMERA.updateProjectionMatrix(); RENDERER.setSize(doc.clientWidth, doc.clientHeight); }
html:
<div class="three-container"> <div id="starry-frame" #starry></div> </div>
css:
.three-container { width: 100%; height: 100%; background: #000 url(src/assets/imgs/img-three/starry_sky_bg.jpg) no-repeat center center; overflow: hidden; } #starry-frame { width: 100%; min-height: 800px; // 这里写了最小高度,因为发现使用100%,没有办法出现效果; position: relative; }
ts
import { Component, OnInit, ViewChild, ElementRef, HostListener } from ‘@angular/core‘; import { initRenderer, initCamera, initScene, initObject, initStats, render, resizeRender } from ‘./base‘; @Component({ selector: ‘app-monitor-new‘, templateUrl: ‘./monitor-new.component.html‘, styleUrls: [‘./monitor-new.component.scss‘] }) export class MonitorNewComponent implements OnInit { @ViewChild(‘starry‘, { static: true }) starry: ElementRef; constructor() {} ngOnInit() { this.init(); } init() { initRenderer(this.starry.nativeElement); initCamera(this.starry.nativeElement); initScene(); initObject(); initStats(this.starry.nativeElement); render(); } // 监听当前窗口大小 @HostListener(‘window:resize‘, [‘$event‘]) onResize(event: any) { resizeRender(this.starry.nativeElement); } }
代码出来了;这里放几个相对于新手友好的几个入门学习网站,我就是看这些学习的:注意以下网站有免费资料和收费资料
http://www.yanhuangxueyuan.com/
当时也参考了这位博主的一些;当然我没有运行他的例子,因为我在他的oribcontrols引入就卡住了;所以他这里也有坑;
https://www.jianshu.com/p/c359480b0e08
至于太阳系的贴图image;百度可以找到,或者参考这位博主的;我就是看他的文章然后修改了一些;