diff --git a/js/globe.js b/js/globe.js index c0d4d5c..5ec9ace 100644 --- a/js/globe.js +++ b/js/globe.js @@ -58,11 +58,12 @@ return 1 - Math.pow(1 - t, 3); } - // 准备粒子数据:startPositions / targetPositions / current positions + // 准备粒子数据:startPositions / targetPositions / current positions / size var geometry = new THREE.BufferGeometry(); var positions = new Float32Array(PARTICLE_COUNT * 3); var startPositions = new Float32Array(PARTICLE_COUNT * 3); var targetPositions = new Float32Array(PARTICLE_COUNT * 3); + var sizes = new Float32Array(PARTICLE_COUNT); // 随机在球壳表面生成点(均匀分布),作为目标位置 function randomPointOnSphere(radius) { @@ -108,19 +109,56 @@ positions[i3] = start.x; positions[i3 + 1] = start.y; positions[i3 + 2] = start.z; + + // 粒子尺寸:每个粒子随机一个基础大小,后续在顶点着色器中再做距离衰减 + // 你可以调节下方这两个数,控制整体粒子大小的分布范围 + sizes[i] = 0.8 + Math.random() * 2.2; // 0.8 ~ 3.0 之间 } geometry.setAttribute( "position", new THREE.BufferAttribute(positions, 3) ); + geometry.setAttribute( + "size", + new THREE.BufferAttribute(sizes, 1) + ); - var material = new THREE.PointsMaterial({ - color: 0x38bdf8, // 粒子主色(可自行调整) - size: 0.08, // 每个粒子在世界中的尺寸 - sizeAttenuation: true, + // 使用自定义着色器,让粒子可以拥有不同大小 + var material = new THREE.ShaderMaterial({ + uniforms: { + uColor: { value: new THREE.Color(0x38bdf8) }, // 粒子主色 + uOpacity: { value: 0.95 }, + uSize: { value: 18.0 }, // 全局尺寸基准,越大粒子越大 + uPixelRatio: { value: Math.min(window.devicePixelRatio || 1, 2) }, + }, + vertexShader: ` + attribute float size; + uniform float uSize; + uniform float uPixelRatio; + void main() { + vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); + gl_Position = projectionMatrix * mvPosition; + // 简单的距离衰减,让远处粒子稍微小一些 + float dist = length(mvPosition.xyz); + float att = 1.0 / (0.1 + dist * 0.35); + gl_PointSize = size * uSize * uPixelRatio * att; + } + `, + fragmentShader: ` + uniform vec3 uColor; + uniform float uOpacity; + void main() { + // 将点渲染成柔和的圆形(不使用默认的方形 point sprite) + vec2 c = gl_PointCoord - vec2(0.5); + float d = length(c); + if (d > 0.5) discard; + float alpha = (1.0 - d * 2.0) * uOpacity; + if (alpha <= 0.0) discard; + gl_FragColor = vec4(uColor, alpha); + } + `, transparent: true, - opacity: 0.95, depthWrite: false, blending: THREE.AdditiveBlending, }); @@ -133,7 +171,7 @@ var innerSphereMat = new THREE.MeshBasicMaterial({ color: 0x0f172a, transparent: true, - opacity: 0.25, + opacity: 0.0, // 表面颜色完全透明,只保留粒子视觉 side: THREE.BackSide, }); var innerSphereMesh = new THREE.Mesh(innerSphereGeom, innerSphereMat); @@ -161,6 +199,10 @@ camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); + // 同步像素比,避免在缩放/视网膜屏下粒子尺寸异常 + if (material && material.uniforms && material.uniforms.uPixelRatio) { + material.uniforms.uPixelRatio.value = Math.min(window.devicePixelRatio || 1, 2); + } } window.addEventListener("resize", onResize);