window.addEventListener('DOMContentLoaded', function() {
// 初始化场景
const container = document.getElementById('threeBall') || document.body;
const width = container.offsetWidth || window.innerWidth;
const height = container.offsetHeight || window.innerHeight;
const scene = new THREE.Scene();
// 相机
const camera = new THREE.PerspectiveCamera(75, width/height, 0.1, 1000);
camera.position.z = 8;
// 渲染器
const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild(renderer.domElement);
// 环境光和点光源
scene.add(new THREE.AmbientLight(0xffffff, 0.2));
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(5, 5, 5);
scene.add(pointLight);
// 顶点着色器
const vertexShader = `
uniform float uTime;
uniform float uSpeed;
uniform float uIntensity;
uniform float uFlowIntensity;
uniform float uHueSpeed;
varying vec3 vNormal;
varying vec3 vPosition;
varying float vNoise;
varying float vFresnel;
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
float snoise(vec3 v) {
const vec2 C = vec2(1.0/6.0, 1.0/3.0);
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
vec3 i = floor(v + dot(v, C.yyy));
vec3 x0 = v - i + dot(i, C.xxx);
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min(g.xyz, l.zxy);
vec3 i2 = max(g.xyz, l.zxy);
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy;
vec3 x3 = x0 - D.yyy;
i = mod289(i);
vec4 p = permute(permute(permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0));
float n_ = 0.142857142857;
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_);
vec4 x = x_ * ns.x + ns.yyyy;
vec4 y = y_ * ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4(x.xy, y.xy);
vec4 b1 = vec4(x.zw, y.zw);
vec4 s0 = floor(b0) * 2.0 + 1.0;
vec4 s1 = floor(b1) * 2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
vec3 p0 = vec3(a0.xy, h.x);
vec3 p1 = vec3(a0.zw, h.y);
vec3 p2 = vec3(a1.xy, h.z);
vec3 p3 = vec3(a1.zw, h.w);
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
}
vec3 flowNoise(vec3 p) {
float eps = 0.1;
float n = snoise(p);
float nx = snoise(p + vec3(eps, 0.0, 0.0));
float ny = snoise(p + vec3(0.0, eps, 0.0));
float nz = snoise(p + vec3(0.0, 0.0, eps));
vec3 grad = vec3(nx - n, ny - n, nz - n) / eps;
vec3 curl;
curl.x = grad.z - grad.y;
curl.y = grad.x - grad.z;
curl.z = grad.y - grad.x;
return curl * uFlowIntensity;
}
void main() {
vNormal = normal;
vPosition = position;
vec3 viewDir = normalize(cameraPosition - position);
vFresnel = pow(1.0 - abs(dot(normal, viewDir)), 2.0);
vec3 flow = flowNoise(position * 0.8 + uTime * uSpeed * 0.3);
vec3 p = position + flow;
float noise1 = snoise(p * 1.5 + uTime * uSpeed);
float noise2 = snoise(p * 3.0 + uTime * uSpeed * 1.3);
float noise3 = snoise(p * 6.0 + uTime * uSpeed * 1.7);
vNoise = (noise1 * 0.6 + noise2 * 0.3 + noise3 * 0.1);
float displacement = vNoise * uIntensity;
vec3 newPosition = position + normal * displacement;
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`;
// 片元着色器
const fragmentShader = `
uniform float uTime;
uniform float uHueSpeed;
uniform float uGlowIntensity;
uniform vec3 uColor1;
uniform vec3 uColor2;
varying vec3 vNormal;
varying vec3 vPosition;
varying float vNoise;
varying float vFresnel;
vec3 hsl2rgb(vec3 c) {
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0);
return c.z + c.y * (rgb - 0.5) * (1.0 - abs(2.0 * c.z - 1.0));
}
vec3 rgb2hsl(vec3 color) {
float minVal = min(min(color.r, color.g), color.b);
float maxVal = max(max(color.r, color.g), color.b);
float delta = maxVal - minVal;
float h = 0.0;
float s = 0.0;
float l = (maxVal + minVal) / 2.0;
if (delta != 0.0) {
s = l < 0.5 ? delta / (maxVal + minVal) : delta / (2.0 - maxVal - minVal);
if (color.r == maxVal) {
h = (color.g - color.b) / delta + (color.g < color.b ? 6.0 : 0.0);
} else if (color.g == maxVal) {
h = (color.b - color.r) / delta + 2.0;
} else {
h = (color.r - color.g) / delta + 4.0;
}
h /= 6.0;
}
return vec3(h, s, l);
}
void main() {
float hueShift = sin(uTime * uHueSpeed) * 0.1;
vec3 baseColor = mix(uColor1, uColor2, (vPosition.y + 1.0) * 0.5);
vec3 hsl = rgb2hsl(baseColor);
hsl.x += hueShift;
if(hsl.x > 1.0) hsl.x -= 1.0;
if(hsl.x < 0.0) hsl.x += 1.0;
hsl.y = min(hsl.y * 1.5, 0.9);
vec3 color = hsl2rgb(hsl);
float brightness = 0.7 + vNoise * 0.3;
color *= brightness;
float glow = vFresnel * uGlowIntensity * (1.0 + sin(uTime * 2.0) * 0.2);
float core = pow(1.0 - length(vNormal), 2.0) * 0.5;
vec3 finalColor = color + glow * vec3(0.8, 1.0, 1.0) + core * vec3(1.0, 0.8, 0.5);
gl_FragColor = vec4(finalColor, 1.0);
}
`;
// 创建霓虹能量环
const geometry = new THREE.SphereGeometry(2, 96, 96);
const material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms: {
uTime: { value: 0 },
uSpeed: { value: 0.4 },
uIntensity: { value: 0.25 },
uFlowIntensity: { value: 0.3 },
uHueSpeed: { value: 0.2 },
uGlowIntensity: { value: 4.0 },
uColor1: { value: new THREE.Color(0x00ffff) },
uColor2: { value: new THREE.Color(0xff00ff) }
},
transparent: true,
side: THREE.DoubleSide,
wireframe: false
});
const neonHalo = new THREE.Mesh(geometry, material);
scene.add(neonHalo);
// 动画循环
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const elapsedTime = clock.getElapsedTime();
material.uniforms.uTime.value = elapsedTime;
neonHalo.rotation.y = elapsedTime * 0.1;
neonHalo.rotation.x = elapsedTime * 0.05;
renderer.render(scene, camera);
}
animate();
// 响应窗口大小变化
window.addEventListener('resize', () => {
const w = container.offsetWidth || window.innerWidth;
const h = container.offsetHeight || window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
});
});这个能量球的代码有没有可以用到我能量环代码上的地方
最新发布