技术栈: Three.js + GLSL + 程序化几何体 + 数学几何
本篇学习目标
- 掌握程序化几何体生成技术
- 理解球面几何学和六边形密铺
- 学习高级着色器编程
- 实现复杂的扫光动画算法
- 创造科幻感的视觉效果
几何学理论基础
六边形密铺的数学原理
六边形是自然界中最高效的平面密铺形状:
- 蜂巢结构 - 蜜蜂用六边形建造蜂巢
- 数学最优 - 相同面积下周长最小
- 工程应用 - 材料科学、建筑设计广泛应用
- 游戏开发 - 策略游戏的地图系统
球面六边形的挑战
将平面六边形映射到球面上面临的问题:
javascript
// 球面几何的挑战
const sphericalChallenges = {
曲率问题: "平面图形无法完美贴合球面",
极点聚集: "极点附近六边形会过度聚集",
大小变化: "不同纬度的六边形大小不一致",
密铺困难: "球面上无法实现完美的六边形密铺",
};
// 我们的解决方案
const solutions = {
自适应密度: "根据纬度调整六边形数量",
极点特殊处理: "极点附近跳过或减少六边形",
角度尺寸控制: "使用角度而非线性尺寸",
偏移模式: "奇偶行偏移实现更好的密铺",
};
程序化几何体生成
1. 球面网格算法
javascript
// HoneycombGrid.js - 球面网格生成
createHoneycombGeometry() {
const vertices = [];
const indices = [];
const radius = EARTH_RADIUS * 1.001; // 稍大于地球,避免Z-fighting
// 球面参数化
const latitudeDivisions = 192; // 纬度分割数
const longitudeDivisions = 384; // 经度分割数
for (let lat = 0; lat <= latitudeDivisions; lat++) {
// 计算纬度角:0 到 π
const phi = (lat / latitudeDivisions) * Math.PI;
const sinPhi = Math.sin(phi);
const cosPhi = Math.cos(phi);
// 极点特殊处理
if (sinPhi < 0.05) {
if (lat === 0) this.createHexagon(vertices, indices, 0, radius, 0, hexRadius);
if (lat === latitudeDivisions) this.createHexagon(vertices, indices, 0, -radius, 0, hexRadius);
continue;
}
// 自适应经度分割
const lonDivisions = Math.max(3, Math.floor(longitudeDivisions * sinPhi));
for (let lon = 0; lon < lonDivisions; lon++) {
// 六边形偏移模式
const offset = (lat % 2) * 0.5;
const theta = ((lon + offset) / lonDivisions) * Math.PI * 2;
// 球面坐标转换
const x = radius * sinPhi * Math.cos(theta);
const y = radius * cosPhi;
const z = radius * sinPhi * Math.sin(theta);
this.createHexagon(vertices, indices, x, y, z, hexRadius);
}
}
}
2. 六边形生成算法
javascript
// 在球面上创建单个六边形
createHexagon(vertices, indices, centerX, centerY, centerZ, size) {
const center = new THREE.Vector3(centerX, centerY, centerZ);
const normal = center.clone().normalize();
// 建立局部坐标系
const up = Math.abs(normal.y) < 0.9 ?
new THREE.Vector3(0, 1, 0) : new THREE.Vector3(1, 0, 0);
const right = new THREE.Vector3().crossVectors(up, normal).normalize();
const forward = new THREE.Vector3().crossVectors(normal, right).normalize();
// 生成六边形顶点
for (let i = 0; i < 6; i++) {
const angle = (i / 6) * Math.PI * 2;
// 使用角度尺寸确保一致性
const angularSize = size / radius;
const localX = Math.cos(angle) * angularSize;
const localY = Math.sin(angle) * angularSize;
// 计算球面位置
const vertex = center.clone()
.add(right.clone().multiplyScalar(localX * radius))
.add(forward.clone().multiplyScalar(localY * radius));
// 投影到球面
vertex.normalize().multiplyScalar(radius);
vertices.push(vertex.x, vertex.y, vertex.z);
}
// 连接六边形边
for (let i = 0; i < 6; i++) {
const next = (i + 1) % 6;
indices.push(startIndex + i, startIndex + next);
}
}
关键技术点:
- 局部坐标系 - 在球面任意点建立正交坐标系
- 角度尺寸 - 使用角度而非线性距离保证一致性
- 球面投影 - 将局部坐标投影回球面
- 边连接 - 生成线段索引用于线框渲染
扫光着色器系统
1. 扫光算法核心
glsl
// fragmentShader - 扫光计算函数
float calculateLinearSweep(vec3 position, vec3 direction, float time, float speed, float width) {
// 球面坐标标准化
float sphereRadius = length(position);
vec3 normalizedPos = position / sphereRadius;
// 应用旋转变换
float rotationRad = uSweepRotation * 3.14159265 / 180.0;
float cosRot = cos(rotationRad);
float sinRot = sin(rotationRad);
vec3 rotatedPos = vec3(
normalizedPos.x * cosRot - normalizedPos.y * sinRot,
normalizedPos.x * sinRot + normalizedPos.y * cosRot,
normalizedPos.z
);
// 计算扫光坐标
float sweepCoord = rotatedPos.y;
// 创建连续扫光运动
float sweepCycle = mod(time * speed, 4.0); // 4秒周期
float sweepPos = 1.0 - (sweepCycle / 4.0) * 2.0; // 从顶部(1)到底部(-1)
// 计算到扫光线的距离
float distanceFromSweep = abs(sweepCoord - sweepPos);
// 应用平滑过渡
float falloffPower = mix(0.5, 3.0, uSweepFalloff);
float sweepFalloff = 1.0 - smoothstep(0.0, width * 0.5, distanceFromSweep);
sweepFalloff = pow(sweepFalloff, falloffPower);
// 强度衰减计算
float sweepProgress = (1.0 - sweepPos) * 0.5;
float intensityMultiplier = exp(-sweepProgress * uIntensityDecay);
return sweepFalloff * intensityMultiplier;
}
2. 扫光参数系统
javascript
// HoneycombGrid.js - 扫光参数
const sweepConfig = {
// 基础扫光参数
sweepSpeed: 0.42, // 扫光速度 (0.1 - 1.0)
sweepWidth: 0.34, // 扫光宽度 (0.1 - 1.0)
sweepIntensity: 0.33, // 扫光强度 (0.1 - 1.0)
// 视觉效果参数
sweepFalloff: 1.0, // 边缘过渡强度 (0=柔和, 1=锐利)
sweepRotation: 66, // 扫光旋转角度 (0-360度)
intensityDecay: 4.8, // 强度衰减速度 (0=无衰减, 5=快速衰减)
// 颜色和透明度
baseOpacity: 0.01, // 基础透明度
color: "#00ccff", // 扫光颜色
};
3. 拖尾效果算法
glsl
// 扫光拖尾效果
float trailEffect = 0.0;
if (sweepCoord < sweepPos) { // 只在扫光后方产生拖尾
float trailDistance = sweepPos - sweepCoord;
float trailFalloff = mix(2.0, 5.0, uSweepFalloff);
// 指数衰减拖尾
trailEffect = exp(-trailDistance * trailFalloff) *
(0.3 * (1.0 - uSweepFalloff * 0.5));
// 应用强度衰减
trailEffect *= intensityMultiplier;
}
// 合并主扫光和拖尾效果
float intensity = (sweepFalloff * intensityMultiplier) + trailEffect;
视觉效果技术解析
1. 科幻感设计原理
蜂巢网格的科幻感来源于:
- 几何美学 - 六边形的完美对称性
-⚡ 动态扫光 - 持续的能量流动感 - 发光效果 - 加法混合的光晕感
- 技术质感 - 精密的网格结构
2. 扫光动画的视觉层次
javascript
// 扫光效果的多层次设计
const sweepLayers = {
// 主扫光带
mainSweep: {
width: "0.34",
intensity: "0.33",
effect: "主要的亮光带"
},
// 拖尾效果
trailEffect: {
width: "渐变衰减",
intensity: "0.3 × (1 - falloff × 0.5)",
effect: "扫光后的余晖"
},
// 强度衰减
intensityDecay: {
formula: "exp(-progress × decay)",
effect: "从顶部到底部的强度递减"
}
};
3. 颜色和透明度设计
javascript
// 最终颜色计算
const colorCalculation = {
baseOpacity: 0.01, // 极低的基础透明度
sweepHighlight: "动态计算", // 扫光时的高亮
finalOpacity: "base + sweep", // 最终透明度
// 颜色选择
科技蓝: "#00ccff", // 冷色调,科技感
能量绿: "#00ff88", // 绿色,能量感
警告橙: "#ff8800", // 橙色,警告感
};
核心算法详解
1. 球面坐标系统
javascript
// 球面坐标参数化
const sphericalCoordinates = {
// 纬度角:从北极到南极
phi: "lat / latitudeDivisions × π", // [0, π]
// 经度角:围绕地球一周
theta: "lon / lonDivisions × 2π", // [0, 2π]
// 笛卡尔转换
x: "radius × sin(phi) × cos(theta)",
y: "radius × cos(phi)",
z: "radius × sin(phi) × sin(theta)"
};
2. 自适应密度算法
javascript
// 根据纬度调整六边形密度
const adaptiveDensity = (lat, latitudeDivisions, longitudeDivisions) => {
const phi = (lat / latitudeDivisions) * Math.PI;
const sinPhi = Math.sin(phi);
// 密度随纬度变化
const lonDivisions = Math.max(3, Math.floor(longitudeDivisions * sinPhi));
// 密度分布
return {
赤道附近: "最高密度,sinPhi ≈ 1",
中纬度: "中等密度,sinPhi ≈ 0.7",
极地附近: "最低密度,sinPhi ≈ 0.1"
};
};
3. 局部坐标系建立
javascript
// 在球面任意点建立正交坐标系
createLocalCoordinateSystem(center) {
const normal = center.clone().normalize();
// 选择参考向量(避免平行)
const up = Math.abs(normal.y) < 0.9 ?
new THREE.Vector3(0, 1, 0) : // 通常情况
new THREE.Vector3(1, 0, 0); // normal接近Y轴时
// 计算正交基
const right = new THREE.Vector3().crossVectors(up, normal).normalize();
const forward = new THREE.Vector3().crossVectors(normal, right).normalize();
return { normal, right, forward };
}
扫光着色器深度解析
1. 顶点着色器
glsl
// vertexShader - 数据传递
uniform float uTime;
uniform vec3 uSweepDirection;
uniform float uSweepSpeed;
varying vec3 vWorldPosition;
varying vec3 vLocalPosition;
varying vec2 vUv;
varying float vDistanceFromCenter;
void main() {
// 🌍世界坐标计算
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
vWorldPosition = worldPosition.xyz;
vLocalPosition = position;
// 球面UV映射
vec3 normalized = normalize(position);
vUv.x = atan(normalized.z, normalized.x) / (2.0 * PI) + 0.5;
vUv.y = asin(normalized.y) / PI + 0.5;
// 距离计算
vDistanceFromCenter = length(position);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
2. 片段着色器核心算法
glsl
// fragmentShader - 扫光计算
void main() {
// 计算扫光强度
float sweepIntensity = calculateLinearSweep(
vLocalPosition, // 当前位置
uSweepDirection, // 扫光方向
uTime, // 时间
uSweepSpeed, // 速度
uSweepWidth // 宽度
);
// 应用强度倍数
sweepIntensity *= uSweepIntensity;
// 计算最终透明度
float finalOpacity = uBaseOpacity + sweepIntensity;
finalOpacity = clamp(finalOpacity, 0.0, 1.0);
// 输出最终颜色
gl_FragColor = vec4(uColor, finalOpacity);
}
3. 复杂扫光算法
glsl
// 扫光计算的完整算法
float calculateLinearSweep(vec3 position, vec3 direction, float time, float speed, float width) {
// 坐标旋转
float rotationRad = uSweepRotation * PI / 180.0;
vec3 rotatedPos = vec3(
position.x * cos(rotationRad) - position.y * sin(rotationRad),
position.x * sin(rotationRad) + position.y * cos(rotationRad),
position.z
);
// 周期性扫光
float sweepCycle = mod(time * speed, 4.0);
float sweepPos = 1.0 - (sweepCycle / 4.0) * 2.0; // [1, -1]
// 距离计算
float distanceFromSweep = abs(rotatedPos.y - sweepPos);
// 平滑过渡
float falloffPower = mix(0.5, 3.0, uSweepFalloff);
float sweepFalloff = 1.0 - smoothstep(0.0, width * 0.5, distanceFromSweep);
sweepFalloff = pow(sweepFalloff, falloffPower);
// 强度衰减
float sweepProgress = (1.0 - sweepPos) * 0.5;
float intensityMultiplier = exp(-sweepProgress * uIntensityDecay);
return sweepFalloff * intensityMultiplier;
}
参数调节指南
获得最佳扫光效果
科幻扫描效果
javascript
{
sweepSpeed: 0.5, // 较快扫描
sweepWidth: 0.2, // 窄扫光带
sweepIntensity: 0.6, // 高强度
sweepFalloff: 0.8, // 锐利边缘
intensityDecay: 2.0, // 慢衰减
color: "#00ffff" // 青色科技感
}
雷达扫描效果
javascript
{
sweepSpeed: 0.3, // 中等速度
sweepWidth: 0.4, // 宽扫光带
sweepIntensity: 0.4, // 中等强度
sweepFalloff: 0.5, // 柔和边缘
intensityDecay: 3.0, // 中等衰减
color: "#00ff00" // 绿色雷达感
}
能量波动效果
javascript
{
sweepSpeed: 0.8, // 快速扫描
sweepWidth: 0.6, // 很宽扫光带
sweepIntensity: 0.8, // 高强度
sweepFalloff: 0.2, // 很柔和
intensityDecay: 1.0, // 慢衰减
color: "#ff8800" // 橙色能量感
}
技术实现细节
1. 几何体优化
javascript
// 性能优化策略
const geometryOptimization = {
// 顶点数量控制
latitudeDivisions: 192, // 足够精细,不过度密集
longitudeDivisions: 384, // 2:1比例,符合球面特性
// 极点优化
polarOptimization: "极点附近跳过,避免过度聚集",
// 索引优化
indexBuffer: "使用索引缓冲区,减少顶点重复",
// 内存布局
bufferGeometry: "连续内存布局,GPU友好"
};
2. 着色器性能优化
glsl
// 着色器优化技巧
const shaderOptimizations = {
// 预计算常量
"const float PI = 3.14159265359;",
"const float TWO_PI = 6.28318530718;",
// 条件优化
"使用 mix() 替代 if-else",
"使用 step() 和 smoothstep() 进行条件判断",
// 数学优化
"预计算 sin/cos 值",
"使用 mad() 指令优化乘加运算"
};
3. 渲染状态管理
javascript
// 渲染状态优化
const renderState = {
// 材质配置
transparent: true,
depthWrite: false, // 避免透明度问题
blending: THREE.AdditiveBlending, // 加法混合增强发光
// 渲染配置
side: THREE.DoubleSide, // 双面渲染
wireframe: true, // 线框模式显示网格
// 性能配置
frustumCulled: true, // 启用视锥剔除
renderOrder: 1, // 确保在地球之后渲染
};
尝试修改以下参数观察效果:
javascript
// 扫光效果实验
{
sweepSpeed: 0.6, // 尝试 0.2, 0.4, 0.8
sweepWidth: 0.5, // 尝试 0.2, 0.4, 0.6
sweepIntensity: 0.5, // 尝试 0.2, 0.4, 0.8
sweepRotation: 45, // 尝试 0, 45, 90度
}
// 网格密度实验
{
latitudeDivisions: 128, // 尝试 96, 128, 256
longitudeDivisions: 256, // 尝试 192, 256, 512
hexagonSize: 0.15, // 尝试 0.08, 0.12, 0.2
}
高级技术实现
1. 球面UV映射
glsl
// 球面到平面的UV映射
vec3 normalized = normalize(position);
// 球面坐标映射
vUv.x = atan(normalized.z, normalized.x) / (2.0 * PI) + 0.5; // 经度 [0,1]
vUv.y = asin(normalized.y) / PI + 0.5; // 纬度 [0,1]
映射原理:
- atan2函数 - 将XZ平面角度映射到[0, 2π]
- asin函数 - 将Y坐标映射到[-π/2, π/2]
- 归一化 - 转换到[0, 1]范围供纹理采样
2. 旋转矩阵变换
glsl
// 2D旋转矩阵应用
float rotationRad = uSweepRotation * PI / 180.0;
float cosRot = cos(rotationRad);
float sinRot = sin(rotationRad);
// 应用旋转变换
vec3 rotatedPos = vec3(
position.x * cosRot - position.y * sinRot, // 新X坐标
position.x * sinRot + position.y * cosRot, // 新Y坐标
position.z // Z坐标不变
);
3. 指数衰减函数
glsl
// 强度衰减的数学模型
float intensityMultiplier = exp(-sweepProgress * uIntensityDecay);
// 衰减效果对比
const decayComparison = {
"decay = 0": "无衰减,强度恒定",
"decay = 1": "缓慢衰减,底部仍有50%强度",
"decay = 3": "中等衰减,底部约5%强度",
"decay = 5": "快速衰减,底部几乎为0"
};
视觉设计技巧
1. 科幻感营造
javascript
// 科幻视觉的关键要素
const sciFiElements = {
// 几何精确性
geometry: "完美的数学几何体",
// ⚡ 能量流动
animation: "持续的扫光运动",
// 发光效果
glow: "加法混合的光晕",
// 冷色调
color: "青色/蓝色的技术感色彩",
// 动态变化
dynamics: "强度衰减和拖尾效果"
};
2. 层次感设计
javascript
// 当前的完整视觉层次
const completeVisualHierarchy = {
背景层: "星空背景 (radius: 500)",
装饰层: "轨道星星 (radius: 20-23)",
大气层: "云层效果 (radius: 20.6)",
技术层: "蜂巢网格 (radius: 20.02)", // 🆕 新增
表面层: "夜晚纹理 (radius: 20.01)",
基础层: "地球纹理 (radius: 19.98)"
};
程序化几何体的工程化
1. 模块化设计
javascript
// 几何体生成器的模块化设计
class GeometryGenerator {
// 六边形生成器
static createHexagon(center, size, normal) {
return HexagonGenerator.generate(center, size, normal);
}
// 球面网格生成器
static createSphericalGrid(radius, divisions) {
return SphericalGridGenerator.generate(radius, divisions);
}
// 自适应密度生成器
static createAdaptiveMesh(config) {
return AdaptiveMeshGenerator.generate(config);
}
}
2. 配置驱动设计
javascript
// 完全配置化的蜂巢网格
const honeycombConfig = {
// 几何参数
geometry: {
hexagonSize: 0.12,
latitudeDivisions: 192,
longitudeDivisions: 384,
radius: EARTH_RADIUS * 1.001
},
// 扫光参数
sweep: {
enabled: true,
speed: 0.42,
width: 0.34,
intensity: 0.33,
rotation: 66,
decay: 4.8
},
// 视觉参数
visual: {
color: "#00ccff",
baseOpacity: 0.01,
blending: THREE.AdditiveBlending
}
};
本篇总结
** 核心知识点**
-
程序化几何体
- 球面参数化数学
- 六边形密铺算法
- 自适应密度控制
- 局部坐标系建立
-
高级着色器
- 复杂的数学运算
- 旋转矩阵变换
- 指数衰减函数
- 多效果合成
-
视觉效果设计
- 科幻感营造
- 动态扫光算法
- 强度衰减控制
- 拖尾效果实现
-
工程化实践
- 模块化设计
- 配置驱动开发
- 性能优化策略
- 资源管理