第30节:大规模地形渲染与LOD技术

概述

大规模地形渲染是开放世界游戏和仿真应用的核心技术挑战。本节将深入探索地形LOD(Level of Detail)系统、分块加载策略、视距剔除算法,以及如何实现无限地形的程序化生成,构建真正意义上的广阔虚拟世界。

以下是项目截图,以及控制台打印信息,
支持源码下载 ! 支持源码下载! 支持源码下载!
在这里插入图片描述

在这里插入图片描述

地形渲染系统架构:

地形渲染引擎
数据管理层
渲染优化层
视觉表现层
地形分块
LOD系统
流式加载
视锥剔除
遮挡剔除
实例化渲染
地形着色
细节纹理
动态贴花
动态调度
性能优化
视觉质量

核心原理深度解析

LOD技术体系

多层次细节渲染是现代地形系统的核心技术:

LOD级别三角形密度使用距离性能影响
LOD0100%0-50m
LOD150%50-100m
LOD225%100-200m
LOD312.5%200m+极低

地形分块策略

基于四叉树的地形分块管理:

  1. 空间分割

    • 递归四叉树分割
    • 动态块加载/卸载
    • 边界裂缝处理
  2. 内存管理

    • LRU缓存淘汰
    • 预加载策略
    • 压缩存储格式

完整代码实现

大规模地形渲染系统


