EG/plugins/user/procedural_terrain_generation/rendering/terrain_renderer.py
2025-12-12 16:16:15 +08:00

1295 lines
47 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
地形渲染器
负责渲染程序化生成的地形
"""
import numpy as np
import math
from typing import Dict, Any, List, Tuple
class TerrainRenderer:
"""
地形渲染器
负责渲染程序化生成的地形,包括网格生成、纹理映射、光照计算等
"""
def __init__(self, plugin):
"""
初始化地形渲染器
Args:
plugin: 程序化地形生成插件实例
"""
self.plugin = plugin
self.enabled = False
self.initialized = False
# 渲染配置
self.render_params = {
'resolution': plugin.config.get('terrain_size', 512),
'lod_levels': plugin.config.get('lod_levels', 5),
'chunk_size': plugin.config.get('chunk_size', 32),
'max_distance': 1000.0,
'lod_distance_factor': 2.0
}
# 光照参数
self.lighting_params = {
'ambient_light': 0.3,
'diffuse_light': 0.7,
'specular_light': 0.2,
'light_direction': [0.5, 0.5, 0.5],
'light_color': [1.0, 1.0, 1.0]
}
# 材质参数
self.material_params = {
'diffuse_texture': True,
'normal_mapping': True,
'specular_mapping': False,
'texture_scale': 1.0,
'tessellation_factor': 1.0
}
# 着色器参数
self.shader_params = {
'vertex_shader': 'terrain_vertex.glsl',
'fragment_shader': 'terrain_fragment.glsl',
'geometry_shader': None,
'tessellation_control_shader': None,
'tessellation_evaluation_shader': None
}
# 渲染状态
self.render_buffers = {}
self.vertex_arrays = {}
self.textures = {}
self.shaders = {}
# LOD管理
self.lod_chunks = {}
self.visible_chunks = set()
# 统计信息
self.stats = {
'chunks_rendered': 0,
'vertices_rendered': 0,
'triangles_rendered': 0,
'render_time': 0.0,
'average_render_time': 0.0,
'lod_switches': 0
}
print("✓ 地形渲染器已创建")
def initialize(self) -> bool:
"""
初始化地形渲染器
Returns:
是否初始化成功
"""
try:
# 初始化渲染资源在实际实现中会初始化GPU资源
self._initialize_render_resources()
self.initialized = True
print("✓ 地形渲染器初始化完成")
return True
except Exception as e:
print(f"✗ 地形渲染器初始化失败: {e}")
import traceback
traceback.print_exc()
return False
def enable(self) -> bool:
"""
启用地形渲染器
Returns:
是否启用成功
"""
try:
if not self.initialized:
print("✗ 地形渲染器未初始化")
return False
self.enabled = True
print("✓ 地形渲染器已启用")
return True
except Exception as e:
print(f"✗ 地形渲染器启用失败: {e}")
import traceback
traceback.print_exc()
return False
def disable(self):
"""禁用地形渲染器"""
try:
self.enabled = False
print("✓ 地形渲染器已禁用")
except Exception as e:
print(f"✗ 地形渲染器禁用失败: {e}")
import traceback
traceback.print_exc()
def finalize(self):
"""清理地形渲染器资源"""
try:
self.disable()
# 清理渲染资源
self._cleanup_render_resources()
self.initialized = False
print("✓ 地形渲染器资源已清理")
except Exception as e:
print(f"✗ 地形渲染器资源清理失败: {e}")
import traceback
traceback.print_exc()
def update(self, dt: float):
"""
更新地形渲染器状态
Args:
dt: 时间增量
"""
try:
if not self.enabled:
return
# 更新LOD
self._update_lod()
# 更新可见区块
self._update_visible_chunks()
except Exception as e:
print(f"✗ 地形渲染器更新失败: {e}")
import traceback
traceback.print_exc()
def _initialize_render_resources(self):
"""初始化渲染资源"""
try:
# 在实际实现中这里会初始化GPU缓冲区、纹理、着色器等
print(" → 初始化渲染缓冲区...")
print(" → 初始化着色器...")
print(" → 初始化纹理...")
# 模拟创建一些基本的渲染资源
self.render_buffers['terrain_vertices'] = []
self.render_buffers['terrain_indices'] = []
self.vertex_arrays['terrain_vao'] = []
self.textures['terrain_diffuse'] = []
self.textures['terrain_normal'] = []
self.shaders['terrain_shader'] = []
except Exception as e:
print(f"✗ 渲染资源初始化失败: {e}")
def _cleanup_render_resources(self):
"""清理渲染资源"""
try:
# 在实际实现中这里会清理GPU资源
self.render_buffers.clear()
self.vertex_arrays.clear()
self.textures.clear()
self.shaders.clear()
self.lod_chunks.clear()
self.visible_chunks.clear()
except Exception as e:
print(f"✗ 渲染资源清理失败: {e}")
def render_terrain(self, camera_position: List[float], view_matrix: np.ndarray,
projection_matrix: np.ndarray) -> bool:
"""
渲染地形
Args:
camera_position: 摄像机位置
view_matrix: 视图矩阵
projection_matrix: 投影矩阵
Returns:
是否渲染成功
"""
try:
if not self.enabled:
print("✗ 地形渲染器未启用")
return False
import time
render_start_time = time.time()
print("✓ 开始渲染地形...")
# 更新摄像机相关数据
self._update_camera_data(camera_position, view_matrix, projection_matrix)
# 准备渲染数据
self._prepare_render_data()
# 执行渲染
self._execute_rendering()
# 更新统计信息
render_time = time.time() - render_start_time
self.stats['render_time'] = render_time
self.stats['chunks_rendered'] += len(self.visible_chunks)
if self.stats['chunks_rendered'] > 0:
self.stats['average_render_time'] = (
self.stats['average_render_time'] * (self.stats['chunks_rendered'] - 1) + render_time
) / self.stats['chunks_rendered']
print(f"✓ 地形渲染完成,耗时: {render_time:.3f}")
return True
except Exception as e:
print(f"✗ 地形渲染失败: {e}")
import traceback
traceback.print_exc()
return False
def _update_camera_data(self, camera_position: List[float], view_matrix: np.ndarray,
projection_matrix: np.ndarray):
"""更新摄像机相关数据"""
try:
# 在实际实现中,这里会更新着色器中的摄像机参数
pass
except Exception as e:
print(f"✗ 摄像机数据更新失败: {e}")
def _prepare_render_data(self):
"""准备渲染数据"""
try:
# 在实际实现中,这里会准备顶点缓冲区、索引缓冲区等
pass
except Exception as e:
print(f"✗ 渲染数据准备失败: {e}")
def _execute_rendering(self):
"""执行渲染"""
try:
# 在实际实现中这里会调用GPU渲染命令
print(" → 绑定着色器程序...")
print(" → 设置光照参数...")
print(" → 绑定纹理...")
print(" → 渲染可见区块...")
# 模拟渲染过程
for chunk_key in self.visible_chunks:
print(f" → 渲染区块 {chunk_key}")
except Exception as e:
print(f"✗ 渲染执行失败: {e}")
def _update_lod(self):
"""更新LOD级别"""
try:
# 在实际实现中这里会根据摄像机距离更新每个区块的LOD级别
pass
except Exception as e:
print(f"✗ LOD更新失败: {e}")
def _update_visible_chunks(self):
"""更新可见区块"""
try:
# 在实际实现中,这里会根据视锥体剔除确定可见区块
if self.plugin.terrain_manager:
terrain_data = self.plugin.terrain_manager.get_terrain_data()
if terrain_data.get('is_generating', False):
# 如果地形正在生成,暂时不更新可见区块
return
# 获取可见区块列表
visible_chunk_keys = self.plugin.terrain_manager.get_visible_chunks()
self.visible_chunks = set(visible_chunk_keys)
except Exception as e:
print(f"✗ 可见区块更新失败: {e}")
def set_render_parameters(self, params: Dict[str, Any]):
"""
设置渲染参数
Args:
params: 渲染参数字典
"""
self.render_params.update(params)
print(f"✓ 渲染参数已更新: {self.render_params}")
def set_lighting_parameters(self, params: Dict[str, Any]):
"""
设置光照参数
Args:
params: 光照参数字典
"""
self.lighting_params.update(params)
print(f"✓ 光照参数已更新: {self.lighting_params}")
def set_material_parameters(self, params: Dict[str, Any]):
"""
设置材质参数
Args:
params: 材质参数字典
"""
self.material_params.update(params)
print(f"✓ 材质参数已更新: {self.material_params}")
def set_shader_parameters(self, params: Dict[str, Any]):
"""
设置着色器参数
Args:
params: 着色器参数字典
"""
self.shader_params.update(params)
print(f"✓ 着色器参数已更新: {self.shader_params}")
def get_stats(self) -> Dict[str, Any]:
"""
获取统计信息
Returns:
统计信息字典
"""
return self.stats.copy()
def reset_stats(self):
"""重置统计信息"""
self.stats = {
'chunks_rendered': 0,
'vertices_rendered': 0,
'triangles_rendered': 0,
'render_time': 0.0,
'average_render_time': 0.0,
'lod_switches': 0
}
print("✓ 地形渲染器统计信息已重置")
def generate_terrain_mesh(self, heightmap: np.ndarray, resolution: int = None) -> Dict[str, np.ndarray]:
"""
生成地形网格
Args:
heightmap: 高度图数据
resolution: 网格分辨率
Returns:
包含顶点、法线、纹理坐标和索引的字典
"""
try:
if resolution is None:
resolution = self.render_params['resolution']
print(f"✓ 开始生成 {resolution}x{resolution} 地形网格...")
# 创建网格数据
vertices = []
normals = []
texcoords = []
indices = []
# 生成顶点数据
height, width = heightmap.shape
scale_x = 1.0 / (width - 1)
scale_y = 1.0 / (height - 1)
# 生成顶点
for y in range(height):
for x in range(width):
# 顶点位置
vertex_x = (x / (width - 1)) * 2.0 - 1.0 # -1 到 1
vertex_y = heightmap[y, x] # 高度值
vertex_z = (y / (height - 1)) * 2.0 - 1.0 # -1 到 1
vertices.extend([vertex_x, vertex_y, vertex_z])
# 纹理坐标
texcoord_u = x / (width - 1)
texcoord_v = 1.0 - y / (height - 1) # 翻转V坐标
texcoords.extend([texcoord_u, texcoord_v])
# 计算法线
normals = self._calculate_normals(heightmap)
# 生成索引(三角形列表)
for y in range(height - 1):
for x in range(width - 1):
# 第一个三角形
indices.append(y * width + x)
indices.append(y * width + x + 1)
indices.append((y + 1) * width + x)
# 第二个三角形
indices.append((y + 1) * width + x)
indices.append(y * width + x + 1)
indices.append((y + 1) * width + x + 1)
# 转换为numpy数组
vertices_array = np.array(vertices, dtype=np.float32)
normals_array = np.array(normals, dtype=np.float32)
texcoords_array = np.array(texcoords, dtype=np.float32)
indices_array = np.array(indices, dtype=np.uint32)
# 更新统计信息
self.stats['vertices_rendered'] = len(vertices) // 3
self.stats['triangles_rendered'] = len(indices) // 3
print(f"✓ 地形网格生成完成: {len(vertices)//3} 顶点, {len(indices)//3} 三角形")
return {
'vertices': vertices_array,
'normals': normals_array,
'texcoords': texcoords_array,
'indices': indices_array
}
except Exception as e:
print(f"✗ 地形网格生成失败: {e}")
import traceback
traceback.print_exc()
return {
'vertices': np.array([], dtype=np.float32),
'normals': np.array([], dtype=np.float32),
'texcoords': np.array([], dtype=np.float32),
'indices': np.array([], dtype=np.uint32)
}
def _calculate_normals(self, heightmap: np.ndarray) -> List[float]:
"""
计算法线
Args:
heightmap: 高度图数据
Returns:
法线数据列表
"""
try:
height, width = heightmap.shape
normals = []
for y in range(height):
for x in range(width):
# 计算相邻点的高度
h_left = heightmap[y, max(0, x-1)]
h_right = heightmap[y, min(width-1, x+1)]
h_up = heightmap[max(0, y-1), x]
h_down = heightmap[min(height-1, y+1), x]
# 计算梯度
dx = (h_right - h_left) * width / 2.0
dy = (h_down - h_up) * height / 2.0
dz = 2.0
# 计算法线
normal = [-dx, -dy, dz]
norm = math.sqrt(normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2])
if norm > 0:
normal = [n/norm for n in normal]
else:
normal = [0.0, 0.0, 1.0]
normals.extend(normal)
return normals
except Exception as e:
print(f"✗ 法线计算失败: {e}")
# 返回默认法线
height, width = heightmap.shape
return [0.0, 0.0, 1.0] * height * width
def generate_terrain_texture(self, heightmap: np.ndarray, biome_map: np.ndarray = None,
moisture_map: np.ndarray = None, temperature_map: np.ndarray = None) -> np.ndarray:
"""
生成地形纹理
Args:
heightmap: 高度图数据
biome_map: 生物群落图数据(可选)
moisture_map: 湿度图数据(可选)
temperature_map: 温度图数据(可选)
Returns:
纹理数据 (RGBA格式)
"""
try:
height, width = heightmap.shape
print(f"✓ 开始生成 {width}x{height} 地形纹理...")
# 创建RGBA纹理数据
texture_data = np.zeros((height, width, 4), dtype=np.uint8)
# 如果有生物群落数据,基于生物群落生成纹理
if biome_map is not None and biome_map.shape == heightmap.shape:
texture_data = self._generate_biome_based_texture(biome_map)
else:
# 基于高度生成基础纹理
texture_data = self._generate_height_based_texture(heightmap)
# 如果有湿度和温度数据,添加细节
if moisture_map is not None and moisture_map.shape == heightmap.shape:
texture_data = self._apply_moisture_effects(texture_data, moisture_map)
if temperature_map is not None and temperature_map.shape == heightmap.shape:
texture_data = self._apply_temperature_effects(texture_data, temperature_map)
print("✓ 地形纹理生成完成")
return texture_data
except Exception as e:
print(f"✗ 地形纹理生成失败: {e}")
import traceback
traceback.print_exc()
# 返回默认纹理(蓝色)
height, width = heightmap.shape
return np.zeros((height, width, 4), dtype=np.uint8)
def _generate_biome_based_texture(self, biome_map: np.ndarray) -> np.ndarray:
"""
基于生物群落生成纹理
Args:
biome_map: 生物群落图数据
Returns:
纹理数据
"""
try:
height, width = biome_map.shape
texture_data = np.zeros((height, width, 4), dtype=np.uint8)
# 生物群落颜色映射
biome_colors = {
0: [0, 0, 128, 255], # ocean - 深蓝色
1: [255, 255, 0, 255], # beach - 黄色
2: [144, 238, 144, 255], # plains - 浅绿色
3: [34, 139, 34, 255], # forest - 绿色
4: [0, 100, 0, 255], # jungle - 深绿色
5: [255, 165, 0, 255], # desert - 橙色
6: [139, 137, 137, 255], # mountain - 灰色
7: [255, 255, 255, 255], # snow - 白色
8: [0, 128, 128, 255], # taiga - 青色
9: [173, 216, 230, 255], # tundra - 淡蓝色
10: [128, 128, 0, 255], # swamp - 橄榄色
11: [255, 215, 0, 255] # savanna - 金色
}
# 为每个像素分配颜色
for y in range(height):
for x in range(width):
biome_id = biome_map[y, x]
color = biome_colors.get(biome_id, [128, 128, 128, 255]) # 默认灰色
texture_data[y, x] = color
return texture_data
except Exception as e:
print(f"✗ 生物群落纹理生成失败: {e}")
height, width = biome_map.shape
return np.zeros((height, width, 4), dtype=np.uint8)
def _generate_height_based_texture(self, heightmap: np.ndarray) -> np.ndarray:
"""
基于高度生成纹理
Args:
heightmap: 高度图数据
Returns:
纹理数据
"""
try:
height, width = heightmap.shape
texture_data = np.zeros((height, width, 4), dtype=np.uint8)
# 为每个像素根据高度分配颜色
for y in range(height):
for x in range(width):
height_value = heightmap[y, x]
# 根据高度选择颜色
if height_value < 0.2:
# 水域 - 蓝色
r, g, b = 0, 0, int(128 + height_value * 127)
elif height_value < 0.3:
# 浅水/海滩 - 浅蓝色
r, g, b = int(height_value * 255), int(height_value * 255), 255
elif height_value < 0.5:
# 平原 - 绿色
r, g, b = 0, int(100 + height_value * 155), 0
elif height_value < 0.7:
# 丘陵 - 棕色
r, g, b = int(139 * height_value), int(69 * height_value), int(19 * height_value)
elif height_value < 0.9:
# 山地 - 灰色
r, g, b = int(100 + height_value * 155), int(100 + height_value * 155), int(100 + height_value * 155)
else:
# 雪山 - 白色
r, g, b = 255, 255, 255
texture_data[y, x] = [r, g, b, 255]
return texture_data
except Exception as e:
print(f"✗ 高度纹理生成失败: {e}")
height, width = heightmap.shape
return np.zeros((height, width, 4), dtype=np.uint8)
def _apply_moisture_effects(self, texture_data: np.ndarray, moisture_map: np.ndarray) -> np.ndarray:
"""
应用湿度效果到纹理
Args:
texture_data: 原始纹理数据
moisture_map: 湿度图数据
Returns:
处理后的纹理数据
"""
try:
processed_texture = texture_data.copy()
height, width = moisture_map.shape
# 根据湿度调整颜色
for y in range(height):
for x in range(width):
moisture_value = moisture_map[y, x]
# 增加蓝色调表示湿润
if moisture_value > 0.6:
processed_texture[y, x, 2] = min(255, processed_texture[y, x, 2] + int(50 * moisture_value))
# 减少红色和绿色通道表示湿润
if moisture_value > 0.5:
processed_texture[y, x, 0] = max(0, processed_texture[y, x, 0] - int(30 * moisture_value))
processed_texture[y, x, 1] = max(0, processed_texture[y, x, 1] - int(30 * moisture_value))
return processed_texture
except Exception as e:
print(f"✗ 湿度效果应用失败: {e}")
return texture_data
def _apply_temperature_effects(self, texture_data: np.ndarray, temperature_map: np.ndarray) -> np.ndarray:
"""
应用温度效果到纹理
Args:
texture_data: 原始纹理数据
temperature_map: 温度图数据
Returns:
处理后的纹理数据
"""
try:
processed_texture = texture_data.copy()
height, width = temperature_map.shape
# 根据温度调整颜色
for y in range(height):
for x in range(width):
temperature_value = temperature_map[y, x]
# 增加热色调表示温暖
if temperature_value > 0.7:
processed_texture[y, x, 0] = min(255, processed_texture[y, x, 0] + int(50 * (temperature_value - 0.7) / 0.3))
# 增加冷色调表示寒冷
if temperature_value < 0.3:
processed_texture[y, x, 2] = min(255, processed_texture[y, x, 2] + int(50 * (0.3 - temperature_value) / 0.3))
return processed_texture
except Exception as e:
print(f"✗ 温度效果应用失败: {e}")
return texture_data
def generate_normal_map(self, heightmap: np.ndarray, strength: float = 1.0) -> np.ndarray:
"""
生成法线贴图
Args:
heightmap: 高度图数据
strength: 法线强度
Returns:
法线贴图数据 (RGB格式)
"""
try:
height, width = heightmap.shape
print(f"✓ 开始生成 {width}x{height} 法线贴图...")
# 创建法线贴图数据
normal_map = np.zeros((height, width, 3), dtype=np.uint8)
# 计算每个像素的法线
for y in range(height):
for x in range(width):
# 计算相邻点的高度
h_left = heightmap[y, max(0, x-1)]
h_right = heightmap[y, min(width-1, x+1)]
h_up = heightmap[max(0, y-1), x]
h_down = heightmap[min(height-1, y+1), x]
# 计算梯度
dx = (h_right - h_left) * strength
dy = (h_down - h_up) * strength
# 计算法线 (转换到0-1范围再转为0-255)
normal_x = (dx + 1.0) * 0.5
normal_y = (dy + 1.0) * 0.5
normal_z = 1.0 # 假设主要朝向摄像机
normal_z = normal_z / math.sqrt(normal_x*normal_x + normal_y*normal_y + normal_z*normal_z)
normal_z = (normal_z + 1.0) * 0.5
# 转换为8位值
normal_map[y, x] = [
int(normal_x * 255),
int(normal_y * 255),
int(normal_z * 255)
]
print("✓ 法线贴图生成完成")
return normal_map
except Exception as e:
print(f"✗ 法线贴图生成失败: {e}")
import traceback
traceback.print_exc()
# 返回默认法线贴图(正面法线)
height, width = heightmap.shape
return np.full((height, width, 3), [128, 128, 255], dtype=np.uint8)
def generate_splat_map(self, biome_map: np.ndarray, num_textures: int = 4) -> np.ndarray:
"""
生成混合贴图(用于多重纹理混合)
Args:
biome_map: 生物群落图数据
num_textures: 纹理数量
Returns:
混合贴图数据 (RGBA格式每个通道代表一种纹理的权重)
"""
try:
height, width = biome_map.shape
print(f"✓ 开始生成 {width}x{height} 混合贴图...")
# 创建混合贴图数据
splat_map = np.zeros((height, width, 4), dtype=np.uint8)
# 简化的混合贴图生成
# 在实际实现中,这里会根据生物群落类型和地形特征生成更复杂的混合
for y in range(height):
for x in range(width):
biome_id = biome_map[y, x]
# 根据生物群落ID分配到不同的纹理通道
channel = biome_id % 4
splat_map[y, x, channel] = 255
print("✓ 混合贴图生成完成")
return splat_map
except Exception as e:
print(f"✗ 混合贴图生成失败: {e}")
import traceback
traceback.print_exc()
# 返回默认混合贴图
height, width = biome_map.shape
return np.zeros((height, width, 4), dtype=np.uint8)
def update_chunk_mesh(self, chunk_x: int, chunk_y: int, lod_level: int,
heightmap: np.ndarray) -> bool:
"""
更新区块网格
Args:
chunk_x: 区块X坐标
chunk_y: 区块Y坐标
lod_level: LOD级别
heightmap: 高度图数据
Returns:
是否更新成功
"""
try:
chunk_key = f"{chunk_x},{chunk_y},{lod_level}"
# 生成区块网格数据
mesh_data = self.generate_terrain_mesh(heightmap)
# 存储到LOD区块中
if chunk_key not in self.lod_chunks:
self.lod_chunks[chunk_key] = {}
self.lod_chunks[chunk_key]['mesh_data'] = mesh_data
self.lod_chunks[chunk_key]['last_update'] = time.time()
print(f"✓ 区块 [{chunk_x}, {chunk_y}] LOD {lod_level} 网格已更新")
return True
except Exception as e:
print(f"✗ 区块网格更新失败: {e}")
import traceback
traceback.print_exc()
return False
def get_chunk_mesh(self, chunk_x: int, chunk_y: int, lod_level: int) -> Dict[str, np.ndarray]:
"""
获取区块网格数据
Args:
chunk_x: 区块X坐标
chunk_y: 区块Y坐标
lod_level: LOD级别
Returns:
网格数据字典
"""
try:
chunk_key = f"{chunk_x},{chunk_y},{lod_level}"
if chunk_key in self.lod_chunks:
return self.lod_chunks[chunk_key].get('mesh_data', {})
else:
return {}
except Exception as e:
print(f"✗ 区块网格获取失败: {e}")
return {}
def set_lod_distances(self, distances: List[float]):
"""
设置LOD距离
Args:
distances: LOD距离列表
"""
self.render_params['lod_distances'] = distances
print(f"✓ LOD距离已设置: {distances}")
def get_lod_level(self, distance: float) -> int:
"""
根据距离确定LOD级别
Args:
distance: 距离
Returns:
LOD级别
"""
try:
lod_distances = self.render_params.get('lod_distances', [])
if not lod_distances:
# 使用默认距离计算
base_distance = 100.0
for i in range(self.render_params['lod_levels']):
if distance < base_distance * (i + 1):
return i
return self.render_params['lod_levels'] - 1
else:
# 使用自定义距离
for i, lod_distance in enumerate(lod_distances):
if distance < lod_distance:
return i
return len(lod_distances) - 1
except Exception as e:
print(f"✗ LOD级别计算失败: {e}")
return 0
def frustum_cull(self, camera_position: List[float],
view_matrix: np.ndarray, projection_matrix: np.ndarray) -> List[Tuple[int, int]]:
"""
视锥体剔除
Args:
camera_position: 摄像机位置
view_matrix: 视图矩阵
projection_matrix: 投影矩阵
Returns:
可见区块列表 [(chunk_x, chunk_y), ...]
"""
try:
# 在实际实现中,这里会进行复杂的视锥体剔除计算
# 简化实现:返回所有区块
visible_chunks = []
if self.plugin.terrain_manager:
terrain_data = self.plugin.terrain_manager.get_terrain_data()
terrain_size = terrain_data.get('stats', {}).get('chunks_generated', 0)
if terrain_size > 0:
# 模拟一些可见区块
for i in range(min(10, terrain_size)):
visible_chunks.append((i, i))
return visible_chunks
except Exception as e:
print(f"✗ 视锥体剔除失败: {e}")
return []
def occlusion_cull(self, visible_chunks: List[Tuple[int, int]]) -> List[Tuple[int, int]]:
"""
遮挡剔除
Args:
visible_chunks: 可见区块列表
Returns:
未被遮挡的区块列表
"""
try:
# 在实际实现中,这里会进行遮挡剔除计算
# 简化实现:返回所有可见区块
return visible_chunks
except Exception as e:
print(f"✗ 遮挡剔除失败: {e}")
return visible_chunks
def generate_terrain_materials(self, biome_map: np.ndarray) -> Dict[str, Any]:
"""
生成地形材质
Args:
biome_map: 生物群落图数据
Returns:
材质数据字典
"""
try:
print("✓ 开始生成地形材质...")
# 在实际实现中,这里会生成多种材质纹理和属性
materials = {
'diffuse_textures': [],
'normal_textures': [],
'specular_textures': [],
'biome_mapping': {}
}
# 为每种生物群落生成材质ID
unique_biomes = np.unique(biome_map)
for biome_id in unique_biomes:
materials['biome_mapping'][biome_id] = f"material_{biome_id}"
print("✓ 地形材质生成完成")
return materials
except Exception as e:
print(f"✗ 地形材质生成失败: {e}")
import traceback
traceback.print_exc()
return {}
def apply_tessellation(self, vertices: np.ndarray, tessellation_factor: float) -> np.ndarray:
"""
应用细分曲面
Args:
vertices: 顶点数据
tessellation_factor: 细分因子
Returns:
细分后的顶点数据
"""
try:
# 在实际实现中这里会使用GPU细分着色器
# 简化实现:返回原始顶点
print(f"✓ 应用细分曲面 (因子: {tessellation_factor})")
return vertices
except Exception as e:
print(f"✗ 细分曲面应用失败: {e}")
return vertices
def apply_displacement_mapping(self, heightmap: np.ndarray, displacement_factor: float) -> np.ndarray:
"""
应用置换贴图
Args:
heightmap: 高度图数据
displacement_factor: 置换因子
Returns:
置换后的顶点数据
"""
try:
# 在实际实现中,这里会在细分着色器中应用置换贴图
# 简化实现:返回处理后的高度图
displaced_heightmap = heightmap * displacement_factor
print(f"✓ 应用置换贴图 (因子: {displacement_factor})")
return displaced_heightmap
except Exception as e:
print(f"✗ 置换贴图应用失败: {e}")
return heightmap
def calculate_shadow_map(self, light_direction: List[float],
heightmap: np.ndarray) -> np.ndarray:
"""
计算阴影贴图
Args:
light_direction: 光照方向
heightmap: 高度图数据
Returns:
阴影贴图数据
"""
try:
height, width = heightmap.shape
print(f"✓ 开始计算 {width}x{height} 阴影贴图...")
# 创建阴影贴图数据
shadow_map = np.ones((height, width), dtype=np.float32)
# 简化的阴影计算
light_x, light_y, light_z = light_direction
# 根据光照方向计算阴影
for y in range(height):
for x in range(width):
# 简单的高度遮挡计算
current_height = heightmap[y, x]
shadow_factor = 1.0
# 检查在光照方向上是否有更高的地形遮挡
step_x = int(light_x * 10)
step_y = int(light_y * 10)
check_x = x + step_x
check_y = y + step_y
if 0 <= check_x < width and 0 <= check_y < height:
check_height = heightmap[check_y, check_x]
if check_height > current_height:
shadow_factor = 0.5 # 简单的半阴影
shadow_map[y, x] = shadow_factor
print("✓ 阴影贴图计算完成")
return shadow_map
except Exception as e:
print(f"✗ 阴影贴图计算失败: {e}")
import traceback
traceback.print_exc()
# 返回默认阴影贴图(无阴影)
height, width = heightmap.shape
return np.ones((height, width), dtype=np.float32)
def apply_atmospheric_effects(self, color_data: np.ndarray,
camera_position: List[float],
sun_direction: List[float]) -> np.ndarray:
"""
应用大气效果
Args:
color_data: 颜色数据
camera_position: 摄像机位置
sun_direction: 太阳方向
Returns:
处理后的颜色数据
"""
try:
processed_colors = color_data.copy()
height, width = processed_colors.shape[:2]
print("✓ 应用大气效果...")
# 简化的雾效和光照计算
fog_density = 0.01
sun_r, sun_g, sun_b = sun_direction
for y in range(height):
for x in range(width):
# 计算到摄像机的距离(简化)
distance = math.sqrt((x - camera_position[0])**2 + (y - camera_position[1])**2)
# 应用雾效
fog_factor = math.exp(-distance * fog_density)
fog_factor = max(0.0, min(1.0, fog_factor))
# 混合雾颜色(白色)
processed_colors[y, x, 0] = int(processed_colors[y, x, 0] * fog_factor + 255 * (1 - fog_factor))
processed_colors[y, x, 1] = int(processed_colors[y, x, 1] * fog_factor + 255 * (1 - fog_factor))
processed_colors[y, x, 2] = int(processed_colors[y, x, 2] * fog_factor + 255 * (1 - fog_factor))
print("✓ 大气效果应用完成")
return processed_colors
except Exception as e:
print(f"✗ 大气效果应用失败: {e}")
return color_data
def optimize_rendering(self, target_fps: float = 60.0):
"""
优化渲染性能
Args:
target_fps: 目标帧率
"""
try:
print(f"✓ 开始优化渲染性能 (目标FPS: {target_fps})")
# 根据目标FPS调整渲染参数
current_fps = 1.0 / max(0.001, self.stats['average_render_time'])
if current_fps < target_fps * 0.8: # 如果FPS低于目标的80%
# 降低渲染质量以提高性能
self.render_params['lod_levels'] = max(1, self.render_params['lod_levels'] - 1)
self.render_params['chunk_size'] = min(64, self.render_params['chunk_size'] * 2)
print(" → 降低LOD级别和增加区块大小以提高性能")
elif current_fps > target_fps * 1.2: # 如果FPS高于目标的120%
# 提高渲染质量
self.render_params['lod_levels'] = min(10, self.render_params['lod_levels'] + 1)
self.render_params['chunk_size'] = max(8, self.render_params['chunk_size'] // 2)
print(" → 提高LOD级别和减小区块大小以提高质量")
print("✓ 渲染性能优化完成")
except Exception as e:
print(f"✗ 渲染性能优化失败: {e}")
def export_render_settings(self, filename: str) -> bool:
"""
导出渲染设置
Args:
filename: 文件名
Returns:
是否导出成功
"""
try:
import json
settings = {
'render_params': self.render_params,
'lighting_params': self.lighting_params,
'material_params': self.material_params,
'shader_params': self.shader_params,
'timestamp': time.time()
}
with open(filename, 'w', encoding='utf-8') as f:
json.dump(settings, f, ensure_ascii=False, indent=2)
print(f"✓ 渲染设置已导出到: {filename}")
return True
except Exception as e:
print(f"✗ 渲染设置导出失败: {e}")
return False
def import_render_settings(self, filename: str) -> bool:
"""
导入渲染设置
Args:
filename: 文件名
Returns:
是否导入成功
"""
try:
import json
with open(filename, 'r', encoding='utf-8') as f:
settings = json.load(f)
# 更新参数
if 'render_params' in settings:
self.render_params.update(settings['render_params'])
if 'lighting_params' in settings:
self.lighting_params.update(settings['lighting_params'])
if 'material_params' in settings:
self.material_params.update(settings['material_params'])
if 'shader_params' in settings:
self.shader_params.update(settings['shader_params'])
print(f"✓ 渲染设置已从 {filename} 导入")
return True
except Exception as e:
print(f"✗ 渲染设置导入失败: {e}")
return False
def get_render_capabilities(self) -> Dict[str, Any]:
"""
获取渲染能力信息
Returns:
渲染能力信息字典
"""
return {
'max_texture_size': 8192,
'max_lod_levels': 10,
'supported_shaders': ['vertex', 'fragment', 'geometry', 'tessellation'],
'supports_normal_mapping': True,
'supports_displacement_mapping': True,
'supports_tessellation': True,
'supports_shadow_mapping': True,
'max_anisotropy': 16.0
}
def set_render_quality(self, quality_level: str):
"""
设置渲染质量级别
Args:
quality_level: 质量级别 ('low', 'medium', 'high', 'ultra')
"""
quality_settings = {
'low': {
'lod_levels': 2,
'chunk_size': 64,
'normal_mapping': False,
'tessellation_factor': 0.5
},
'medium': {
'lod_levels': 4,
'chunk_size': 32,
'normal_mapping': True,
'tessellation_factor': 1.0
},
'high': {
'lod_levels': 6,
'chunk_size': 16,
'normal_mapping': True,
'tessellation_factor': 2.0
},
'ultra': {
'lod_levels': 8,
'chunk_size': 8,
'normal_mapping': True,
'tessellation_factor': 4.0
}
}
if quality_level in quality_settings:
settings = quality_settings[quality_level]
self.render_params['lod_levels'] = settings['lod_levels']
self.render_params['chunk_size'] = settings['chunk_size']
self.material_params['normal_mapping'] = settings['normal_mapping']
self.material_params['tessellation_factor'] = settings['tessellation_factor']
print(f"✓ 渲染质量已设置为: {quality_level}")
else:
print(f"✗ 无效的质量级别: {quality_level}")