Skip to content

Commit 047889f

Browse files
committed
feat(sun): 实现太阳自定义着色器火焰动画效果
- 将太阳的渲染方式从基础材质改为使用自定义 shader 来模拟火焰闪动效果,增强视觉表现 - 同时调整了三层光晕的尺寸参数,并在动画循环中增加对太阳相关元素的更新逻辑,以支持动态效果
1 parent 79c9189 commit 047889f

1 file changed

Lines changed: 153 additions & 10 deletions

File tree

index.html

Lines changed: 153 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,24 +1015,166 @@
10151015

10161016
/**
10171017
* 创建太阳及其光晕效果
1018-
* 太阳由主体和三层光晕组成,营造发光效果,光晕会在动画中产生呼吸效果
1018+
* 太阳使用自定义shader实现表面火焰闪动效果,配合多层光晕营造发光效果
10191019
*/
10201020
createSun() {
10211021
const sunGeometry = new THREE.SphereGeometry(
10221022
3,
1023-
32,
1024-
32
1023+
64,
1024+
64
10251025
)
1026-
const sunMaterial = new THREE.MeshBasicMaterial({
1027-
color: 0xffd700,
1028-
emissive: 0xffd700,
1029-
emissiveIntensity: 1,
1026+
1027+
// 自定义shader实现火焰闪动效果
1028+
const sunVertexShader = `
1029+
varying vec3 vPosition;
1030+
varying vec3 vNormal;
1031+
varying vec2 vUv;
1032+
1033+
void main() {
1034+
vPosition = position;
1035+
vNormal = normalize(normalMatrix * normal);
1036+
vUv = uv;
1037+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
1038+
}
1039+
`
1040+
1041+
const sunFragmentShader = `
1042+
// based on https://www.shadertoy.com/view/lsf3RH by trisomie21
1043+
1044+
uniform float uTime;
1045+
uniform vec3 uSunColor;
1046+
uniform vec3 uFlameColor;
1047+
1048+
varying vec3 vPosition;
1049+
varying vec3 vNormal;
1050+
varying vec2 vUv;
1051+
1052+
// 3D噪声函数 by trisomie21
1053+
float snoise(vec3 uv, float res) {
1054+
const vec3 s = vec3(1e0, 1e2, 1e4);
1055+
1056+
uv *= res;
1057+
1058+
vec3 uv0 = floor(mod(uv, res)) * s;
1059+
vec3 uv1 = floor(mod(uv + vec3(1.0), res)) * s;
1060+
1061+
vec3 f = fract(uv);
1062+
f = f * f * (3.0 - 2.0 * f);
1063+
1064+
vec4 v = vec4(uv0.x + uv0.y + uv0.z, uv1.x + uv0.y + uv0.z,
1065+
uv0.x + uv1.y + uv0.z, uv1.x + uv1.y + uv0.z);
1066+
1067+
vec4 r = fract(sin(v * 1e-3) * 1e5);
1068+
float r0 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);
1069+
1070+
r = fract(sin((v + uv1.z - uv0.z) * 1e-3) * 1e5);
1071+
float r1 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);
1072+
1073+
return mix(r0, r1, f.z) * 2.0 - 1.0;
1074+
}
1075+
1076+
void main() {
1077+
// 模拟音频频率(使用时间变化代替)
1078+
float freqs[4];
1079+
freqs[0] = 0.3 + sin(uTime * 2.0) * 0.2;
1080+
freqs[1] = 0.4 + sin(uTime * 3.0) * 0.3;
1081+
freqs[2] = 0.5 + sin(uTime * 4.0) * 0.25;
1082+
freqs[3] = 0.3 + sin(uTime * 5.0) * 0.2;
1083+
1084+
float brightness = freqs[1] * 0.25 + freqs[2] * 0.25;
1085+
float radius = 0.24 + brightness * 0.2;
1086+
float invRadius = 1.0 / radius;
1087+
1088+
vec3 orange = vec3(0.8, 0.65, 0.3);
1089+
vec3 orangeRed = vec3(0.8, 0.35, 0.1);
1090+
float time = uTime * 0.1;
1091+
1092+
// 使用UV坐标,适配到球体
1093+
vec2 uv = vUv;
1094+
vec2 p = -0.5 + uv;
1095+
float aspect = 1.0;
1096+
p.x *= aspect;
1097+
1098+
float fade = pow(length(2.0 * p), 0.5);
1099+
float fVal1 = 1.0 - fade;
1100+
float fVal2 = 1.0 - fade;
1101+
1102+
float angle = atan(p.x, p.y) / 6.2832;
1103+
float dist = length(p);
1104+
vec3 coord = vec3(angle, dist, time * 0.1);
1105+
1106+
float newTime1 = abs(snoise(coord + vec3(0.0, -time * (0.35 + brightness * 0.001), time * 0.015), 15.0));
1107+
float newTime2 = abs(snoise(coord + vec3(0.0, -time * (0.15 + brightness * 0.001), time * 0.015), 45.0));
1108+
1109+
for (int i = 1; i <= 7; i++) {
1110+
float power = pow(2.0, float(i + 1));
1111+
fVal1 += (0.5 / power) * snoise(coord + vec3(0.0, -time, time * 0.2), (power * (10.0) * (newTime1 + 1.0)));
1112+
fVal2 += (0.5 / power) * snoise(coord + vec3(0.0, -time, time * 0.2), (power * (25.0) * (newTime2 + 1.0)));
1113+
}
1114+
1115+
float corona = pow(fVal1 * max(1.1 - fade, 0.0), 2.0) * 50.0;
1116+
corona += pow(fVal2 * max(1.1 - fade, 0.0), 2.0) * 50.0;
1117+
corona *= 1.2 - newTime1;
1118+
1119+
vec3 starSphere = vec3(0.0);
1120+
1121+
vec2 sp = -1.0 + 2.0 * uv;
1122+
sp.x *= aspect;
1123+
sp *= (2.0 - brightness);
1124+
float r = dot(sp, sp);
1125+
float f = (1.0 - sqrt(abs(1.0 - r))) / (r) + brightness * 0.5;
1126+
1127+
if (dist < radius) {
1128+
corona *= pow(dist * invRadius, 24.0);
1129+
vec2 newUv;
1130+
newUv.x = sp.x * f;
1131+
newUv.y = sp.y * f;
1132+
newUv += vec2(time, 0.0);
1133+
1134+
// 使用噪声代替纹理采样
1135+
vec3 texSample = vec3(
1136+
snoise(vec3(newUv, time), 10.0) * 0.5 + 0.5,
1137+
snoise(vec3(newUv, time + 1.0), 10.0) * 0.5 + 0.5,
1138+
snoise(vec3(newUv, time + 2.0), 10.0) * 0.5 + 0.5
1139+
);
1140+
1141+
float uOff = (texSample.g * brightness * 4.5 + time);
1142+
vec2 starUV = newUv + vec2(uOff, 0.0);
1143+
starSphere = vec3(
1144+
snoise(vec3(starUV, time), 8.0) * 0.5 + 0.5,
1145+
snoise(vec3(starUV, time + 1.0), 8.0) * 0.5 + 0.5,
1146+
snoise(vec3(starUV, time + 2.0), 8.0) * 0.5 + 0.5
1147+
) * 0.3;
1148+
}
1149+
1150+
float starGlow = min(max(1.0 - dist * (1.0 - brightness), 0.0), 1.0);
1151+
1152+
vec3 finalColor = vec3(f * (0.75 + brightness * 0.3) * orange) + starSphere + corona * orange + starGlow * orangeRed;
1153+
1154+
gl_FragColor = vec4(finalColor, 1.0);
1155+
}
1156+
`
1157+
1158+
const sunMaterial = new THREE.ShaderMaterial({
1159+
uniforms: {
1160+
uTime: { value: 0.0 },
1161+
uSunColor: {
1162+
value: new THREE.Color(0xffd700),
1163+
},
1164+
uFlameColor: {
1165+
value: new THREE.Color(0xff4500),
1166+
},
1167+
},
1168+
vertexShader: sunVertexShader,
1169+
fragmentShader: sunFragmentShader,
10301170
})
1171+
10311172
const sun = new THREE.Mesh(sunGeometry, sunMaterial)
10321173
this.scene.add(sun)
1174+
this.sun = sun // 保存引用用于更新uniform
10331175

10341176
const glowGeometry1 = new THREE.SphereGeometry(
1035-
3.5,
1177+
3.2,
10361178
32,
10371179
32
10381180
)
@@ -1048,7 +1190,7 @@
10481190
this.scene.add(sunGlow1)
10491191

10501192
const glowGeometry2 = new THREE.SphereGeometry(
1051-
4.2,
1193+
3.4,
10521194
32,
10531195
32
10541196
)
@@ -1064,7 +1206,7 @@
10641206
this.scene.add(sunGlow2)
10651207

10661208
const glowGeometry3 = new THREE.SphereGeometry(
1067-
5.0,
1209+
3.8,
10681210
32,
10691211
32
10701212
)
@@ -1550,6 +1692,7 @@
15501692
planet.mesh.rotation.y += 0.01 * this.speed
15511693
})
15521694

1695+
// 更新太阳光晕动画
15531696
if (this.sunGlows) {
15541697
this.sunGlows.forEach((glow, index) => {
15551698
const time = Date.now() * 0.001

0 commit comments

Comments
 (0)