当前位置: 首页 > news >正文

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

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

概述

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

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

在这里插入图片描述

地形渲染系统架构:

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

核心原理深度解析

LOD技术体系

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

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

地形分块策略

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

  1. 空间分割

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

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

完整代码实现

大规模地形渲染系统

<template><div class="terrain-container"><!-- 主渲染画布 --><canvas ref="terrainCanvas" class="terrain-canvas"></canvas><!-- 地形控制面板 --><div class="terrain-controls"><div class="control-section"><h3>地形渲染设置</h3><div class="control-group"><label>视距: {{ viewDistance }}m</label><input type="range" v-model="viewDistance" min="500" max="10000" step="100"></div><div class="control-group"><label>LOD级别: {{ lodLevel }}</label><input type="range" v-model="lodLevel" min="0" max="4" step="1"></div><div class="control-group"><label>地形精度: {{ terrainQuality }}</label><select v-model="terrainQuality"><option value="low">低</option><option value="medium">中</option><option value="high">高</option><option value="ultra">超高</option></select></div></div><div class="control-section"><h3>性能监控</h3><div class="performance-stats"><div class="stat-item"><span>渲染区块:</span><span>{{ renderedTiles }}</span></div><div class="stat-item"><span>三角形数量:</span><span>{{ formatNumber(triangleCount) }}</span></div><div class="stat-item"><span>帧率:</span><span>{{ currentFPS }} FPS</span></div><div class="stat-item"><span>内存使用:</span><span>{{ formatMemory(memoryUsage) }}</span></div></div></div><div class="control-section"><h3>地形生成</h3><div class="generation-controls"><button @click="generateProcedural" class="control-button">🏔️ 生成程序地形</button><button @click="loadHeightMap" class="control-button">📁 加载高度图</button><button @click="clearTerrain" class="control-button">🧹 清空地形</button></div><div class="noise-controls"><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>噪声频率: {{ noiseFrequency }}</label><input type="range" v-model="noiseFrequency" min="0.001" max="0.1" step="0.001"></div></div></div></div><!-- 调试信息 --><div class="debug-overlay"><div class="debug-info"><div>相机位置: X:{{ cameraPos.x.toFixed(1) }} Y:{{ cameraPos.y.toFixed(1) }} Z:{{ cameraPos.z.toFixed(1) }}</div><div>加载区块: {{ loadedTiles }} / 总区块: {{ totalTiles }}</div><div>视锥剔除: {{ culledTiles }} 个区块被剔除</div><div>LOD分布: {{ lodDistribution }}</div></div></div><!-- 加载进度 --><div v-if="isLoading" class="loading-overlay"><div class="loading-content"><div class="terrain-loader"><div class="mountain"></div><div class="mountain"></div><div class="mountain"></div></div><h3>正在生成地形...</h3><div class="loading-progress"><div class="progress-bar"><div class="progress-fill" :style="progressStyle"></div></div><span class="progress-text">{{ loadingMessage }}</span></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';
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><style scoped>
.terrain-container {width: 100%;height: 100vh;position: relative;background: #000;
}.terrain-canvas {width: 100%;height: 100%;display: block;
}.terrain-controls {position: absolute;top: 20px;right: 20px;width: 300px;background: rgba(0, 0, 0, 0.8);padding: 20px;border-radius: 10px;color: white;backdrop-filter: blur(10px);border: 1px solid rgba(255, 255, 255, 0.1);
}.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: #00ffff;margin-bottom: 15px;font-size: 16px;
}.control-group {margin-bottom: 15px;
}.control-group label {display: block;margin-bottom: 5px;color: #ccc;font-size: 14px;
}.control-group input[type="range"],
.control-group select {width: 100%;padding: 8px;border: 1px solid #444;border-radius: 4px;background: #333;color: white;
}.performance-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: #00ffff;font-weight: bold;
}.generation-controls {display: flex;flex-direction: column;gap: 10px;margin-bottom: 15px;
}.control-button {padding: 10px;border: none;border-radius: 6px;background: linear-gradient(45deg, #667eea, #764ba2);color: white;cursor: pointer;font-size: 14px;transition: transform 0.2s;
}.control-button:hover {transform: translateY(-2px);
}.noise-controls {margin-top: 15px;
}.debug-overlay {position: absolute;bottom: 20px;left: 20px;background: rgba(0, 0, 0, 0.7);padding: 15px;border-radius: 8px;color: white;font-size: 12px;backdrop-filter: blur(10px);
}.debug-info {display: flex;flex-direction: column;gap: 5px;
}.loading-overlay {position: absolute;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0, 0, 0, 0.9);display: flex;justify-content: center;align-items: center;z-index: 1000;
}.loading-content {text-align: center;color: white;
}.terrain-loader {position: relative;width: 120px;height: 60px;margin: 0 auto 30px;
}.mountain {position: absolute;bottom: 0;width: 0;height: 0;border-left: 30px solid transparent;border-right: 30px solid transparent;border-bottom: 50px solid #4a5568;animation: float 3s ease-in-out infinite;
}.mountain:nth-child(1) {left: 0;animation-delay: 0s;
}.mountain:nth-child(2) {left: 40px;animation-delay: 0.5s;border-bottom-color: #2d3748;
}.mountain:nth-child(3) {left: 80px;animation-delay: 1s;border-bottom-color: #1a202c;
}.loading-content h3 {color: #00ffff;margin-bottom: 20px;
}.loading-progress {width: 300px;margin: 0 auto;
}.progress-bar {width: 100%;height: 6px;background: rgba(255, 255, 255, 0.1);border-radius: 3px;overflow: hidden;margin-bottom: 10px;
}.progress-fill {height: 100%;background: linear-gradient(90deg, #00ffff, #0088ff);border-radius: 3px;transition: width 0.3s ease;
}.progress-text {color: #00aaff;font-size: 14px;
}@keyframes float {0%, 100% { transform: translateY(0); }50% { transform: translateY(-10px); }
}
</style>

高级地形特性

动态地形变形系统

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计算uniformsconst 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的流体模拟技术,包括:波浪方程实现、法线贴图生成、交互式水波纹、以及实时流体物理计算,创造逼真的水域效果。

http://www.dtcms.com/a/601274.html

相关文章:

  • Goer-Docker系列1-容器数据持久化
  • 天机学堂——day1(修改bug)
  • 国外网站设计欣赏长沙网页设计哪家专业
  • php做图片交互网站代码成都制作网站公司
  • AI应用开发神器coze(扣子):使用智能体生成文案和图片
  • Java·如何区别多态中的“重写”与“重载”。
  • B端系统自动化MCP工具开发指南
  • 外贸整合营销网站如何快速开发手机app
  • 谢赛宁×李飞飞×LeCun联手重磅|Cambrian-S:「视频空间超感知」新范式,实现真正持续视频感知
  • 在服务器网站上做跳转网站运营推广方式
  • Ansible 安装与入门
  • VMMap 学习笔记(8.7):命令行模式与自动抓取——无界面采集内存证据的正确姿势
  • 大型网站服务器得多少钱app大全免费软件排行榜
  • AXI-5.3.2~5.3.5
  • Anaconda安装与配置:构建人工智能开发环境
  • 从入门到精通:周志华《机器学习》第一、二章深度解析
  • 网站建设品牌策划装修设计软件排名
  • 社区投稿 | Oinone应用于整车制造供应链决策
  • 加强网站建设的制度网站网址怎么找
  • 【Git】Git04:分支管理
  • R语言用什么编译器 | 如何选择最适合你的R语言开发环境
  • cuda12 cudnn9 tensorflow 显卡加速
  • 网站建设目标的文字嘉兴企业网站排名
  • 手机网站开发语言选择怎么能在网上卖货
  • 编程算法学习
  • 在Beego框架中创建Services层
  • PPT插入的音乐怎么让它播放到某一页就停?
  • 打包,压缩解压,上传下载
  • 【动态规划】专题完结,题单汇总
  • 微信公众好第三方网站怎么做wordpress订阅关闭