Documentation

Rendering Optimization

Π’ этом Ρ€Π°Π·Π΄Π΅Π»Π΅ ΠΎΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‚ΡΡ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π° Π² Wudgine.

ΠžΡΠ½ΠΎΠ²Π½Ρ‹Π΅ Ρ‚Π΅Ρ…Π½ΠΈΠΊΠΈ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ

ΠžΡ‚ΡΠ΅Ρ‡Π΅Π½ΠΈΠ΅ Π½Π΅Π²ΠΈΠ΄ΠΈΠΌΡ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² (Culling)

Wudgine ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ нСсколько ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² отсСчСния для ΡƒΠΌΠ΅Π½ΡŒΡˆΠ΅Π½ΠΈΡ количСства ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², отправляСмых Π½Π° Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³:

Frustum Culling

ΠžΡ‚ΡΠ΅Ρ‡Π΅Π½ΠΈΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², находящихся Π·Π° ΠΏΡ€Π΅Π΄Π΅Π»Π°ΠΌΠΈ Π²ΠΈΠ΄ΠΈΠΌΠΎΠΉ области ΠΊΠ°ΠΌΠ΅Ρ€Ρ‹.

Occlusion Culling

ΠžΡ‚ΡΠ΅Ρ‡Π΅Π½ΠΈΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², Π·Π°ΠΊΡ€Ρ‹Ρ‚Ρ‹Ρ… Π΄Ρ€ΡƒΠ³ΠΈΠΌΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°ΠΌΠΈ.

Portal Culling

ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ для сцСн с ΠΊΠΎΠΌΠ½Π°Ρ‚Π°ΠΌΠΈ ΠΈ ΠΏΠΎΡ€Ρ‚Π°Π»Π°ΠΌΠΈ.

Detail Culling

ΠžΡ‚ΡΠ΅Ρ‡Π΅Π½ΠΈΠ΅ ΠΌΠ΅Π»ΠΊΠΈΡ… Π΄Π΅Ρ‚Π°Π»Π΅ΠΉ Π½Π° большом расстоянии.

// Настройка Frustum Culling
auto& renderer = engine.getRenderer();
renderer.enableFrustumCulling(true);

// Настройка Occlusion Culling
renderer.enableOcclusionCulling(true);
renderer.setOcclusionCullingPrecision(OcclusionPrecision::Medium);

// Настройка Detail Culling
renderer.setDetailCullingDistance(100.0f);
renderer.setDetailCullingSize(0.01f);

Π£Ρ€ΠΎΠ²Π½ΠΈ Π΄Π΅Ρ‚Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ (LOD)

БистСма LOD автоматичСски сниТаСт Π΄Π΅Ρ‚Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π½Π° большом расстоянии:

// БозданиС LOD для модСли
auto& lodGroup = entity.addComponent<LODGroupComponent>();
lodGroup.addLOD(0, "assets/models/character/character_high.mesh", 0.0f);
lodGroup.addLOD(1, "assets/models/character/character_medium.mesh", 10.0f);
lodGroup.addLOD(2, "assets/models/character/character_low.mesh", 30.0f);
lodGroup.addLOD(3, "assets/models/character/character_very_low.mesh", 60.0f);
lodGroup.setCrossFadeEnabled(true);
lodGroup.setCrossFadeDuration(0.5f);

Π˜Π½ΡΡ‚Π°Π½ΡΠΈΠ½Π³

Π˜Π½ΡΡ‚Π°Π½ΡΠΈΠ½Π³ позволяСт эффСктивно Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΡ‚ΡŒ мноТСство ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²Ρ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²:

// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ инстансированного Ρ€Π΅Π½Π΄Π΅Ρ€Π΅Ρ€Π°
auto& instanceRenderer = entity.addComponent<InstancedMeshRendererComponent>();
instanceRenderer.setMesh("assets/models/grass/grass_blade.mesh");
instanceRenderer.setMaterial("assets/materials/grass.mat");

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ инстансов
for (int i = 0; i < 1000; ++i) {
    Transform transform;
    transform.position = Vector3(Random::range(-50.0f, 50.0f), 0.0f, Random::range(-50.0f, 50.0f));
    transform.rotation = Quaternion::fromEuler(0.0f, Random::range(0.0f, 360.0f), 0.0f);
    transform.scale = Vector3(1.0f, Random::range(0.8f, 1.2f), 1.0f);
    
    instanceRenderer.addInstance(transform);
}

Π‘Π°Ρ‚Ρ‡ΠΈΠ½Π³

Π‘Π°Ρ‚Ρ‡ΠΈΠ½Π³ ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΠ΅Ρ‚ нСсколько ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² ΠΎΠ΄ΠΈΠ½ для ΡƒΠΌΠ΅Π½ΡŒΡˆΠ΅Π½ΠΈΡ количСства Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² отрисовки:

  • Static Batching для Π½Π΅ΠΏΠΎΠ΄Π²ΠΈΠΆΠ½Ρ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²
  • Dynamic Batching для двиТущихся ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² с ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²Ρ‹ΠΌΠΈ ΠΌΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π»Π°ΠΌΠΈ
  • GPU Instancing для мноТСства ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²Ρ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²
