第33节:程序化生成与无限地形算法

第33节:程序化生成与无限地形算法

概述

程序化生成是现代游戏和虚拟世界的核心技术,能够创建无限多样且性能高效的环境。本节将深入探索噪声算法、地形侵蚀模拟、生物群落系统,以及动态流式加载技术,构建真正无限的虚拟世界。

程序化生成系统架构:
程序化生成系统 噪声生成层 地形生成层 生态生成层 动态加载层 多频噪声 域扭曲技术 缓存优化 高度场生成 侵蚀模拟 纹理合成 生物群落 植被分布 生物分布 流式加载 LOD管理 内存管理 无限变化 真实地形 生态环境 无缝体验

核心原理深度解析

噪声算法体系

程序化生成的数学基础:

噪声类型 特性 适用场景 性能
Perlin噪声 平滑连续梯度 基础地形、云层 中等
Simplex噪声 计算效率高 实时效果、细节
Value噪声 计算简单 基础图案、高度
Worley噪声 细胞图案 石头、生物细胞 中等

分形布朗运动(FBM)

通过多频噪声叠加创造自然细节:

复制代码
FBM(p) = noise(p) + 0.5*noise(2p) + 0.25*noise(4p) + ...

完整代码实现

无限程序化地形系统

vue 复制代码
<template>
  <div class="procedural-world-container">
    <!-- 主渲染画布 -->
    <canvas ref="worldCanvas" class="world-canvas"></canvas>
    
    <!-- 生成控制面板 -->
    <div class="generation-controls">
      <div class="control-section">
        <h3>🌍 地形生成设置</h3>
        
        <div class="control-group">
          <label>地形规模: {{ terrainScale }}</label>
          <input 
            type="range" 
            v-model="terrainScale" 
            min="100" 
            max="1000" 
            step="10"
          >
        </div>
        
        <div class="control-group">
          <label>噪声强度: {{ noiseIntensity }}</label>
          <input 
            type="range" 
            v-model="noiseIntensity" 
            min="0" 
            max="2" 
            step="0.1"
          >
        </div>
        
        <div class="control-group">
          <label>细节层级: {{ detailLevels }}</label>
          <input 
            type="range" 
            v-model="detailLevels" 
            min="1" 
            max="8" 
            step="1"
          >
        </div>
        
        <div class="control-group">
          <label>侵蚀强度: {{ erosionStrength }}</label>
          <input 
            type="range" 
            v-model="erosionStrength" 
            min="0" 
            max="1" 
            step="0.05"
          >
        </div>
      </div>

      <div class="control-section">
        <h3>🌳 生态群落设置</h3>
        
        <div class="biome-controls">
          <div class="control-group">
            <label>温度: {{ temperature }}</label>
            <input 
              type="range" 
              v-model="temperature" 
              min="0" 
              max="1" 
              step="0.05"
            >
          </div>
          
          <div class="control-group">
            <label>湿度: {{ humidity }}</label>
            <input 
              type="range" 
              v-model="humidity" 
              min="0" 
              max="1" 
              step="0.05"
            >
          </div>
        </div>
        
        <div class="vegetation-controls">
          <div class="control-group">
            <label>树木密度: {{ treeDensity }}</label>
            <input 
              type="range" 
              v-model="treeDensity" 
              min="0" 
              max="1" 
              step="0.05"
            >
          </div>
          
          <div class="control-group">
            <label>草地密度: {{ grassDensity }}</label>
            <input 
              type="range" 
              v-model="grassDensity" 
              min="0" 
              max="1" 
              step="0.05"
            >
          </div>
        </div>
      </div>

      <div class="control-section">
        <h3>⚙️ 生成算法</h3>
        
        <div class="algorithm-buttons">
          <button 
            @click="setAlgorithm('perlin')" 
            class="algo-button"
            :class="{ active: currentAlgorithm === 'perlin' }"
          >
            Perlin噪声
          </button>
          <button 
            @click="setAlgorithm('simplex')" 
            class="algo-button"
            :class="{ active: currentAlgorithm === 'simplex' }"
          >
            Simplex噪声
          </button>
          <button 
            @click="setAlgorithm('fbm')" 
            class="algo-button"
            :class="{ active: currentAlgorithm === 'fbm' }"
          >
            分形噪声
          </button>
          <button 
            @click="setAlgorithm('domain')" 
            class="algo-button"
            :class="{ active: currentAlgorithm === 'domain' }"
          >
            域扭曲
          </button>
        </div>
        
        <div class="generation-actions">
          <button @click="generateNewWorld" class="action-button primary">
            🎲 重新生成
          </button>
          <button @click="exportWorld" class="action-button">
            💾 导出世界
          </button>
          <button @click="clearWorld" class="action-button danger">
            🗑️ 清空世界
          </button>
        </div>
      </div>

      <div class="control-section">
        <h3>📊 世界统计</h3>
        <div class="world-stats">
          <div class="stat-item">
            <span>生成区块:</span>
            <span>{{ generatedChunks }}</span>
          </div>
          <div class="stat-item">
            <span>顶点数量:</span>
            <span>{{ formatNumber(vertexCount) }}</span>
          </div>
          <div class="stat-item">
            <span>生物群落:</span>
            <span>{{ biomeCount }}</span>
          </div>
          <div class="stat-item">
            <span>帧率:</span>
            <span>{{ currentFPS }} FPS</span>
          </div>
        </div>
      </div>
    </div>

    <!-- 世界信息显示 -->
    <div class="world-info-overlay">
      <div class="info-panel">
        <h4>世界信息</h4>
        <div class="info-content">
          <div>种子: {{ worldSeed }}</div>
          <div>坐标: X{{ currentPos.x }}, Y{{ currentPos.y }}, Z{{ currentPos.z }}</div>
          <div>生物群系: {{ currentBiome }}</div>
          <div>海拔: {{ currentElevation }}m</div>
          <div>温度: {{ currentTemperature }}°C</div>
        </div>
      </div>
    </div>

    <!-- 加载界面 -->
    <div v-if="isGenerating" class="generation-overlay">
      <div class="generation-loader">
        <div class="terrain-preview">
          <div class="mountain-range"></div>
          <div class="mountain-range"></div>
          <div class="mountain-range"></div>
        </div>
        <h3>正在生成无限世界...</h3>
        <div class="generation-progress">
          <div class="progress-bar">
            <div class="progress-fill" :style="progressStyle"></div>
          </div>
          <span class="progress-text">{{ generationMessage }}</span>
        </div>
        <div class="generation-details">
          <span>已生成: {{ loadedChunks }} 个区块</span>
          <span>内存使用: {{ formatMemory(memoryUsage) }}</span>
        </div>
      </div>
    </div>

    <!-- 调试面板 -->
    <div class="debug-panel">
      <button @click="toggleDebug" class="debug-toggle">
        {{ showDebug ? '🔍 隐藏调试' : '🔍 显示调试' }}
      </button>
      
      <div v-if="showDebug" class="debug-info">
        <div>噪声采样: {{ noiseSamples }} 次/帧</div>
        <div>LOD级别: {{ currentLOD }}</div>
        <div>视距: {{ viewDistance }}m</div>
        <div>加载队列: {{ loadingQueue }} 个区块</div>
      </div>
    </div>
  </div>