<script>
import { onMounted, onUnmounted, ref, reactive, computed } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GPUComputationRenderer } from 'three/addons/misc/GPUComputationRenderer.js';
// 四叉树地形管理器
class QuadtreeTerrainManager {
  constructor(renderer, camera) {
    this.renderer = renderer;
    this.camera = camera;
    this.scene = new THREE.Scene();
    this.quadtree = new Quadtree();
    this.tileCache = new Map();
    this.visibleTiles = new Set();
    this.tileSize = 256;
    this.maxLOD = 4;
    this.viewDistance = 2000;
    this.setupScene();
  }
  // 初始化场景
  setupScene() {
    // 环境光
    const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
    this.scene.add(ambientLight);
    // 方向光
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
    directionalLight.position.set(100, 100, 50);
    directionalLight.castShadow = true;
    this.scene.add(directionalLight);
    // 天空盒
    this.setupSkybox();
  }
  // 设置天空盒
  setupSkybox() {
    const skyboxGeometry = new THREE.BoxGeometry(10000, 10000, 10000);
    const skyboxMaterials = [
      new THREE.MeshBasicMaterial({ color: 0x87CEEB, side: THREE.BackSide }),
      new THREE.MeshBasicMaterial({ color: 0x87CEEB, side: THREE.BackSide }),
      new THREE.MeshBasicMaterial({ color: 0x87CEEB, side: THREE.BackSide }),
      new THREE.MeshBasicMaterial({ color: 0x87CEEB, side: THREE.BackSide }),
      new THREE.MeshBasicMaterial({ color: 0x87CEEB, side: THREE.BackSide }),
      new THREE.MeshBasicMaterial({ color: 0x87CEEB, side: THREE.BackSide })
    ];
    const skybox = new THREE.Mesh(skyboxGeometry, skyboxMaterials);
    this.scene.add(skybox);
  }
  // 更新可见区块
  updateVisibleTiles() {
    const cameraPosition = this.camera.position;
    const frustum = this.getCameraFrustum();
    this.visibleTiles.clear();
    this.quadtree.getVisibleTiles(cameraPosition, frustum, this.viewDistance, this.visibleTiles);
    this.scheduleTileLoading();
    this.processTileUnloading();
  }
  // 获取相机视锥体
  getCameraFrustum() {
    const frustum = new THREE.Frustum();
    const cameraViewProjectionMatrix = new THREE.Matrix4();
    cameraViewProjectionMatrix.multiplyMatrices(
      this.camera.projectionMatrix,
      this.camera.matrixWorldInverse
    );
    frustum.setFromProjectionMatrix(cameraViewProjectionMatrix);
    return frustum;
  }
  // 调度区块加载
  async scheduleTileLoading() {
    const tilesToLoad = [];
    for (const tileInfo of this.visibleTiles) {
      const tileKey = this.getTileKey(tileInfo);
      if (!this.tileCache.has(tileKey)) {
        tilesToLoad.push(tileInfo);
      }
    }
    // 按距离排序,优先加载近的区块
    tilesToLoad.sort((a, b) => {
      const distA = this.getTileDistance(a, this.camera.position);
      const distB = this.getTileDistance(b, this.camera.position);
      return distA - distB;
    });
    await this.loadTiles(tilesToLoad);
  }
  // 加载多个区块
  async loadTiles(tileInfos) {
    const MAX_CONCURRENT_LOADS = 2;
    const loadPromises = [];
    for (let i = 0; i < tileInfos.length && loadPromises.length < MAX_CONCURRENT_LOADS; i++) {
      const tileInfo = tileInfos[i];
      const loadPromise = this.loadTile(tileInfo);
      loadPromises.push(loadPromise);
    }
    await Promise.all(loadPromises);
    // 继续加载剩余的区块
    if (tileInfos.length > MAX_CONCURRENT_LOADS) {
      await this.loadTiles(tileInfos.slice(MAX_CONCURRENT_LOADS));
    }
  }
  // 加载单个区块
  async loadTile(tileInfo) {
    const tileKey = this.getTileKey(tileInfo);
    try {
      const heightData = await this.generateTileHeightData(tileInfo);
      const tileMesh = this.createTileMesh(tileInfo, heightData);
      this.tileCache.set(tileKey, {
        mesh: tileMesh,
        tileInfo: tileInfo,
        lastUsed: Date.now()
      });
      this.scene.add(tileMesh);
    } catch (error) {
      console.error(`Failed to load tile ${tileKey}:`, error);
    }
  }
  // 生成区块高度数据
  async generateTileHeightData(tileInfo) {
    const { x, y, lod } = tileInfo;
    const size = this.tileSize >> lod; // 根据LOD调整分辨率
    const heightData = new Float32Array(size * size);
    // 使用多频噪声生成地形
    for (let i = 0; i < size; i++) {
      for (let j = 0; j < size; j++) {
        const worldX = (x * this.tileSize + i) * (1 << lod);
        const worldZ = (y * this.tileSize + j) * (1 << lod);
        heightData[i * size + j] = this.sampleHeight(worldX, worldZ);
      }
    }
    return heightData;
  }
  // 采样高度值
  sampleHeight(x, z) {
    let height = 0;
    // 多频噪声叠加
    const octaves = 6;
    let frequency = this.noiseFrequency;
    let amplitude = this.noiseIntensity;
    let persistence = 0.5;
    for (let i = 0; i < octaves; i++) {
      const noiseValue = this.simplexNoise(x * frequency, z * frequency);
      height += noiseValue * amplitude;
      frequency *= 2;
      amplitude *= persistence;
    }
    return height * 100; // 放大高度值
  }
  // 简化版的Simplex噪声
  simplexNoise(x, z) {
    // 这里使用简化实现,实际项目应该使用完整的噪声库
    return Math.sin(x * 0.01) * Math.cos(z * 0.01) * 0.5 + 0.5;
  }
  // 创建区块网格
  createTileMesh(tileInfo, heightData) {
    const { x, y, lod } = tileInfo;
    const size = this.tileSize >> lod;
    const geometry = this.createTileGeometry(heightData, size, lod);
    const material = this.createTileMaterial(tileInfo);
    const mesh = new THREE.Mesh(geometry, material);
    // 计算世界位置
    const worldX = x * this.tileSize * (1 << lod);
    const worldZ = y * this.tileSize * (1 << lod);
    mesh.position.set(worldX, 0, worldZ);
    mesh.castShadow = true;
    mesh.receiveShadow = true;
    mesh.userData = { tileInfo };
    return mesh;
  }
  // 创建区块几何体
  createTileGeometry(heightData, size, lod) {
    const geometry = new THREE.PlaneGeometry(
      this.tileSize * (1 << lod),
      this.tileSize * (1 << lod),
      size - 1,
      size - 1
    );
    const vertices = geometry.attributes.position.array;
    // 应用高度数据
    for (let i = 0, j = 0; i < vertices.length; i += 3, j++) {
      vertices[i + 1] = heightData[j];
    }
    geometry.rotateX(-Math.PI / 2); // 从XY平面转到XZ平面
    geometry.computeVertexNormals();
    return geometry;
  }
  // 创建区块材质
  createTileMaterial(tileInfo) {
    const { lod } = tileInfo;
    // 根据LOD级别使用不同复杂度的材质
    if (lod === 0) {
      return new THREE.MeshStandardMaterial({
        color: 0x7cfc00, // 绿色
        roughness: 0.8,
        metalness: 0.2
      });
    } else {
      return new THREE.MeshStandardMaterial({
        color: 0x8B4513, // 棕色
        roughness: 0.9,
        metalness: 0.1,
        wireframe: lod > 2 // 高LOD级别使用线框模式
      });
    }
  }
  // 处理区块卸载
  processTileUnloading() {
    const now = Date.now();
    const unusedTimeout = 30000; // 30秒未使用
    for (const [tileKey, tileData] of this.tileCache) {
      if (!this.visibleTiles.has(tileData.tileInfo) &&
          now - tileData.lastUsed > unusedTimeout) {
        this.unloadTile(tileKey);
      }
    }
  }
  // 卸载区块
  unloadTile(tileKey) {
    const tileData = this.tileCache.get(tileKey);
    if (tileData) {
      this.scene.remove(tileData.mesh);
      tileData.mesh.geometry.dispose();
      tileData.mesh.material.dispose();
      this.tileCache.delete(tileKey);
    }
  }
  // 获取区块键值
  getTileKey(tileInfo) {
    return `${tileInfo.x},${tileInfo.y},${tileInfo.lod}`;
  }
  // 获取区块距离
  getTileDistance(tileInfo, cameraPos) {
    const worldX = tileInfo.x * this.tileSize * (1 << tileInfo.lod);
    const worldZ = tileInfo.y * this.tileSize * (1 << tileInfo.lod);
    return Math.sqrt(
      Math.pow(worldX - cameraPos.x, 2) +
      Math.pow(worldZ - cameraPos.z, 2)
    );
  }
  // 渲染场景
  render() {
    this.renderer.render(this.scene, this.camera);
  }
}
// 四叉树实现
class Quadtree {
  constructor() {
    this.root = new QuadtreeNode(0, 0, 0);
    this.maxDepth = 6;
  }
  // 获取可见区块
  getVisibleTiles(cameraPos, frustum, viewDistance, visibleTiles) {
    this.collectVisibleTiles(this.root, cameraPos, frustum, viewDistance, visibleTiles);
  }
  // 收集可见区块
  collectVisibleTiles(node, cameraPos, frustum, viewDistance, visibleTiles) {
    if (!this.isNodeVisible(node, cameraPos, frustum, viewDistance)) {
      return;
    }
    if (this.shouldSubdivide(node, cameraPos)) {
      this.subdivideNode(node);
      for (const child of node.children) {
        this.collectVisibleTiles(child, cameraPos, frustum, viewDistance, visibleTiles);
      }
    } else {
      visibleTiles.add({
        x: node.x,
        y: node.y,
        lod: node.lod
      });
    }
  }
  // 检查节点是否可见
  isNodeVisible(node, cameraPos, frustum, viewDistance) {
    const nodeSize = 256 * (1 << node.lod);
    const nodeCenter = new THREE.Vector3(
      (node.x + 0.5) * nodeSize,
      0,
      (node.y + 0.5) * nodeSize
    );
    const distance = cameraPos.distanceTo(nodeCenter);
    if (distance > viewDistance) {
      return false;
    }
    // 简化的视锥体检测
    const boundingBox = new THREE.Box3(
      new THREE.Vector3(node.x * nodeSize, -1000, node.y * nodeSize),
      new THREE.Vector3((node.x + 1) * nodeSize, 1000, (node.y + 1) * nodeSize)
    );
    return frustum.intersectsBox(boundingBox);
  }
  // 判断是否应该细分节点
  shouldSubdivide(node, cameraPos) {
    if (node.lod >= 4) return false; // 最大LOD级别
    const nodeSize = 256 * (1 << node.lod);
    const nodeCenter = new THREE.Vector3(
      (node.x + 0.5) * nodeSize,
      0,
      (node.y + 0.5) * nodeSize
    );
    const distance = cameraPos.distanceTo(nodeCenter);
    const threshold = nodeSize * 2; // 细分阈值
    return distance < threshold;
  }
  // 细分节点
  subdivideNode(node) {
    if (node.children.length > 0) return; // 已经细分过了
    const childLod = node.lod + 1;
    const childSize = 1 << childLod;
    for (let i = 0; i < 2; i++) {
      for (let j = 0; j < 2; j++) {
        const childX = node.x * 2 + i;
        const childY = node.y * 2 + j;
        node.children.push(new QuadtreeNode(childX, childY, childLod));
      }
    }
  }
}
// 四叉树节点
class QuadtreeNode {
  constructor(x, y, lod) {
    this.x = x;
    this.y = y;
    this.lod = lod;
    this.children = [];
  }
}
// GPU地形计算器
class GPUTerrainComputer {
  constructor(renderer) {
    this.renderer = renderer;
    this.gpuCompute = null;
    this.initGPUCompute();
  }
  initGPUCompute() {
    this.gpuCompute = new GPUComputationRenderer(1024, 1024, this.renderer);
    // 创建高度场计算着色器
    const heightFieldShader = `
      uniform float time;
      uniform float noiseFrequency;
      uniform float noiseIntensity;
      void main() {
        vec2 uv = gl_FragCoord.xy / resolution.xy;
        // 多频噪声计算
        float height = 0.0;
        float frequency = noiseFrequency;
        float amplitude = noiseIntensity;
        for (int i = 0; i < 6; i++) {
          vec2 samplePos = uv * frequency;
          float noiseValue = simplexNoise(samplePos);
          height += noiseValue * amplitude;
          frequency *= 2.0;
          amplitude *= 0.5;
        }
        gl_FragColor = vec4(height, 0.0, 0.0, 1.0);
      }
      // 简化版Simplex噪声
      float simplexNoise(vec2 pos) {
        return (sin(pos.x * 10.0) * cos(pos.y * 10.0) + 1.0) * 0.5;
      }
    `;
    const heightFieldVariable = this.gpuCompute.addVariable(
      "textureHeight",
      heightFieldShader,
      new Float32Array(1024 * 1024)
    );
    this.gpuCompute.setVariableDependencies(heightFieldVariable, [heightFieldVariable]);
    this.gpuCompute.init();
  }
  // 计算高度场
  computeHeightField(noiseFrequency, noiseIntensity) {
    const variable = this.gpuCompute.variables[0];
    variable.material.uniforms.noiseFrequency = { value: noiseFrequency };
    variable.material.uniforms.noiseIntensity = { value: noiseIntensity };
    this.gpuCompute.compute();
    return this.gpuCompute.getCurrentRenderTarget(variable).texture;
  }
}
export default {
  name: 'LargeScaleTerrain',
  setup() {
    const terrainCanvas = ref(null);
    const viewDistance = ref(2000);
    const lodLevel = ref(2);
    const terrainQuality = ref('high');
    const renderedTiles = ref(0);
    const triangleCount = ref(0);
    const currentFPS = ref(0);
    const memoryUsage = ref(0);
    const noiseIntensity = ref(1.0);
    const noiseFrequency = ref(0.01);
    const isLoading = ref(false);
    const loadingMessage = ref('');
    const cameraPos = reactive({ x: 0, y: 0, z: 0 });
    const loadedTiles = ref(0);
    const totalTiles = ref(0);
    const culledTiles = ref(0);
    const lodDistribution = ref('');
    let scene, camera, renderer, controls;
    let terrainManager, gpuTerrainComputer;
    let stats, clock;
    let frameCount = 0;
    let lastFpsUpdate = 0;
    // 初始化场景
    const initScene = async () => {
      // 创建场景
      scene = new THREE.Scene();
      scene.background = new THREE.Color(0x87CEEB);
      scene.fog = new THREE.Fog(0x87CEEB, 500, 5000);
      // 创建相机
      camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        10000
      );
      camera.position.set(0, 100, 200);
      // 创建渲染器
      renderer = new THREE.WebGLRenderer({
        canvas: terrainCanvas.value,
        antialias: true,
        powerPreference: "high-performance"
      });
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
      renderer.shadowMap.enabled = true;
      renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      // 添加控制器
      controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
      controls.dampingFactor = 0.05;
      controls.minDistance = 10;
      controls.maxDistance = 5000;
      // 初始化地形管理器
      terrainManager = new QuadtreeTerrainManager(renderer, camera);
      // 初始化GPU计算
      gpuTerrainComputer = new GPUTerrainComputer(renderer);
      // 启动渲染循环
      clock = new THREE.Clock();
      animate();
    };
    // 生成程序地形
    const generateProcedural = async () => {
      isLoading.value = true;
      loadingMessage.value = '正在生成程序地形...';
      // 模拟生成过程
      for (let progress = 0; progress <= 100; progress += 10) {
        loadingMessage.value = `生成地形中... ${progress}%`;
        await new Promise(resolve => setTimeout(resolve, 200));
      }
      isLoading.value = false;
    };
    // 加载高度图
    const loadHeightMap = async () => {
      isLoading.value = true;
      loadingMessage.value = '正在加载高度图...';
      // 模拟加载过程
      await new Promise(resolve => setTimeout(resolve, 2000));
      isLoading.value = false;
    };
    // 清空地形
    const clearTerrain = () => {
      // 实现地形清空逻辑
      console.log('清空地形');
    };
    // 动画循环
    const animate = () => {
      requestAnimationFrame(animate);
      const deltaTime = clock.getDelta();
      // 更新控制器
      controls.update();
      // 更新相机位置
      cameraPos.x = camera.position.x;
      cameraPos.y = camera.position.y;
      cameraPos.z = camera.position.z;
      // 更新地形管理器
      if (terrainManager) {
        terrainManager.updateVisibleTiles();
        terrainManager.render();
      }
      // 更新性能统计
      updatePerformanceStats(deltaTime);
    };
    // 更新性能统计
    const updatePerformanceStats = (deltaTime) => {
      frameCount++;
      lastFpsUpdate += deltaTime;
      if (lastFpsUpdate >= 1.0) {
        currentFPS.value = Math.round(frameCount / lastFpsUpdate);
        // 模拟统计数据
        renderedTiles.value = Math.floor(Math.random() * 50) + 20;
        triangleCount.value = renderedTiles.value * 5000;
        memoryUsage.value = triangleCount.value * 32; // 估算内存使用
        loadedTiles.value = renderedTiles.value + Math.floor(Math.random() * 10);
        totalTiles.value = 100;
        culledTiles.value = Math.floor(Math.random() * 20);
        lodDistribution.value = 'LOD0:10 LOD1:15 LOD2:20 LOD3:5';
        frameCount = 0;
        lastFpsUpdate = 0;
      }
    };
    // 格式化数字
    const formatNumber = (num) => {
      if (num >= 1000000) {
        return (num / 1000000).toFixed(1) + 'M';
      } else if (num >= 1000) {
        return (num / 1000).toFixed(1) + 'K';
      }
      return num.toString();
    };
    // 格式化内存大小
    const formatMemory = (bytes) => {
      if (bytes >= 1024 * 1024) {
        return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
      } else if (bytes >= 1024) {
        return (bytes / 1024).toFixed(1) + ' KB';
      }
      return bytes + ' B';
    };
    // 进度条样式
    const progressStyle = computed(() => ({
      width: '100%' // 简化实现
    }));
    // 响应式设置
    watch(viewDistance, (newDistance) => {
      if (terrainManager) {
        terrainManager.viewDistance = newDistance;
      }
    });
    watch(noiseIntensity, (newIntensity) => {
      // 更新噪声强度
      if (terrainManager) {
        terrainManager.noiseIntensity = newIntensity;
      }
    });
    watch(noiseFrequency, (newFrequency) => {
      // 更新噪声频率
      if (terrainManager) {
        terrainManager.noiseFrequency = newFrequency;
      }
    });
    onMounted(() => {
      initScene();
      window.addEventListener('resize', handleResize);
    });
    onUnmounted(() => {
      if (renderer) {
        renderer.dispose();
      }
      window.removeEventListener('resize', handleResize);
    });
    const handleResize = () => {
      if (!camera || !renderer) return;
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    };
    return {
      terrainCanvas,
      viewDistance,
      lodLevel,
      terrainQuality,
      renderedTiles,
      triangleCount,
      currentFPS,
      memoryUsage,
      noiseIntensity,
      noiseFrequency,
      isLoading,
      loadingMessage,
      cameraPos,
      loadedTiles,
      totalTiles,
      culledTiles,
      lodDistribution,
      progressStyle,
      generateProcedural,
      loadHeightMap,
      clearTerrain,
      formatNumber,
      formatMemory
    };
  }
};
</script>

