预览
可通过右上角调整参数,进行光影练习
代码
<template>
<div id="body"></div>
</template>
<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min';
/*场景,渲染器,镜头,背景星星,帧率器,第一人称控制*/
let scene, renderer, camera, particleSystem, control;
let Sun,
Mercury, //水星
Venus, //金星
Earth,
Mars,
Jupiter, //木星
Saturn, //土星
Uranus, //天王
Neptune, //海王
stars = [];
const cameraFar = 3000; //镜头视距
export default {
mounted() {
/*renderer*/
//渲染器
renderer = new THREE.WebGLRenderer({ alpha: true })
renderer.physicallyCorrectLights = true;
renderer.shadowMap.enabled = true; //辅助线
renderer.shadowMapSoft = true; //柔和阴影
renderer.setClearColor('black', 1);
renderer.setSize(window.innerWidth, window.innerHeight)
document.getElementById('body').appendChild(renderer.domElement);
/*scene*/
scene = new THREE.Scene();
/*camera*/
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, cameraFar);
camera.position.set(-200, 50, 0);
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);
control = new OrbitControls(camera, renderer.domElement)
control.update()
/*sun skin pic*/
// const texture = new THREE.TextureLoader().load( 'textures/land_ocean_ice_cloud_2048.jpg' );
let sunSkinPic = new THREE.TextureLoader().load(new URL('../assets/sunCore.jpg', import.meta.url), {}, () => {
renderer.render(scene, camera);
});
/*sun*/
Sun = new THREE.Mesh(new THREE.SphereGeometry(12, 16, 16),
new THREE.MeshLambertMaterial({
/*color: 0xffff00,*/
emissive: 0xdd4422,
map: sunSkinPic
})
);
Sun.name = 'Sun';
Sun.castShadow = false;
Sun.receiveShadow = false;
scene.add(Sun);
/*opacity sun*/
let opSun = new THREE.Mesh(new THREE.SphereGeometry(14, 16, 16),
new THREE.MeshLambertMaterial({
side: THREE.DoubleSide,
color: 0xff0000,
/*emissive: 0xdd4422,*/
transparent: true,
opacity: .35
})
);
opSun.name = 'Sun';
scene.add(opSun);
const plane = new THREE.Mesh(new THREE.PlaneGeometry(500, 500),
new THREE.MeshStandardMaterial({
side: THREE.DoubleSide,
color: 0xffffff,
metalness: .7,
roughness: 0.5,
// emissive: 0xffffff,
})
);
plane.rotateX(-Math.PI / 2)
plane.position.y = -30
// plane.castShadow = true;
plane.receiveShadow = true;
scene.add(plane)
//gui
const gui = new GUI()
/*planets*/
Mercury = this.initPlanet('Mercury', 0.02, 0, 'rgb(124,131,203)', 20, 2);
stars.push(Mercury);
Venus = this.initPlanet('Venus', 0.012, 0, 'rgb(190,138,44)', 30, 4);
stars.push(Venus);
Earth = this.initPlanet('Earth', 0.010, 0, 'rgb(46,69,119)', 40, 5);
stars.push(Earth);
Mars = this.initPlanet('Mars', 0.008, 0, 'rgb(210,81,16)', 50, 4);
stars.push(Mars);
Jupiter = this.initPlanet('Jupiter', 0.006, 0, 'rgb(254,208,101)', 70, 9);
stars.push(Jupiter);
Saturn = this.initPlanet('Saturn', 0.005, 0, 'rgb(210,140,39)', 100, 7, {
color: 'rgb(136,75,30)',
innerRedius: 9,
outerRadius: 11
});
stars.push(Saturn);
Uranus = this.initPlanet('Uranus', 0.003, 0, 'rgb(49,168,218)', 120, 4);
stars.push(Uranus);
Neptune = this.initPlanet('Neptune', 0.002, 0, 'rgb(84,125,204)', 150, 3);
stars.push(Neptune);
//环境光
let ambient = new THREE.AmbientLight(0x999999, 3);
scene.add(ambient);
/*太阳光*/
let sunLight = new THREE.PointLight(0xddddaa, 6500, 500);
sunLight.castShadow = true;
sunLight.position.set(0, 0)
// sunLight.scale.set(25,25,25)
opSun.add(sunLight);
let sunAmbient = new THREE.AmbientLight(0x999999, 3);
opSun.add(sunAmbient)
sunLight.shadow.mapSize.set(2048, 2048)
/*背景星星*/
const particles = 20000; //星星数量
/*buffer做星星*/
let bufferGeometry = new THREE.BufferGeometry();
let positions = new Float32Array(particles * 3);
let colors = new Float32Array(particles * 3);
let color = new THREE.Color();
const gap = 1000; // 定义星星的最近出现位置
for (let i = 0; i < positions.length; i += 3) {
// positions
/*-2gap < x < 2gap */
let x = (Math.random() * gap * 2) * (Math.random() < .5 ? -1 : 1);
let y = (Math.random() * gap * 2) * (Math.random() < .5 ? -1 : 1);
let z = (Math.random() * gap * 2) * (Math.random() < .5 ? -1 : 1);
/*找出x,y,z中绝对值最大的一个数*/
let biggest = Math.abs(x) > Math.abs(y) ? Math.abs(x) > Math.abs(z) ? 'x' : 'z' :
Math.abs(y) > Math.abs(z) ? 'y' : 'z';
let pos = { x, y, z };
/*如果最大值比n要小(因为要在一个距离之外才出现星星)则赋值为n(-n)*/
if (Math.abs(pos[biggest]) < gap) pos[biggest] = pos[biggest] < 0 ? -gap : gap;
x = pos['x'];
y = pos['y'];
z = pos['z'];
positions[i] = x;
positions[i + 1] = y;
positions[i + 2] = z;
// colors
/*70%星星有颜色*/
let hasColor = Math.random() > 0.3;
let vx, vy, vz;
if (hasColor) {
vx = (Math.random() + 1) / 2;
vy = (Math.random() + 1) / 2;
vz = (Math.random() + 1) / 2;
} else {
vx = 1;
vy = 1;
vz = 1;
}
/*let vx = ( Math.abs(x) / n*2 ) ;
var vy = ( Math.abs(y) / n*2 ) ;
var vz = ( Math.abs(z) / n*2 ) ;*/
color.setRGB(vx, vy, vz);
colors[i] = color.r;
colors[i + 1] = color.g;
colors[i + 2] = color.b;
}
bufferGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
bufferGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
bufferGeometry.computeBoundingSphere();
/*星星的material*/
let material = new THREE.PointsMaterial({ size: 6, vertexColors: THREE.VertexColors });
particleSystem = new THREE.Points(bufferGeometry, material);
scene.add(particleSystem);
//帮助模块
const axesHelper = new THREE.AxesHelper(500)
scene.add(axesHelper)
axesHelper.visible = false;
const lightHelper = new THREE.PointLightHelper(sunLight, 1, 100);
scene.add(lightHelper);
lightHelper.visible = false;
plane.visible = false;
gui.add(Sun.position, 'y').name('太阳Y轴').min(-100).max(100).onChange((val) => {
opSun.position.y = val;
})
gui.add(sunLight.position, 'y').name('光源Y轴').min(-100).max(100).onChange((val) => {
sunLight.position.y = val;
})
gui.add(sunAmbient, 'intensity').name('太阳环境光强度').min(0).max(10)
gui.add(sunLight, 'intensity').name('太阳点光照强度').min(0).max(20000)
gui.add(sunLight, 'distance').name('太阳点光照距离').min(0).max(20000)
gui.add(sunLight, 'decay').name('太阳点光照衰减').min(0).max(5)
gui.add(plane, 'visible').name('显示平面')
gui.add(axesHelper, 'visible').name('显示坐标轴')
gui.add(lightHelper, 'visible').name('显示光源辅助')
//渲染动画
let run = { run: true };
gui.add(run, 'run')
const animate = () => {
if (run.run) {
/*行星公转*/
stars.forEach(star =>
this.moveEachStar(star)
)
Sun.rotation.y = (Sun.rotation.y == 2 * Math.PI ? 0.0008 * Math.PI : Sun.rotation.y + 0.0008 * Math.PI);
}
requestAnimationFrame(animate)
renderer.render(scene, camera)
lightHelper.update()
}
requestAnimationFrame(animate)
},
methods: {
/*每一颗行星的公转*/
moveEachStar(star) {
star.angle += star.speed;
if (star.angle > Math.PI * star.distance) {
star.angle -= Math.PI * star.distance;
}
star.Mesh.position.set(star.distance * Math.sin(star.angle), 0, star.distance * Math.cos(star.angle));
/*碎星带*/
if (star.ring) {
star.ring.position.set(star.distance * Math.sin(star.angle), 0, star.distance * Math.cos(star.angle));
}
},
initPlanet(name, speed, angle, color, distance, volume, ringMsg) {
let mesh = new THREE.Mesh(new THREE.SphereGeometry(volume, 16, 16),
new THREE.MeshStandardMaterial({
color, metalness: .7,
roughness: 0.5,
})
);
mesh.position.x = distance;
mesh.receiveShadow = true;
mesh.castShadow = true;
mesh.name = name;
/*轨道*/
let track = new THREE.Mesh(new THREE.RingGeometry(distance - 0.2, distance + 0.2, 64, 1),
new THREE.MeshBasicMaterial({ color: 0x888888, side: THREE.DoubleSide })
);
track.rotation.x = - Math.PI / 2;
scene.add(track);
let star = {
name,
speed,
angle,
distance,
volume,
Mesh: mesh
}
/*如果有碎星带*/
if (ringMsg) {
let ring = new THREE.Mesh(new THREE.RingGeometry(ringMsg.innerRedius, ringMsg.outerRadius, 32, 6),
new THREE.MeshBasicMaterial({ color: ringMsg.color, side: THREE.DoubleSide, opacity: .7, transparent: true })
);
ring.name = `Ring of ${name}`;
ring.rotation.x = - Math.PI / 3;
ring.rotation.y = - Math.PI / 4;
scene.add(ring);
star.ring = ring;
}
scene.add(mesh);
return star;
},
}
}
</script>
完整代码
ThreeJs DEMO: 通过行星模型辅助光源阴影的练习项目