</template>

<script>
import { onMounted, onUnmounted, ref, reactive, computed } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

// 高级噪声生成系统
class AdvancedNoiseSystem {
  constructor(seed = 12345) {
    this.seed = seed;
    this.permutation = this.generatePermutation(seed);
    this.gradients = this.generateGradients();
  }

  // 生成排列表
  generatePermutation(seed) {
    const permutation = new Array(512);
    const source = new Array(256);
    
    for (let i = 0; i < 256; i++) {
      source[i] = i;
    }
    
    // 使用种子随机化
    this.seedRandom(seed);
    for (let i = 255; i > 0; i--) {
      const j = Math.floor(this.random() * (i + 1));
      [source[i], source[j]] = [source[j], source[i]];
    }
    
    for (let i = 0; i < 512; i++) {
      permutation[i] = source[i & 255];
    }
    
    return permutation;
  }

  // 种子随机数生成器
  seedRandom(seed) {
    this.randomState = seed;
  }

  random() {
    this.randomState = (this.randomState * 9301 + 49297) % 233280;
    return this.randomState / 233280;
  }

  // 生成梯度向量
  generateGradients() {
    const gradients = [];
    for (let i = 0; i < 256; i++) {
      const angle = (i / 256) * Math.PI * 2;
      gradients[i] = new THREE.Vector2(Math.cos(angle), Math.sin(angle));
    }
    return gradients;
  }

  // 2D Perlin噪声
  perlin2D(x, y) {
    const X = Math.floor(x) & 255;
    const Y = Math.floor(y) & 255;
    
    x -= Math.floor(x);
    y -= Math.floor(y);
    
    const u = this.fade(x);
    const v = this.fade(y);
    
    const aa = this.permutation[this.permutation[X] + Y];
    const ab = this.permutation[this.permutation[X] + Y + 1];
    const ba = this.permutation[this.permutation[X + 1] + Y];
    const bb = this.permutation[this.permutation[X + 1] + Y + 1];
    
    const gradAA = this.gradients[aa];
    const gradAB = this.gradients[ab];
    const gradBA = this.gradients[ba];
    const gradBB = this.gradients[bb];
    
    const x1 = this.lerp(u, this.dot(gradAA, x, y), this.dot(gradBA, x - 1, y));
    const x2 = this.lerp(u, this.dot(gradAB, x, y - 1), this.dot(gradBB, x - 1, y - 1));
    
    return this.lerp(v, x1, x2);
  }

  // 3D Perlin噪声
  perlin3D(x, y, z) {
    const X = Math.floor(x) & 255;
    const Y = Math.floor(y) & 255;
    const Z = Math.floor(z) & 255;
    
    x -= Math.floor(x);
    y -= Math.floor(y);
    z -= Math.floor(z);
    
    const u = this.fade(x);
    const v = this.fade(y);
    const w = this.fade(z);
    
    const a = this.permutation[X] + Y;
    const aa = this.permutation[a] + Z;
    const ab = this.permutation[a + 1] + Z;
    const b = this.permutation[X + 1] + Y;
    const ba = this.permutation[b] + Z;
    const bb = this.permutation[b + 1] + Z;
    
    const gradAA = this.grad3D[this.permutation[aa] & 15];
    const gradAB = this.grad3D[this.permutation[ab] & 15];
    const gradBA = this.grad3D[this.permutation[ba] & 15];
    const gradBB = this.grad3D[this.permutation[bb] & 15];
    
    const x1 = this.lerp(u, this.dot3(gradAA, x, y, z), this.dot3(gradBA, x - 1, y, z));
    const x2 = this.lerp(u, this.dot3(gradAB, x, y - 1, z), this.dot3(gradBB, x - 1, y - 1, z));
    const y1 = this.lerp(v, x1, x2);
    
    const x3 = this.lerp(u, this.dot3(gradAA, x, y, z - 1), this.dot3(gradBA, x - 1, y, z - 1));
    const x4 = this.lerp(u, this.dot3(gradAB, x, y - 1, z - 1), this.dot3(gradBB, x - 1, y - 1, z - 1));
    const y2 = this.lerp(v, x3, x4);
    
    return this.lerp(w, y1, y2);
  }