高级地形特性

动态地形变形系统

class DynamicTerrainSystem {
constructor(terrainManager) {
this.terrainManager = terrainManager;
this.modifications = new Map();
this.gpuCompute = null;
this.initModificationSystem();
}
// 初始化变形系统
initModificationSystem() {
// 创建GPU计算渲染器用于实时地形变形
this.gpuCompute = new GPUComputationRenderer(
1024, 1024, this.terrainManager.renderer
);
this.setupModificationShaders();
}
// 设置变形着色器
setupModificationShaders() {
const modificationShader = `
uniform sampler2D baseHeightmap;
uniform sampler2D modifications;
uniform vec3 modificationCenter;
uniform float modificationRadius;
uniform float modificationStrength;
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
float baseHeight = texture2D(baseHeightmap, uv).r;
vec4 modification = texture2D(modifications, uv);
// 计算到变形中心的距离
vec2 worldPos = uv * 1024.0; // 假设世界大小1024单位
float distanceToCenter = length(worldPos - modificationCenter.xz);
if (distanceToCenter < modificationRadius) {
// 应用变形影响
float influence = 1.0 - smoothstep(0.0, modificationRadius, distanceToCenter);
baseHeight += modificationStrength * influence * modification.r;
}
gl_FragColor = vec4(baseHeight, 0.0, 0.0, 1.0);
}
`;
// 配置GPU计算变量
const modificationVariable = this.gpuCompute.addVariable(
"textureHeight",
modificationShader,
new Float32Array(1024 * 1024)
);
this.gpuCompute.setVariableDependencies(modificationVariable, [modificationVariable]);
this.gpuCompute.init();
}
// 应用地形变形
applyModification(center, radius, strength, type = 'raise') {
const modification = {
center: center,
radius: radius,
strength: strength * (type === 'raise' ? 1 : -1),
timestamp: Date.now()
};
const modificationKey = `${center.x},${center.z}`;
this.modifications.set(modificationKey, modification);
// 标记受影响的地形块需要更新
this.markAffectedTilesForUpdate(center, radius);
// 执行GPU计算更新地形
this.updateTerrainHeightmap();
}
// 标记受影响的地形块
markAffectedTilesForUpdate(center, radius) {
const affectedTiles = new Set();
for (const [tileKey, tileData] of this.terrainManager.tileCache) {
if (this.isTileAffected(tileData, center, radius)) {
affectedTiles.add(tileKey);
}
}
// 重新生成受影响的地形块
this.regenerateAffectedTiles(affectedTiles);
}
// 检查地形块是否受影响
isTileAffected(tileData, center, radius) {
const tileBounds = this.getTileBounds(tileData.tileInfo);
const distance = this.pointToRectDistance(center, tileBounds);
return distance <= radius;
}
// 重新生成受影响的地形块
async regenerateAffectedTiles(affectedTiles) {
for (const tileKey of affectedTiles) {
const tileData = this.terrainManager.tileCache.get(tileKey);
if (tileData) {
await this.terrainManager.regenerateTile(tileData.tileInfo);
}
}
}
// 更新地形高度图
updateTerrainHeightmap() {
// 收集所有变形数据
const modificationsArray = Array.from(this.modifications.values());
// 更新GPU计算uniforms
const variable = this.gpuCompute.variables[0];
modificationsArray.forEach((mod, index) => {
variable.material.uniforms[`modificationCenter${index}`] = { value: mod.center };
variable.material.uniforms[`modificationRadius${index}`] = { value: mod.radius };
variable.material.uniforms[`modificationStrength${index}`] = { value: mod.strength };
});
variable.material.uniforms.modificationCount = { value: modificationsArray.length };
// 执行计算
this.gpuCompute.compute();
}
}