// Настройка Static Batching
auto& renderer = engine.getRenderer();
renderer.enableStaticBatching(true);
renderer.setStaticBatchingThreshold(100); // МинимальноС количСство Π²Π΅Ρ€ΡˆΠΈΠ½ для Π±Π°Ρ‚Ρ‡ΠΈΠ½Π³Π°

// ΠŸΠΎΠΌΠ΅Ρ‚ΠΊΠ° ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° ΠΊΠ°ΠΊ статичСского для Π±Π°Ρ‚Ρ‡ΠΈΠ½Π³Π°
auto& meshRenderer = entity.getComponent<MeshRendererComponent>();
meshRenderer.setStaticBatching(true);

ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ графичСского ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€Π°

ΠœΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½Π°Ρ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° ΠΊΠΎΠΌΠ°Π½Π΄

Wudgine ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒ для ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠΈ ΠΊΠΎΠΌΠ°Π½Π΄ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°:

// Настройка ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΠΉ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠΈ ΠΊΠΎΠΌΠ°Π½Π΄
auto& renderer = engine.getRenderer();
renderer.enableMultithreadedRendering(true);
renderer.setRenderThreadCount(4); // ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² для Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°

АсинхронныС вычислСния

АсинхронныС вычислСния ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎ с основным Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³ΠΎΠΌ:

// Настройка асинхронных вычислСний
auto& renderer = engine.getRenderer();
renderer.enableAsyncCompute(true);
renderer.setAsyncComputeQueues({
    AsyncComputeQueue::Physics,
    AsyncComputeQueue::ParticleSimulation,
    AsyncComputeQueue::OcclusionCulling
});

ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ ΡˆΠ΅ΠΉΠ΄Π΅Ρ€ΠΎΠ²

// ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚Π½Ρ‹ΠΉ ΡˆΠ΅ΠΉΠ΄Π΅Ρ€
void main() {
    // ΠŸΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ вычислСния
    vec3 viewDir = normalize(camera.position - inWorldPos);
    float NdotV = max(dot(normal, viewDir), 0.0);
    
    // ИспользованиС ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ вычислСнных Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ
    vec3 F0 = mix(vec3(0.04), albedo, metallic);
    vec3 F = fresnelSchlick(NdotV, F0);
    
    // ИзбСганиС Π²Π΅Ρ‚Π²Π»Π΅Π½ΠΈΠΉ
    float roughnessFactor = mix(roughness, 1.0, step(roughness, 0.01));
}

ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ рСсурсов

Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ тСкстурами

  • ВСкстурныС атласы для ΡƒΠΌΠ΅Π½ΡŒΡˆΠ΅Π½ΠΈΡ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ тСкстур
  • Π‘ΠΆΠ°Ρ‚ΠΈΠ΅ тСкстур (BC7, ASTC) для ΡƒΠΌΠ΅Π½ΡŒΡˆΠ΅Π½ΠΈΡ использования памяти
  • Мипмаппинг для ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ Π½Π° Ρ€Π°Π·Π½Ρ‹Ρ… расстояниях
  • ΠŸΠΎΡ‚ΠΎΠΊΠΎΠ²Π°Ρ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° тСкстур для Π±ΠΎΠ»ΡŒΡˆΠΈΡ… ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹Ρ… ΠΌΠΈΡ€ΠΎΠ²
// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ тСкстурного атласа
TextureAtlas atlas = TextureAtlas::create(2048, 2048);
atlas.addTexture("grass", "assets/textures/terrain/grass.png");
atlas.addTexture("dirt", "assets/textures/terrain/dirt.png");
atlas.addTexture("rock", "assets/textures/terrain/rock.png");
atlas.addTexture("sand", "assets/textures/terrain/sand.png");
atlas.build();

// ИспользованиС тСкстурного атласа
auto& material = entity.getComponent<MeshRendererComponent>().getMaterial();
material.setTextureAtlas("albedo", atlas);
material.setTextureRegion("albedo", "grass");

Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ мСшами

ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ мСш-Π΄Π°Π½Π½Ρ‹Ρ… для ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ:

  • ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ Ρ‚ΠΎΠΏΠΎΠ»ΠΎΠ³ΠΈΠΈ для ΡƒΠΌΠ΅Π½ΡŒΡˆΠ΅Π½ΠΈΡ количСства Π²Π΅Ρ€ΡˆΠΈΠ½ ΠΈ Ρ‚Ρ€Π΅ΡƒΠ³ΠΎΠ»ΡŒΠ½ΠΈΠΊΠΎΠ²
  • ΠŸΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ вычислСниС Ρ‚Π°Π½Π³Π΅Π½Ρ‚ΠΎΠ² ΠΈ Π±ΠΈΠ½ΠΎΡ€ΠΌΠ°Π»Π΅ΠΉ
  • Π‘ΠΆΠ°Ρ‚ΠΈΠ΅ Π²Π΅Ρ€ΡˆΠΈΠ½Π½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ…
  • Π£Ρ€ΠΎΠ²Π½ΠΈ Π΄Π΅Ρ‚Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ (LOD)
// ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ мСша
Mesh mesh = Mesh::load("assets/models/character/character.mesh");
mesh.optimize();
mesh.generateLODs(4); // ГСнСрация 4 ΡƒΡ€ΠΎΠ²Π½Π΅ΠΉ LOD
mesh.compressVertexData();
mesh.save("assets/models/character/character_optimized.mesh");

ΠŸΡ€ΠΎΡ„ΠΈΠ»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈ ΠΎΡ‚Π»Π°Π΄ΠΊΠ°

Π˜Π½ΡΡ‚Ρ€ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ профилирования

Wudgine прСдоставляСт встроСнныС инструмСнты для профилирования Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°:

// Π’ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ профилирования Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°
auto& profiler = engine.getProfiler();
profiler.enableRendererProfiling(true);

// ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ статистики Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°
auto stats = profiler.getRendererStats();
Debug::log("Draw calls: {}", stats.drawCalls);
Debug::log("Triangles: {}", stats.triangles);
Debug::log("Vertices: {}", stats.vertices);
Debug::log("Render time: {} ms", stats.renderTimeMs);

Визуализация ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ

// Π’ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π²ΠΈΠ·ΡƒΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ
auto& debugRenderer = engine.getDebugRenderer();
debugRenderer.enableFrustumVisualization(true);
debugRenderer.enableOcclusionCullingVisualization(true);
debugRenderer.enableLODVisualization(true);

ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ для Ρ€Π°Π·Π½Ρ‹Ρ… ΠΏΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌ

ΠœΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Π΅ устройства

Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°Ρ†ΠΈΠΈ для ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройствах:

  • УмСньшСниС Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π°
  • ИспользованиС ΠΎΠ±Π»Π΅Π³Ρ‡Π΅Π½Π½Ρ‹Ρ… ΡˆΠ΅ΠΉΠ΄Π΅Ρ€ΠΎΠ²
  • АгрСссивноС отсСчСниС ΠΈ LOD
  • ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ для Ρ‚Π°ΠΉΠ»ΠΎΠ²Ρ‹Ρ… Ρ€Π΅Π½Π΄Π΅Ρ€Π΅Ρ€ΠΎΠ²
  • УмСньшСниС количСства ΠΏΡ€ΠΎΠ·Ρ€Π°Ρ‡Π½Ρ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²
// Настройка для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств
if (Platform::isMobile()) {
    auto& renderer = engine.getRenderer();
    renderer.setRenderScale(0.75f); // Π Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³ Π² 75% ΠΎΡ‚ Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ экрана
    renderer.setShaderQuality(ShaderQuality::Low);
    renderer.setLODBias(2.0f); // Π‘ΠΎΠ»Π΅Π΅ агрСссивноС ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ LOD
    renderer.setMaxLights(4);
    renderer.disableEffect(PostProcessEffectType::SSAO);
    renderer.disableEffect(PostProcessEffectType::DepthOfField);
}

Консоли

ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ для ΠΈΠ³Ρ€ΠΎΠ²Ρ‹Ρ… консолСй:

// Настройка для консолСй
if (Platform::isConsole()) {
    auto& renderer = engine.getRenderer();
    
    if (Platform::getConsoleType() == ConsoleType::HighEnd) {
        // Настройки для Π²Ρ‹ΡΠΎΠΊΠΎΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… консолСй
        renderer.setRenderResolution(RenderResolution::UHD);
        renderer.enableRayTracedShadows(true);
        renderer.enableRayTracedAmbientOcclusion(true);
    } else {
        // Настройки для консолСй срСднСго уровня
        renderer.setRenderResolution(RenderResolution::FullHD);
        renderer.setTargetFrameRate(60);
    }
    
    // ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ для ΡƒΠ½ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎΠΉ памяти
    renderer.enableUnifiedMemoryOptimizations(true);
}

Π‘Π»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ шаги

Π’Π΅ΠΏΠ΅Ρ€ΡŒ, ΠΊΠΎΠ³Π΄Π° Π²Ρ‹ ознакомились с ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π° Π² Wudgine, Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡƒΠ΅ΠΌ:

ECS: Π“Π»ΡƒΠ±ΠΎΠΊΠΎΠ΅ ΠΏΠΎΠ³Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅

Π˜Π·ΡƒΡ‡ΠΈΡ‚Π΅ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρƒ Entity-Component-System.

Π’Π΅Π±-интСрфСйсы

ΠŸΠΎΠ·Π½Π°ΠΊΠΎΠΌΡŒΡ‚Π΅ΡΡŒ с систСмой Π²Π΅Π±-интСрфСйсов Wudgine.

Wudgine β€’ Β© 2025