  // 分形布朗运动(FBM)
  fbm2D(x, y, octaves = 4, lacunarity = 2.0, gain = 0.5) {
    let value = 0.0;
    let amplitude = 1.0;
    let frequency = 1.0;
    let maxValue = 0.0;
    
    for (let i = 0; i < octaves; i++) {
      value += this.perlin2D(x * frequency, y * frequency) * amplitude;
      maxValue += amplitude;
      amplitude *= gain;
      frequency *= lacunarity;
    }
    
    return value / maxValue;
  }

  // 域扭曲噪声
  domainWarp2D(x, y, strength = 1.0) {
    const dx = this.perlin2D(x + 123.4, y + 567.8) * strength;
    const dy = this.perlin2D(x - 123.4, y - 567.8) * strength;
    return this.fbm2D(x + dx, y + dy);
  }

  // 工具函数
  fade(t) { return t * t * t * (t * (t * 6 - 15) + 10); }
  lerp(t, a, b) { return a + t * (b - a); }
  dot(g, x, y) { return g.x * x + g.y * y; }
  dot3(g, x, y, z) { return g.x * x + g.y * y + g.z * z; }
}

// 无限地形生成器
class InfiniteTerrainGenerator {
  constructor(renderer, camera, chunkSize = 32, viewDistance = 5) {
    this.renderer = renderer;
    this.camera = camera;
    this.scene = new THREE.Scene();
    
    this.chunkSize = chunkSize;
    this.viewDistance = viewDistance;
    this.chunks = new Map();
    this.loadingQueue = [];
    
    this.noiseSystem = new AdvancedNoiseSystem(Date.now());
    this.biomeSystem = new BiomeSystem();
    
    this.setupScene();
    this.startLoadingSystem();
  }