无限地形生成器

class InfiniteTerrainGenerator {
constructor(terrainManager) {
this.terrainManager = terrainManager;
this.seed = Math.random() * 1000;
this.biomes = new Map();
this.setupBiomes();
}
// 设置生物群落
setupBiomes() {
this.biomes.set('plains', {
heightRange: [0, 50],
color: 0x7cfc00,
treeDensity: 0.1,
noiseScale: 0.005
});
this.biomes.set('mountains', {
heightRange: [100, 500],
color: 0x8B4513,
treeDensity: 0.01,
noiseScale: 0.002
});
this.biomes.set('forest', {
heightRange: [20, 80],
color: 0x228B22,
treeDensity: 0.3,
noiseScale: 0.008
});
this.biomes.set('desert', {
heightRange: [0, 30],
color: 0xF4A460,
treeDensity: 0.02,
noiseScale: 0.01
});
}
// 获取生物群落
getBiomeAt(x, z) {
const temperature = this.sampleTemperature(x, z);
const humidity = this.sampleHumidity(x, z);
if (temperature > 0.7 && humidity < 0.3) return 'desert';
if (temperature > 0.5 && humidity > 0.6) return 'forest';
if (temperature < 0.3) return 'mountains';
return 'plains';
}
// 采样温度
sampleTemperature(x, z) {
return (this.noise(x * 0.001, z * 0.001) + 1) * 0.5;
}
// 采样湿度
sampleHumidity(x, z) {
return (this.noise(x * 0.0005, z * 0.0005) + 1) * 0.5;
}
// 生成地形高度
generateTerrainHeight(x, z) {
const biome = this.getBiomeAt(x, z);
const biomeConfig = this.biomes.get(biome);
let height = 0;
// 基础地形噪声
height += this.ridgedNoise(x * biomeConfig.noiseScale, z * biomeConfig.noiseScale) * 100;
// 添加细节噪声
height += this.noise(x * 0.01, z * 0.01) * 20;
height += this.noise(x * 0.05, z * 0.05) * 5;
// 应用生物群落高度范围
const [minHeight, maxHeight] = biomeConfig.heightRange;
height = Math.max(minHeight, Math.min(maxHeight, height));
return height;
}
// 脊状噪声(用于山脉)
ridgedNoise(x, z) {
const value = 1 - Math.abs(this.noise(x, z));
return value * value;
}
// 多频噪声
noise(x, z) {
let total = 0;
let frequency = 1;
let amplitude = 1;
let maxValue = 0;
for (let i = 0; i < 6; i++) {
total += this.simplexNoise(x * frequency, z * frequency) * amplitude;
maxValue += amplitude;
amplitude *= 0.5;
frequency *= 2;
}
return total / maxValue;
}
// Simplex噪声实现
simplexNoise(x, z) {
// 简化版的Simplex噪声
// 实际项目应该使用完整的噪声实现
const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);
const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;
const s = (x + z) * F2;
const i = Math.floor(x + s);
const j = Math.floor(z + s);
const t = (i + j) * G2;
const X0 = i - t;
const Z0 = j - t;
const x0 = x - X0;
const z0 = z - Z0;
// 简化计算,返回随机值
return Math.sin(i * 12.9898 + j * 78.233) * 43758.5453 % 1 * 2 - 1;
}
}

注意事项与最佳实践

  1. 性能优化关键

    • 使用四叉树进行空间分割
    • 实现高效的视锥体剔除
    • 采用动态LOD系统
    • 使用GPU计算进行地形生成
  2. 内存管理策略

    • 实现LRU缓存淘汰机制
    • 使用压缩格式存储地形数据
    • 动态加载和卸载地形块
    • 监控内存使用情况
  3. 视觉质量优化

    • 实现无缝LOD过渡
    • 使用细节纹理和法线贴图
    • 应用适当的光照和阴影
    • 添加环境效果(雾、大气散射)

下一节预告

第31节:流体模拟与Shader实现水效果
将深入探索基于Shader的流体模拟技术,包括:波浪方程实现、法线贴图生成、交互式水波纹、以及实时流体物理计算,创造逼真的水域效果。