<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<script src="./main.js"></script>
</body>
</html>
index.html
main.js
package.json
assets
现在支持上传本地图片了!
index.html
main.js
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
let scene, camera, renderer, controls, stars, starGeo, clock = new THREE.Clock();
const starCount = 10000;
const positions = new Float32Array(starCount * 3);
const colors = new Float32Array(starCount * 3);
init();
animate();
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000010);
camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 1, 1000
);
camera.position.z = 10;
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 辅助
const helper = new THREE.AxesHelper(30);
scene.add(helper);
// 交互
controls = new OrbitControls(camera, renderer.domElement);
// 创建几何体
starGeo = new THREE.BufferGeometry();
for (let i = 0; i < starCount; i++) {
// 三轴均匀分布,形成一个立方体壳(缩小可查看)
const x = THREE.MathUtils.randFloatSpread(400); // spread ±200
const y = THREE.MathUtils.randFloatSpread(400);
const z = THREE.MathUtils.randFloatSpread(400);
// set(array, offset)
positions.set([x, y, z], i * 3);
// 多层次星星颜色:90% 冷白,10% 暖色
const starColor = new THREE.Color();
if (Math.random() < 0.9) {
// 冷色(偏蓝白、紫白)
const h = 0.58 + Math.random() * 0.07; // 色相偏蓝紫
const s = 0.3 + Math.random() * 0.3; // 饱和度较低
const l = 0.85 + Math.random() * 0.15; // 明亮
starColor.setHSL(h, s, l);
} else {
// 暖色(微黄或红)
const h = 0.05 + Math.random() * 0.05; // 色相偏橙红
const s = 0.5 + Math.random() * 0.4;
const l = 0.75 + Math.random() * 0.15;
starColor.setHSL(h, s, l);
}
colors.set([starColor.r, starColor.g, starColor.b], i * 3);
}
starGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
starGeo.setAttribute('color', new THREE.BufferAttribute(colors, 3));
// 材质
const sprite = new THREE.TextureLoader().load('./assets/disc.png');
const starMaterial = new THREE.PointsMaterial({
// 纹理 * color 就是最终的展示颜色
// 白色纹理rgb(1,1,1) * color 都是color本身
map: sprite,
transparent: true,
opacity: 0.9,
// 顶点着色:每个顶点有自己的颜色,同时需要attr中存在color分量
vertexColors: true
});
// Points 对象
stars = new THREE.Points(starGeo, starMaterial);
scene.add(stars);
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 动画更新
function animate() {
requestAnimationFrame(animate);
// 穿梭场景
for(let i = 0; i < starCount; i++) {
const zindex = i * 3 + 2;
positions[zindex] += 1.2 + clock.getDelta();
if (positions[zindex] > camera.position.z + 10) {
positions[i * 3] = THREE.MathUtils.randFloatSpread(400);
positions[i * 3 + 1] = THREE.MathUtils.randFloatSpread(400);
positions[zindex] = -200;
}
}
starGeo.attributes.position.needsUpdate = true;
controls.update();
renderer.render(scene, camera);
}
编辑器加载中
package.json
注意:新添加的依赖包首次加载可能会报错,稍后再次刷新即可
{
"dependencies": {
"three": "0.173.0"
}
}
编辑器加载中
预览页面