  // 初始化场景
  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);
  }

  // 启动加载系统
  startLoadingSystem() {
    setInterval(() => this.updateChunks(), 100);
  }

  // 更新区块
  updateChunks() {
    const cameraChunkX = Math.floor(this.camera.position.x / this.chunkSize);
    const cameraChunkZ = Math.floor(this.camera.position.z / this.chunkSize);
    
    // 卸载视野外的区块
    this.unloadDistantChunks(cameraChunkX, cameraChunkZ);
    
    // 加载视野内的区块
    this.loadVisibleChunks(cameraChunkX, cameraChunkZ);
    
    // 处理加载队列
    this.processLoadingQueue();
  }

  // 卸载远处区块
  unloadDistantChunks(cameraChunkX, cameraChunkZ) {
    for (const [chunkKey, chunk] of this.chunks) {
      const [chunkX, chunkZ] = this.parseChunkKey(chunkKey);
      const distance = Math.max(
        Math.abs(chunkX - cameraChunkX),
        Math.abs(chunkZ - cameraChunkZ)
      );
      
      if (distance > this.viewDistance + 1) {
        this.scene.remove(chunk.mesh);
        chunk.mesh.geometry.dispose();
        chunk.mesh.material.dispose();
        this.chunks.delete(chunkKey);
      }
    }
  }

  // 加载可见区块
  loadVisibleChunks(cameraChunkX, cameraChunkZ) {
    for (let x = -this.viewDistance; x <= this.viewDistance; x++) {
      for (let z = -this.viewDistance; z <= this.viewDistance; z++) {
        const chunkX = cameraChunkX + x;
        const chunkZ = cameraChunkZ + z;
        const chunkKey = this.getChunkKey(chunkX, chunkZ);
        
        if (!this.chunks.has(chunkKey) && !this.isInLoadingQueue(chunkKey)) {
          this.loadingQueue.push({ x: chunkX, z: chunkZ, priority: Math.abs(x) + Math.abs(z) });
        }
      }
    }
    
    // 按优先级排序
    this.loadingQueue.sort((a, b) => a.priority - b.priority);
  }

  // 处理加载队列
  async processLoadingQueue() {
    const MAX_CONCURRENT_LOADS = 2;
    const currentLoads = [];
    
    while (this.loadingQueue.length > 0 && currentLoads.length < MAX_CONCURRENT_LOADS) {
      const chunkInfo = this.loadingQueue.shift();
      const loadPromise = this.generateChunk(chunkInfo.x, chunkInfo.z).finally(() => {
        const index = currentLoads.indexOf(loadPromise);
        if (index > -1) currentLoads.splice(index, 1);
      });
      
      currentLoads.push(loadPromise);
    }
  }

  // 生成区块
  async generateChunk(chunkX, chunkZ) {
    const chunkKey = this.getChunkKey(chunkX, chunkZ);
    
    try {
      const heightData = await this.generateHeightData(chunkX, chunkZ);
      const chunkMesh = await this.createChunkMesh(chunkX, chunkZ, heightData);
      
      this.chunks.set(chunkKey, {
        mesh: chunkMesh,
        x: chunkX,
        z: chunkZ,
        heightData: heightData
      });
      
      this.scene.add(chunkMesh);
      
    } catch (error) {
      console.error(`Failed to generate chunk ${chunkKey}:`, error);
    }
  }

  // 生成高度数据
  async generateHeightData(chunkX, chunkZ) {
    const dataSize = this.chunkSize + 1; // 包含边界顶点
    const heightData = new Float32Array(dataSize * dataSize);
    
    const worldScale = 0.01;
    
    for (let x = 0; x < dataSize; x++) {
      for (let z = 0; z < dataSize; z++) {
        const worldX = (chunkX * this.chunkSize + x) * worldScale;
        const worldZ = (chunkZ * this.chunkSize + z) * worldScale;
        
        // 使用FBM生成基础地形
        let height = this.noiseSystem.fbm2D(worldX, worldZ, 6, 2.0, 0.5);
        
        // 添加山脉
        const mountainNoise = this.noiseSystem.fbm2D(worldX * 0.5, worldZ * 0.5, 4, 2.2, 0.6);
        height += Math.max(0, mountainNoise - 0.3) * 2.0;
        
        // 添加丘陵细节
        const hillNoise = this.noiseSystem.fbm2D(worldX * 2.0, worldZ * 2.0, 3, 2.5, 0.4);
        height += hillNoise * 0.3;
        
        // 缩放高度值
        heightData[x * dataSize + z] = height * 50;
      }
    }
    
    return heightData;
  }

  // 创建区块网格
  async createChunkMesh(chunkX, chunkZ, heightData) {
    const dataSize = Math.sqrt(heightData.length);
    const geometry = new THREE.PlaneGeometry(
      this.chunkSize,
      this.chunkSize,
      dataSize - 1,
      dataSize - 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);
    geometry.computeVertexNormals();
    
    // 创建基于生物群落的材质
    const centerX = chunkX * this.chunkSize + this.chunkSize / 2;
    const centerZ = chunkZ * this.chunkSize + this.chunkSize / 2;
    const biome = this.biomeSystem.getBiomeAt(centerX, centerZ, heightData[Math.floor(heightData.length / 2)]);
    
    const material = new THREE.MeshStandardMaterial({
      color: biome.color,
      roughness: biome.roughness,
      metalness: biome.metalness
    });
    
    const mesh = new THREE.Mesh(geometry, material);
    mesh.position.set(chunkX * this.chunkSize, 0, chunkZ * this.chunkSize);
    mesh.castShadow = true;
    mesh.receiveShadow = true;
    
    // 添加植被
    await this.addVegetation(mesh, chunkX, chunkZ, heightData, biome);
    
    return mesh;
  }

  // 添加植被
  async addVegetation(mesh, chunkX, chunkZ, heightData, biome) {
    if (biome.treeDensity > 0) {
      const treeCount = Math.floor(biome.treeDensity * this.chunkSize * this.chunkSize * 0.01);
      
      for (let i = 0; i < treeCount; i++) {
        const localX = Math.floor(Math.random() * this.chunkSize);
        const localZ = Math.floor(Math.random() * this.chunkSize);
        const heightIndex = localX * (this.chunkSize + 1) + localZ;
        
        if (heightData[heightIndex] > 1.0 && Math.random() < 0.3) {
          const tree = this.createTree();
          tree.position.set(
            chunkX * this.chunkSize + localX,
            heightData[heightIndex],
            chunkZ * this.chunkSize + localZ
          );
          this.scene.add(tree);
        }
      }
    }
  }

  // 创建树木
  createTree() {
    const group = new THREE.Group();
    
    // 树干
    const trunkGeometry = new THREE.CylinderGeometry(0.2, 0.3, 3, 8);
    const trunkMaterial = new THREE.MeshStandardMaterial({ color: 0x8B4513 });
    const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
    trunk.position.y = 1.5;
    group.add(trunk);
    
    // 树冠
    const crownGeometry = new THREE.SphereGeometry(1.5, 8, 6);
    const crownMaterial = new THREE.MeshStandardMaterial({ color: 0x228B22 });
    const crown = new THREE.Mesh(crownGeometry, crownMaterial);
    crown.position.y = 4;
    group.add(crown);
    
    return group;
  }

  // 工具函数
  getChunkKey(x, z) { return `${x},${z}`; }
  parseChunkKey(key) { return key.split(',').map(Number); }
  isInLoadingQueue(chunkKey) { 
    return this.loadingQueue.some(chunk => this.getChunkKey(chunk.x, chunk.z) === chunkKey);
  }

  // 渲染场景
  render() {
    this.renderer.render(this.scene, this.camera);
  }
}

// 生物群落系统
class BiomeSystem {
  constructor() {
    this.biomes = this.initializeBiomes();
  }

  // 初始化生物群落
  initializeBiomes() {
    return [
      {
        name: '平原',
        temperature: [0.4, 0.7],
        humidity: [0.3, 0.6],
        elevation: [0, 50],
        color: 0x7CFC00,
        roughness: 0.8,
        metalness: 0.2,
        treeDensity: 0.1
      },
      {
        name: '森林',
        temperature: [0.3, 0.6],
        humidity: [0.5, 0.9],
        elevation: [10, 100],
        color: 0x228B22,
        roughness: 0.7,
        metalness: 0.1,
        treeDensity: 0.4
      },
      {
        name: '沙漠',
        temperature: [0.7, 1.0],
        humidity: [0.0, 0.2],
        elevation: [0, 80],
        color: 0xF4A460,
        roughness: 0.9,
        metalness: 0.3,
        treeDensity: 0.01
      },
      {
        name: '雪山',
        temperature: [0.0, 0.2],
        humidity: [0.2, 0.8],
        elevation: [80, 200],
        color: 0xFFFFFF,
        roughness: 0.6,
        metalness: 0.4,
        treeDensity: 0.0
      },
      {
        name: '海洋',
        temperature: [0.2, 0.9],
        humidity: [0.8, 1.0],
        elevation: [-10, 1],
        color: 0x1E90FF,
        roughness: 0.3,
        metalness: 0.8,
        treeDensity: 0.0
      }
    ];
  }

  // 获取位置的生物群落
  getBiomeAt(x, z, elevation) {
    // 简化实现:使用噪声生成温度和湿度
    const temperature = (Math.sin(x * 0.001) + 1) * 0.5;
    const humidity = (Math.cos(z * 0.001) + 1) * 0.5;
    
    // 找到匹配的生物群落
    for (const biome of this.biomes) {
      if (this.isInRange(temperature, biome.temperature) &&
          this.isInRange(humidity, biome.humidity) &&
          this.isInRange(elevation, biome.elevation)) {
        return biome;
      }
    }
    
    // 默认返回平原
    return this.biomes[0];
  }

  // 检查值是否在范围内
  isInRange(value, range) {
    return value >= range[0] && value <= range[1];
  }
}

export default {
  name: 'ProceduralWorld',
  setup() {
    const worldCanvas = ref(null);
    const terrainScale = ref(100);
    const noiseIntensity = ref(1.0);
    const detailLevels = ref(4);
    const erosionStrength = ref(0.0);
    const temperature = ref(0.5);
    const humidity = ref(0.5);
    const treeDensity = ref(0.3);
    const grassDensity = ref(0.7);
    const currentAlgorithm = ref('fbm');
    const generatedChunks = ref(0);
    const vertexCount = ref(0);
    const biomeCount = ref(5);
    const currentFPS = ref(0);
    const worldSeed = ref(Date.now());
    const currentPos = reactive({ x: 0, y: 0, z: 0 });
    const currentBiome = ref('平原');
    const currentElevation = ref(0);
    const currentTemperature = ref(20);
    const isGenerating = ref(true);
    const generationMessage = ref('初始化地形生成器...');
    const loadedChunks = ref(0);
    const memoryUsage = ref(0);
    const showDebug = ref(false);
    const noiseSamples = ref(0);
    const currentLOD = ref(1);
    const viewDistance = ref(5);
    const loadingQueue = ref(0);

    let scene, camera, renderer, controls;
    let terrainGenerator;
    let clock, stats;
    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, 50, 500);

      // 创建相机
      camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      camera.position.set(50, 50, 50);

      // 创建渲染器
      renderer = new THREE.WebGLRenderer({
        canvas: worldCanvas.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 = 500;

      // 初始化地形生成器
      generationMessage.value = '创建噪声系统...';
      terrainGenerator = new InfiniteTerrainGenerator(renderer, camera);
      
      // 模拟生成过程
      await simulateGeneration();
      
      isGenerating.value = false;
      
      // 启动渲染循环
      clock = new THREE.Clock();
      animate();
    };

    // 模拟生成过程
    const simulateGeneration = async () => {
      const steps = [
        '初始化噪声算法...',
        '生成基础地形...',
        '应用侵蚀模拟...',
        '创建生物群落...',
        '生成植被...',
        '优化渲染性能...'
      ];
      
      for (let i = 0; i < steps.length; i++) {
        generationMessage.value = steps[i];
        loadedChunks.value = Math.floor((i + 1) / steps.length * 25);
        memoryUsage.value = loadedChunks.value * 1024 * 1024;
        await new Promise(resolve => setTimeout(resolve, 800));
      }
    };

    // 设置生成算法
    const setAlgorithm = (algorithm) => {
      currentAlgorithm.value = algorithm;
      // 这里应该更新地形生成器的算法
    };

    // 生成新世界
    const generateNewWorld = () => {
      isGenerating.value = true;
      worldSeed.value = Date.now();
      generationMessage.value = '生成新世界中...';
      
      setTimeout(() => {
        isGenerating.value = false;
        generatedChunks.value = 25;
        vertexCount.value = 25000;
      }, 2000);
    };

    // 导出世界
    const exportWorld = () => {
      console.log('导出世界数据...');
      // 实际实现应该序列化世界数据
    };

    // 清空世界
    const clearWorld = () => {
      generatedChunks.value = 0;
      vertexCount.value = 0;
      loadedChunks.value = 0;
      console.log('世界已清空');
    };

    // 切换调试显示
    const toggleDebug = () => {
      showDebug.value = !showDebug.value;
    };

    // 动画循环
    const animate = () => {
      requestAnimationFrame(animate);
      
      const deltaTime = clock.getDelta();
      
      // 更新控制器
      controls.update();
      
      // 更新相机位置
      currentPos.x = Math.floor(camera.position.x);
      currentPos.y = Math.floor(camera.position.y);
      currentPos.z = Math.floor(camera.position.z);
      
      // 更新地形信息
      updateTerrainInfo();
      
      // 渲染场景
      if (terrainGenerator) {
        terrainGenerator.render();
      }
      
      // 更新性能统计
      updatePerformanceStats(deltaTime);
    };

    // 更新地形信息
    const updateTerrainInfo = () => {
      // 简化实现 - 实际应该采样地形数据
      currentElevation.value = Math.floor(20 + Math.sin(currentPos.x * 0.01) * 15);
      currentTemperature.value = Math.floor(15 + Math.cos(currentPos.z * 0.005) * 10);
      
      // 基于位置选择生物群落
      const biomes = ['平原', '森林', '沙漠', '雪山', '海洋'];
      currentBiome.value = biomes[Math.abs(currentPos.x + currentPos.z) % biomes.length];
    };

    // 更新性能统计
    const updatePerformanceStats = (deltaTime) => {
      frameCount++;
      lastFpsUpdate += deltaTime;
      
      if (lastFpsUpdate >= 1.0) {
        currentFPS.value = Math.round(frameCount / lastFpsUpdate);
        
        // 更新模拟数据
        noiseSamples.value = Math.floor(Math.random() * 10000) + 5000;
        loadingQueue.value = Math.floor(Math.random() * 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%'
    }));

    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 {
      worldCanvas,
      terrainScale,
      noiseIntensity,
      detailLevels,
      erosionStrength,
      temperature,
      humidity,
      treeDensity,
      grassDensity,
      currentAlgorithm,
      generatedChunks,
      vertexCount,
      biomeCount,
      currentFPS,
      worldSeed,
      currentPos,
      currentBiome,
      currentElevation,
      currentTemperature,
      isGenerating,
      generationMessage,
      loadedChunks,
      memoryUsage,
      showDebug,
      noiseSamples,
      currentLOD,
      viewDistance,
      loadingQueue,
      progressStyle,
      setAlgorithm,
      generateNewWorld,
      exportWorld,
      clearWorld,
      toggleDebug,
      formatNumber,
      formatMemory
    };
  }
};
</script>

<style scoped>
.procedural-world-container {
  width: 100%;
  height: 100vh;
  position: relative;
  background: #000;
  overflow: hidden;
}

.world-canvas {
  width: 100%;
  height: 100%;
  display: block;
}

.generation-controls {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 320px;
  background: rgba(0, 0, 0, 0.85);
  padding: 20px;
  border-radius: 12px;
  color: white;
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  max-height: 80vh;
  overflow-y: auto;
}

.control-section {
  margin-bottom: 25px;
  padding-bottom: 15px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.control-section:last-child {
  margin-bottom: 0;
  border-bottom: none;
}

.control-section h3 {
  color: #00ff88;
  margin-bottom: 15px;
  font-size: 16px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.control-group {
  margin-bottom: 15px;
}

.control-group label {
  display: block;
  margin-bottom: 8px;
  color: #ccc;
  font-size: 14px;
}

.control-group input[type="range"] {
  width: 100%;
  height: 6px;
  background: #444;
  border-radius: 3px;
  outline: none;
  opacity: 0.7;
  transition: opacity 0.2s;
}

.control-group input[type="range"]:hover {
  opacity: 1;
}

.control-group input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #00ff88;
  cursor: pointer;
  box-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
}

.biome-controls,
.vegetation-controls {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 15px;
  margin-bottom: 15px;
}

.algorithm-buttons {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 15px;
}

.algo-button {
  padding: 10px;
  border: 2px solid #444;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.1);
  color: white;
  cursor: pointer;
  font-size: 12px;
  transition: all 0.3s ease;
}

.algo-button:hover {
  border-color: #00ff88;
  transform: translateY(-1px);
}

.algo-button.active {
  border-color: #00ff88;
  background: rgba(0, 255, 136, 0.2);
  box-shadow: 0 0 15px rgba(0, 255, 136, 0.3);
}

.generation-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.action-button {
  padding: 12px;
  border: none;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.1);
  color: white;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.action-button:hover {
  transform: translateY(-2px);
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}

.action-button.primary {
  background: linear-gradient(45deg, #667eea, #764ba2);
}

.action-button.danger {
  background: linear-gradient(45deg, #ff6b6b, #ee5a24);
}

.world-stats {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.stat-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 0;
  font-size: 14px;
}

.stat-item span:first-child {
  color: #ccc;
}

.stat-item span:last-child {
  color: #00ff88;
  font-weight: bold;
}

.world-info-overlay {
  position: absolute;
  top: 20px;
  left: 20px;
  background: rgba(0, 0, 0, 0.8);
  padding: 15px;
  border-radius: 8px;
  color: white;
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.info-panel h4 {
  color: #00ff88;
  margin-bottom: 10px;
  font-size: 14px;
}

.info-content {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
}

.info-content div {
  display: flex;
  justify-content: space-between;
  gap: 10px;
}

.generation-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.generation-loader {
  text-align: center;
  color: white;
  position: relative;
}

.terrain-preview {
  position: relative;
  width: 200px;
  height: 100px;
  margin: 0 auto 30px;
  background: linear-gradient(to bottom, #1a2a6c, #b21f1f, #fdbb2d);
  border-radius: 10px;
  overflow: hidden;
}

.mountain-range {
  position: absolute;
  bottom: 0;
  width: 60px;
  height: 40px;
  background: linear-gradient(45deg, #2d3748, #4a5568);
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  animation: riseUp 2s infinite ease-in-out;
}

.mountain-range:nth-child(1) {
  left: 20px;
  animation-delay: 0s;
}

.mountain-range:nth-child(2) {
  left: 70px;
  height: 60px;
  animation-delay: 0.5s;
}

.mountain-range:nth-child(3) {
  left: 120px;
  animation-delay: 1s;
}

.generation-loader h3 {
  margin-bottom: 20px;
  color: white;
  font-size: 20px;
}

.generation-progress {
  width: 300px;
  margin: 0 auto 15px;
}

.progress-bar {
  width: 100%;
  height: 6px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 3px;
  overflow: hidden;
  margin-bottom: 10px;
}

.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #00ff88, #00aaff);
  border-radius: 3px;
  transition: width 0.3s ease;
}

.progress-text {
  color: #00ff88;
  font-size: 14px;
}

.generation-details {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #ccc;
}

.debug-panel {
  position: absolute;
  bottom: 20px;
  right: 20px;
  background: rgba(0, 0, 0, 0.7);
  padding: 10px;
  border-radius: 6px;
  backdrop-filter: blur(10px);
}

.debug-toggle {
  padding: 8px 12px;
  border: none;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.1);
  color: white;
  cursor: pointer;
  font-size: 12px;
  transition: background 0.3s;
}

.debug-toggle:hover {
  background: rgba(255, 255, 255, 0.2);
}

.debug-info {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 11px;
  color: #ccc;
}

@keyframes riseUp {
  0%, 100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-5px);
  }
}

/* 响应式设计 */
@media (max-width: 768px) {
  .generation-controls {
    width: 280px;
    right: 10px;
    top: 10px;
    padding: 15px;
  }
  
  .world-info-overlay {
    left: 10px;
    top: 10px;
  }
  
  .biome-controls,
  .vegetation-controls,
  .algorithm-buttons {
    grid-template-columns: 1fr;
  }
}
</style>

高级程序化特性

地形侵蚀模拟系统

javascript 复制代码
// 水力侵蚀模拟器
class HydraulicErosionSimulator {
  constructor(heightMap, size) {
    this.heightMap = heightMap;
    this.size = size;
    this.waterMap = new Float32Array(size * size);
    this.sedimentMap = new Float32Array(size * size);
    
    this.erosionSettings = {
      rainRate: 0.01,
      evaporationRate: 0.001,
      sedimentCapacity: 0.1,
      erosionRate: 0.3,
      depositionRate: 0.1
    };
  }

  // 模拟侵蚀过程
  simulateErosion(iterations = 1000) {
    for (let i = 0; i < iterations; i++) {
      this.addRainfall();
      this.calculateWaterFlow();
      this.transportSediment();
      this.evaporateWater();
    }
  }

  // 添加降雨
  addRainfall() {
    for (let i = 0; i < this.waterMap.length; i++) {
      this.waterMap[i] += this.erosionSettings.rainRate;
    }
  }

  // 计算水流
  calculateWaterFlow() {
    const outflow = new Float32Array(this.size * size * 4); // 四个方向
    
    for (let y = 1; y < this.size - 1; y++) {
      for (let x = 1; x < this.size - 1; x++) {
        const idx = y * this.size + x;
        const height = this.heightMap[idx] + this.waterMap[idx];
        
        // 计算到相邻格子的高度差
        const neighbors = [
          { idx: idx - 1, height: this.heightMap[idx - 1] + this.waterMap[idx - 1], dir: 0 }, // 左
          { idx: idx + 1, height: this.heightMap[idx + 1] + this.waterMap[idx + 1], dir: 1 }, // 右
          { idx: idx - this.size, height: this.heightMap[idx - this.size] + this.waterMap[idx - this.size], dir: 2 }, // 上
          { idx: idx + this.size, height: this.heightMap[idx + this.size] + this.waterMap[idx + this.size], dir: 3 }  // 下
        ];
        
        let totalOutflow = 0;
        for (const neighbor of neighbors) {
          const delta = height - neighbor.height;
          if (delta > 0) {
            const outflowAmount = Math.min(this.waterMap[idx], delta * 0.25);
            outflow[idx * 4 + neighbor.dir] = outflowAmount;
            totalOutflow += outflowAmount;
          }
        }
        
        // 标准化流出量
        if (totalOutflow > 0) {
          for (let dir = 0; dir < 4; dir++) {
            outflow[idx * 4 + dir] *= Math.min(1, this.waterMap[idx] / totalOutflow);
          }
        }
      }
    }
    
    // 应用水流
    this.applyWaterFlow(outflow);
  }

  // 应用水流
  applyWaterFlow(outflow) {
    for (let y = 1; y < this.size - 1; y++) {
      for (let x = 1; x < this.size - 1; x++) {
        const idx = y * this.size + x;
        
        // 计算净流量
        let netFlow = 0;
        netFlow -= outflow[idx * 4 + 0];     // 向左流出
        netFlow -= outflow[idx * 4 + 1];     // 向右流出  
        netFlow -= outflow[idx * 4 + 2];     // 向上流出
        netFlow -= outflow[idx * 4 + 3];     // 向下流出
        
        netFlow += outflow[(idx - 1) * 4 + 1];     // 从右流入
        netFlow += outflow[(idx + 1) * 4 + 0];     // 从左流入
        netFlow += outflow[(idx - this.size) * 4 + 3]; // 从下流入
        netFlow += outflow[(idx + this.size) * 4 + 2]; // 从上流入
        
        this.waterMap[idx] += netFlow;
      }
    }
  }

  // 传输沉积物
  transportSediment() {
    for (let y = 1; y < this.size - 1; y++) {
      for (let x = 1; x < this.size - 1; x++) {
        const idx = y * this.size + x;
        
        if (this.waterMap[idx] > 0) {
          // 计算沉积物容量
          const capacity = this.erosionSettings.sedimentCapacity * this.waterMap[idx];
          
          if (this.sedimentMap[idx] > capacity) {
            // 沉积
            const deposit = (this.sedimentMap[idx] - capacity) * this.erosionSettings.depositionRate;
            this.sedimentMap[idx] -= deposit;
            this.heightMap[idx] += deposit;
          } else {
            // 侵蚀
            const erosion = (capacity - this.sedimentMap[idx]) * this.erosionSettings.erosionRate;
            this.sedimentMap[idx] += erosion;
            this.heightMap[idx] -= erosion;
          }
        }
      }
    }
  }

  // 水分蒸发
  evaporateWater() {
    for (let i = 0; i < this.waterMap.length; i++) {
      this.waterMap[i] *= (1 - this.erosionSettings.evaporationRate);
    }
  }
}

动态生物群落生成

javascript 复制代码
// 高级生物群落生成器
class AdvancedBiomeGenerator {
  constructor(noiseSystem) {
    this.noiseSystem = noiseSystem;
    this.biomeMaps = new Map();
  }

  // 生成生物群落图
  generateBiomeMap(x, z, size) {
    const biomeMap = new Array(size * size);
    
    for (let localZ = 0; localZ < size; localZ++) {
      for (let localX = 0; localX < size; localX++) {
        const worldX = x + localX;
        const worldZ = z + localZ;
        
        // 使用多频噪声生成气候参数
        const temperature = this.sampleTemperature(worldX, worldZ);
        const humidity = this.sampleHumidity(worldX, worldZ);
        const continentalness = this.sampleContinentalness(worldX, worldZ);
        const erosion = this.sampleErosion(worldX, worldZ);
        
        // 确定生物群落
        biomeMap[localZ * size + localX] = this.determineBiome(
          temperature, humidity, continentalness, erosion
        );
      }
    }
    
    return biomeMap;
  }

  // 采样温度
  sampleTemperature(x, z) {
    // 纬度效应 + 季节性变化
    const latitudeEffect = Math.abs(z * 0.0001);
    const baseTemp = 1.0 - latitudeEffect * 2;
    
    // 噪声变化
    const tempNoise = this.noiseSystem.fbm2D(x * 0.001, z * 0.001, 2, 2.0, 0.5) * 0.3;
    
    return Math.max(0, Math.min(1, baseTemp + tempNoise));
  }

  // 采样湿度
  sampleHumidity(x, z) {
    // 基于与海洋的距离和地形
    const distanceToOcean = this.sampleOceanDistance(x, z);
    const humidityNoise = this.noiseSystem.fbm2D(x * 0.002, z * 0.002, 3, 2.0, 0.5);
    
    return Math.max(0, Math.min(1, (1 - distanceToOcean) * 0.7 + humidityNoise * 0.3));
  }

  // 采样大陆性
  sampleContinentalness(x, z) {
    return this.noiseSystem.fbm2D(x * 0.0001, z * 0.0001, 4, 2.0, 0.5);
  }

  // 采样侵蚀度
  sampleErosion(x, z) {
    return this.noiseSystem.fbm2D(x * 0.005, z * 0.005, 2, 2.5, 0.4);
  }

  // 采样海洋距离
  sampleOceanDistance(x, z) {
    const continentNoise = this.noiseSystem.fbm2D(x * 0.00005, z * 0.00005, 6, 2.0, 0.5);
    return Math.abs(continentNoise - 0.5) * 2;
  }

  // 确定生物群落
  determineBiome(temperature, humidity, continentalness, erosion) {
    // 基于Minecraft风格的生物群落确定逻辑
    if (continentalness < 0.1) {
      return this.getOceanBiome(temperature);
    } else if (continentalness < 0.3) {
      return this.getCoastalBiome(temperature, humidity);
    } else {
      return this.getInlandBiome(temperature, humidity, erosion);
    }
  }

  // 获取海洋生物群落
  getOceanBiome(temperature) {
    if (temperature < 0.3) return 'frozen_ocean';
    if (temperature > 0.7) return 'warm_ocean';
    return 'ocean';
  }

  // 获取海岸生物群落
  getCoastalBiome(temperature, humidity) {
    if (temperature < 0.3) return 'snowy_beach';
    if (humidity < 0.3) return 'desert';
    return 'beach';
  }

  // 获取内陆生物群落
  getInlandBiome(temperature, humidity, erosion) {
    if (temperature < 0.15) {
      return erosion > 0.6 ? 'snowy_mountains' : 'snowy_tundra';
    } else if (temperature < 0.3) {
      if (erosion > 0.7) return 'mountains';
      return humidity > 0.5 ? 'taiga' : 'plains';
    } else if (temperature < 0.7) {
      if (humidity < 0.3) return 'desert';
      if (humidity < 0.6) return 'plains';
      if (erosion > 0.6) return 'wooded_mountains';
      return 'forest';
    } else {
      if (humidity < 0.4) return 'desert';
      if (humidity < 0.7) return 'savanna';
      return 'jungle';
    }
  }
}

注意事项与最佳实践

  1. 性能优化关键

    • 使用适当的区块大小和LOD级别
    • 实现高效的流式加载系统
    • 优化噪声算法和缓存策略
    • 使用Web Workers进行后台生成
  2. 内存管理策略

    • 实现LRU缓存管理生成的区块
    • 使用对象池重用几何体和材质
    • 监控内存使用并自动清理
    • 使用压缩格式存储地形数据
  3. 视觉质量优化

    • 使用多频噪声创造自然细节
    • 实现平滑的生物群落过渡
    • 添加适当的纹理和光照
    • 使用后期处理增强视觉效果

下一节预告

第34节:反向运动学与角色动画自然化

将深入探索角色动画技术,包括:反向运动学原理、CCD算法实现、腿部行走约束、自然动作混合,创造逼真的角色动画系统。

相关推荐
小白程序员成长日记1 小时前
2025.11.13 力扣每日一题
算法·leetcode·职场和发展
QDKuz1 小时前
掌握Vue2转Vue3, Options API 转 Composition API
前端·javascript·vue.js
老前端的功夫1 小时前
前端Echarts性能优化:从卡顿到流畅的百万级数据可视化
前端·javascript
进击的野人1 小时前
深入解析localStorage:前端数据持久化的核心技术
前端·javascript
懵圈1 小时前
第2章:项目启动 - 使用Vite脚手架初始化项目与工程化配置
前端
Mh1 小时前
如何优雅的消除“if...else...”
前端·javascript
火鸟22 小时前
给予虚拟成像台尝鲜版十之二,完善支持 HTML 原型模式
前端·html·原型模式·通用代码生成器·给予虚拟成像台·快速原型·rust语言
Learn Beyond Limits2 小时前
Regression vs. Classification|回归vs分类
人工智能·python·算法·ai·分类·数据挖掘·回归
不去幼儿园2 小时前
【强化学习】可证明安全强化学习(Provably Safe RL)算法详细介绍
人工智能·python·算法·安全·机器学习·强化学习