/
Zamar_Terrier
/
TigorEngine
Обзор
Документация
Войти
/
Zamar_Terrier
/
TigorEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/Core/e_postprocessing.c
613 строк
26 KB
Zamar_Terrier
Офигенное обновление движка
31 июл 2026, 21:03
Верифицирован
31 июл 2026, 21:03
1682686
Код
Авторство
О чём код?
/** * @file e_postprocessing.c * @brief Реализация системы пост-обработки TigorEngine (переработанная версия) * @details Использует стандартные объекты движка: RenderTexture и ShapeObject. * Не требует ручного копирования или барьеров. */ #include "Core/e_postprocessing.h" #include "Core/e_blue_print.h" #include "Core/graphicsObject.h" #include "Core/e_descriptor.h" #include "Core/e_buffer.h" #include "Core/e_memory.h" #include "Objects/gameObject2D.h" #include "Objects/gameObject3D.h" #include "Tools/e_math.h" #include "Tools/e_shaders.h" #include "Tools/glsl_parser.h" #include "Tools/glsl_to_spirv.h" #include "Tools/shader_builder.h" #include "TigorEngine.h" #include <math.h> #include <vulkan/vulkan.h> #include <stdio.h> #include <string.h> #include <stdlib.h> extern TEngine engine; static PostProcessingPipeline* g_ppInstance = NULL; // ============================================================================ // Встроенные GLSL-шейдеры для пост-обработки // ============================================================================ static const char* postVertShaderSource = "#version 450\n" "layout(binding = 0) uniform UBO {\n" " mat4 model;\n" " mat4 view;\n" " mat4 projection;\n" "} ubo;\n" "layout(location = 0) in vec2 aPos;\n" "layout(location = 1) in vec3 aColor;\n" "layout(location = 2) in vec2 aTexCoords;\n" "layout(location = 0) out vec3 fragColor;\n" "layout(location = 1) out vec2 fragTexCoord;\n" "void main() {\n" " fragColor = aColor;\n" " fragTexCoord = aTexCoords;\n" " gl_Position = ubo.projection * ubo.view * ubo.model * vec4(aPos, 0.0, 1.0);\n" "}\n"; static const char* postFragShaderSource = "#version 450\n" "\n" "// ================================================================\n" "// Uniforms (binding = 1)\n" "// ================================================================\n" "layout(binding = 1) uniform PostProcessParams {\n" " float screenWidth;\n" " float screenHeight;\n" " float time;\n" " float threshold;\n" " float strength;\n" " float radius;\n" " int iterations;\n" " float blurSize;\n" " int blurPasses;\n" " float blurDirX;\n" " float blurDirY;\n" " float brightness;\n" " float contrast;\n" " float saturation;\n" " float hueShift;\n" " float exposure;\n" " float whitePoint;\n" " float blackPoint;\n" " float gamma;\n" " uint effectFlags;\n" " // New params\n" " float pixelSize;\n" " float grainIntensity;\n" " float sepiaIntensity;\n" " float temperature;\n" " float edgeThreshold;\n" " float chromaticAberration;\n" " float padding[2];\n" "} params;\n" "\n" "layout(binding = 2) uniform sampler2D texSampler;\n" "\n" "// ================================================================\n" "// Входные данные от вершинного шейдера\n" "// ================================================================\n" "layout(location = 0) in vec3 fragColor;\n" "layout(location = 1) in vec2 fragTexCoord;\n" "\n" "// ================================================================\n" "// Выход\n" "// ================================================================\n" "layout(location = 0) out vec4 outColor;\n" "\n" "// ================================================================\n" "// Константы эффектов (битовые маски)\n" "// ================================================================\n" "#define EFFECT_COLOR_GRADING 1u\n" "#define EFFECT_TONEMAP 2u\n" "#define EFFECT_BLUR 4u\n" "#define EFFECT_VIGNETTE 8u\n" "#define EFFECT_BLOOM 16u\n" "#define EFFECT_SHARPEN 32u\n" "#define EFFECT_PIXELATE 64u\n" "#define EFFECT_GRAIN 128u\n" "#define EFFECT_SEPIA 256u\n" "#define EFFECT_GRAYSCALE 512u\n" "#define EFFECT_EDGE 1024u\n" "#define EFFECT_CHROMATIC 2048u\n" "\n" "#define MAX_RADIUS 16\n" "\n" "// ================================================================\n" "// Функции эффектов\n" "// ================================================================\n" "\n" "vec3 aces_tonemap(vec3 color) {\n" " float a = 2.51;\n" " float b = 0.03;\n" " float c = 2.43;\n" " float d = 0.59;\n" " float e = 0.14;\n" " return clamp((color * (a * color + b)) / (color * (c * color + d) + e), 0.0, 1.0);\n" "}\n" "\n" "vec3 rgb2hsv(vec3 c) {\n" " vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);\n" " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n" " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n" " float d = q.x - min(q.w, q.y);\n" " float e = 1.0e-10;\n" " return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n" "}\n" "\n" "vec3 hsv2rgb(vec3 c) {\n" " vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);\n" " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n" " return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n" "}\n" "\n" "// ИСПРАВЛЕНО: Настоящий контраст вместо ошибочного pow\n" "vec3 colorGrading(vec3 color) {\n" " float c = max(params.contrast, 0.001);\n" " color = (color - 0.5) * c + 0.5;\n" " vec3 hsv = rgb2hsv(color);\n" " hsv.y *= params.saturation;\n" " hsv.x += params.hueShift;\n" " return hsv2rgb(hsv);\n" "}\n" "\n" "// ИСПРАВЛЕНО: Безопасная тональная компрессия с корректной экспозицией\n" "vec3 tonemap(vec3 color) {\n" " float exp = max(params.exposure, 0.001);\n" " color = color * exp;\n" " \n" " // ACES тональная компрессия\n" " float a = 2.51, b = 0.03, c = 2.43, d = 0.59, e = 0.14;\n" " color = (color * (a * color + b)) / (color * (c * color + d) + e);\n" " \n" " // Коррекция белой и черной точек\n" " float wp = params.whitePoint > 0.0 ? params.whitePoint : 1.0;\n" " float bp = params.blackPoint;\n" " if (wp <= bp) wp = bp + 0.001;\n" " color = (color - bp) / (wp - bp);\n" " color = clamp(color, 0.0, 1.0);\n" " \n" " // УСИЛЕНИЕ: Добавляем S-кривую для заметного кинематографичного эффекта\n" " // Даже при exposure=1.0 это даст видимый контраст\n" " color = pow(color, vec3(0.9)); // Лёгкое осветление теней\n" " color = color * color * (3.0 - 2.0 * color); // S-кривая для контраста\n" " \n" " return color;\n" "}\n" "\n" "vec3 blur(vec2 uv) {\n" " float rad = params.radius;\n" " int r = int(min(rad, float(MAX_RADIUS)));\n" " vec2 texelSize = 1.0 / vec2(params.screenWidth, params.screenHeight);\n" " vec3 sum = vec3(0.0);\n" " float weightSum = 0.0;\n" " vec2 dir = vec2(params.blurDirX, params.blurDirY);\n" " \n" " if (length(dir) < 0.001) {\n" " for (int y = -MAX_RADIUS; y <= MAX_RADIUS; y++) {\n" " if (abs(y) > r) continue;\n" " for (int x = -MAX_RADIUS; x <= MAX_RADIUS; x++) {\n" " if (abs(x) > r) continue;\n" " vec2 offset = vec2(float(x), float(y)) * texelSize;\n" " vec4 sample = texture(texSampler, uv + offset);\n" " sum += sample.rgb;\n" " weightSum += 1.0;\n" " }\n" " }\n" " } else {\n" " vec2 step = dir * texelSize;\n" " for (int i = -MAX_RADIUS; i <= MAX_RADIUS; i++) {\n" " if (abs(i) > r) continue;\n" " vec4 sample = texture(texSampler, uv + float(i) * step);\n" " sum += sample.rgb;\n" " weightSum += 1.0;\n" " }\n" " }\n" " return sum / max(weightSum, 1.0);\n" "}\n" "\n" "float vignette(vec2 uv) {\n" " vec2 center = vec2(0.5, 0.5);\n" " float dist = distance(uv, center);\n" " float vig = 1.0 - smoothstep(0.3, 0.8, dist * params.strength);\n" " return mix(1.0, vig, 0.5);\n" "}\n" "\n" "vec3 bloom(vec2 uv) {\n" " vec4 color = texture(texSampler, uv);\n" " float luminance = dot(color.rgb, vec3(0.299, 0.587, 0.114));\n" " float bright = max(0.0, luminance - params.threshold);\n" " float rad = params.radius * 0.5;\n" " int r = int(min(rad, float(MAX_RADIUS)));\n" " vec2 texelSize = 1.0 / vec2(params.screenWidth, params.screenHeight);\n" " vec3 sum = vec3(0.0);\n" " float weightSum = 0.0;\n" " \n" " for (int y = -MAX_RADIUS; y <= MAX_RADIUS; y++) {\n" " if (abs(y) > r) continue;\n" " for (int x = -MAX_RADIUS; x <= MAX_RADIUS; x++) {\n" " if (abs(x) > r) continue;\n" " vec2 offset = vec2(float(x), float(y)) * texelSize;\n" " vec4 sample = texture(texSampler, uv + offset);\n" " float lum = dot(sample.rgb, vec3(0.299, 0.587, 0.114));\n" " float b = max(0.0, lum - params.threshold);\n" " sum += sample.rgb * b / (lum + 0.001);\n" " weightSum += 1.0;\n" " }\n" " }\n" " return (sum / max(weightSum, 1.0)) * params.strength;\n" "}\n" "\n" "vec3 sharpen(vec2 uv) {\n" " vec2 texelSize = 1.0 / vec2(params.screenWidth, params.screenHeight);\n" " vec3 center = texture(texSampler, uv).rgb;\n" " vec3 left = texture(texSampler, uv + vec2(-texelSize.x, 0.0)).rgb;\n" " vec3 right = texture(texSampler, uv + vec2( texelSize.x, 0.0)).rgb;\n" " vec3 up = texture(texSampler, uv + vec2(0.0, -texelSize.y)).rgb;\n" " vec3 down = texture(texSampler, uv + vec2(0.0, texelSize.y)).rgb;\n" " vec3 blurred = (left + right + up + down) * 0.25;\n" " return center + (center - blurred) * params.strength;\n" "}\n" "\n" "vec3 pixelate(vec2 uv) {\n" " float size = max(1.0, params.pixelSize);\n" " vec2 pixel = floor(uv * params.screenWidth / size) * size / params.screenWidth;\n" " return texture(texSampler, pixel).rgb;\n" "}\n" "\n" "float hash(vec2 p) {\n" " return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);\n" "}\n" "\n" "vec3 grain(vec3 color, vec2 uv) {\n" " float noise = hash(uv + params.time * 0.1);\n" " float intensity = params.grainIntensity * 0.15;\n" " return color + (noise - 0.5) * intensity;\n" "}\n" "\n" "vec3 sepia(vec3 color) {\n" " vec3 sepiaColor = vec3(\n" " dot(color, vec3(0.393, 0.769, 0.189)),\n" " dot(color, vec3(0.349, 0.686, 0.168)),\n" " dot(color, vec3(0.272, 0.534, 0.131))\n" " );\n" " return mix(color, sepiaColor, params.sepiaIntensity);\n" "}\n" "\n" "vec3 grayscale(vec3 color) {\n" " float gray = dot(color, vec3(0.299, 0.587, 0.114));\n" " return vec3(gray);\n" "}\n" "\n" "// ИСПРАВЛЕНО: Полностью развёрнутая выборка 3x3 для гарантии работы на всех GPU\n" "vec3 edgeDetection(vec2 uv) {\n" " vec2 texel = 1.0 / vec2(params.screenWidth, params.screenHeight);\n" " vec3 w = vec3(0.299, 0.587, 0.114);\n" " \n" " float m00 = dot(texture(texSampler, uv + vec2(-1.0, -1.0) * texel).rgb, w);\n" " float m01 = dot(texture(texSampler, uv + vec2( 0.0, -1.0) * texel).rgb, w);\n" " float m02 = dot(texture(texSampler, uv + vec2( 1.0, -1.0) * texel).rgb, w);\n" " float m10 = dot(texture(texSampler, uv + vec2(-1.0, 0.0) * texel).rgb, w);\n" " float m11 = dot(texture(texSampler, uv).rgb, w);\n" " float m12 = dot(texture(texSampler, uv + vec2( 1.0, 0.0) * texel).rgb, w);\n" " float m20 = dot(texture(texSampler, uv + vec2(-1.0, 1.0) * texel).rgb, w);\n" " float m21 = dot(texture(texSampler, uv + vec2( 0.0, 1.0) * texel).rgb, w);\n" " float m22 = dot(texture(texSampler, uv + vec2( 1.0, 1.0) * texel).rgb, w);\n" "\n" " float gx = -m00 - 2.0 * m10 - m20 + m02 + 2.0 * m12 + m22;\n" " float gy = -m00 - 2.0 * m01 - m02 + m20 + 2.0 * m21 + m22;\n" "\n" " float edge = sqrt(gx * gx + gy * gy);\n" " float threshold = max(params.edgeThreshold, 0.001);\n" " edge = smoothstep(threshold * 0.5, threshold * 1.5, edge);\n" " \n" " return vec3(edge);\n" "}\n" "\n" "vec3 chromaticAberration(vec2 uv) {\n" " float amount = params.chromaticAberration * 0.001;\n" " vec2 offset = vec2(amount, amount * 0.5);\n" " float r = texture(texSampler, uv + offset).r;\n" " float g = texture(texSampler, uv).g;\n" " float b = texture(texSampler, uv - offset).b;\n" " return vec3(r, g, b);\n" "}\n" "\n" "// ================================================================\n" "// Основная функция\n" "// ================================================================\n" "void main() {\n" " vec2 uv = fragTexCoord;\n" " vec4 texColor = texture(texSampler, uv);\n" " vec3 color = texColor.rgb * fragColor;\n" "\n" " // 1. Пространственные эффекты (работают с исходной текстурой)\n" " if ((params.effectFlags & EFFECT_PIXELATE) != 0u)\n" " color = pixelate(uv);\n" " if ((params.effectFlags & EFFECT_CHROMATIC) != 0u)\n" " color = chromaticAberration(uv);\n" " if ((params.effectFlags & EFFECT_EDGE) != 0u)\n" " color *= edgeDetection(uv);\n" " if ((params.effectFlags & EFFECT_BLUR) != 0u)\n" " color = blur(uv);\n" " if ((params.effectFlags & EFFECT_SHARPEN) != 0u)\n" " color = sharpen(uv);\n" "\n" " // 2. Аддитивные эффекты\n" " if ((params.effectFlags & EFFECT_BLOOM) != 0u)\n" " color += bloom(uv);\n" "\n" " // 3. Цветокоррекция (HSV) - ДО tonemap\n" " if ((params.effectFlags & EFFECT_COLOR_GRADING) != 0u)\n" " color = colorGrading(color);\n" "\n" " // 4. TONEMAP - сжатие HDR в LDR (КРИТИЧЕСКИ ВАЖНЫЙ ПОРЯДОК)\n" " if ((params.effectFlags & EFFECT_TONEMAP) != 0u)\n" " color = tonemap(color);\n" "\n" " // 5. Глобальные цветовые модификации - ПОСЛЕ tonemap (работают с LDR)\n" " if ((params.effectFlags & EFFECT_GRAYSCALE) != 0u)\n" " color = grayscale(color);\n" " if ((params.effectFlags & EFFECT_SEPIA) != 0u)\n" " color = sepia(color);\n" " if ((params.effectFlags & EFFECT_VIGNETTE) != 0u)\n" " color *= vignette(uv);\n" " if ((params.effectFlags & EFFECT_GRAIN) != 0u)\n" " color = grain(color, uv);\n" "\n" " // 6. Гамма-коррекция (всегда в конце)\n" " color = pow(color, vec3(1.0 / max(params.gamma, 0.001)));\n" "\n" " outColor = vec4(color, texColor.a);\n" "}\n"; // ============================================================================ // Функция обновления uniform-буфера для квада // ============================================================================ static void PostProcessingQuadUpdate(GameObject2D* quad, void* data) { PostProcessingPipeline* pp = g_ppInstance; if (pp) { static int frame = 0; if (++frame % 60 == 0) { printf("exposure=%.2f, gamma=%.2f, whitePoint=%.2f, blackPoint=%.2f, flags=0x%X\n", pp->uniforms.uExposure, pp->uniforms.uGamma, pp->uniforms.uWhitePoint, pp->uniforms.uBlackPoint, pp->uniforms.uEffectFlags); } memcpy(data, &pp->uniforms, sizeof(PostProcessingUniforms)); } } // ============================================================================ // Основные функции пайплайна // ============================================================================ int PostProcessingCreate(PostProcessingPipeline* pp, int width, int height) { if (!pp) return -1; memset(pp, 0, sizeof(PostProcessingPipeline)); g_ppInstance = pp; // 1. Offscreen-рендер RenderTextureInit(&pp->sceneRender, TIGOR_RENDER_TYPE_COLORED_IMAGE, width, height, 0); // 2. Полноэкранный квад ShapeObjectInit(&pp->fullscreenQuad, NULL, TIGOR_SHAPE_OBJECT_QUAD, NULL); //GameObject2DInitDefaultShaderWithName((GameObject2D *)&pp->fullscreenQuad, "2DDefault"); // 3. Устанавливаем шейдер через автоматическую функцию uint32_t packIndex = GameObject2DSetGLSLShaderForMemorySimple( (GameObject2D*)&pp->fullscreenQuad, "PostProcessShader", (char*)postVertShaderSource, (char*)postFragShaderSource ); if (packIndex == 0xFFFFFFFF) { printf("[PostProcessing] Failed to set shader for quad\n"); PostProcessingDestroy(pp); return -1; } // Убедимся, что packIndex = 0 (первый пакет) // Теперь добавляем uniform-буферы и текстуру с правильными bindings GameObject2DSetDescriptorUpdate((GameObject2D *)&pp->fullscreenQuad, 0, 0, (UpdateDescriptor)GameObject2DTransformBufferUpdate); GameObject2DSetDescriptorUpdate((GameObject2D *)&pp->fullscreenQuad, 0, 1, (UpdateDescriptor)PostProcessingQuadUpdate); pp->sceneRender.clear_color = vec3_f(1.0f, 0.0f, 0.0f); //Добавляем текстуру с binding 2 (явно) BluePrintAddRenderImage( &pp->fullscreenQuad.go.graphObj.BluePrints, 0, &pp->sceneRender, 2 ); printf("sizeof(PostProcessingUniforms) = %zu (expected 80)\n", sizeof(PostProcessingUniforms)); // 5. Настраиваем размер и позицию квада Transform2DSetScale((GameObject2D*)&pp->fullscreenQuad, (float)width, (float)height); Transform2DSetPosition((GameObject2D*)&pp->fullscreenQuad, 0.0, 0.0); // Инициализация uniform-данных memset(&pp->uniforms, 0, sizeof(PostProcessingUniforms)); pp->uniforms.uScreenWidth = (float)width; pp->uniforms.uScreenHeight = (float)height; pp->uniforms.uTime = 0.0f; pp->uniforms.uThreshold = 0.6f; pp->uniforms.uStrength = 1.5f; pp->uniforms.uRadius = 3.0f; pp->uniforms.uIterations = 4; pp->uniforms.uBlurSize = 20.0f; pp->uniforms.uBlurPasses = 1; pp->uniforms.uBlurDirectionX = cosf(0.0f); pp->uniforms.uBlurDirectionY = sinf(0.0f); pp->uniforms.uBrightness = 0.0f; pp->uniforms.uContrast = 2.5f; pp->uniforms.uSaturation = 5.0f; pp->uniforms.uHueShift = 0.0f; pp->uniforms.uExposure = 10.0f; pp->uniforms.uWhitePoint = 1.0f; pp->uniforms.uBlackPoint = 0.0f; pp->uniforms.uGamma = 0.5f; pp->uniforms.uPixelSize = 4.0f; pp->uniforms.uGrainIntensity = 0.1f; pp->uniforms.uSepiaIntensity = 1.0f; pp->uniforms.uTemperature = 0.0f; pp->uniforms.uEdgeThreshold = 0.1f; pp->uniforms.uChromaticAberration = 2.0f; // Включаем эффекты по умолчанию //PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_BLOOM, true); //PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_COLOR_GRADING, true); //PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_TONEMAP, true); pp->initialized = true; return 0; } void PostProcessingDestroy(PostProcessingPipeline* pp) { if (!pp || !pp->initialized) return; GameObjectDestroy((GameObject*)&pp->fullscreenQuad); RenderTextureDestroy(&pp->sceneRender); memset(pp, 0, sizeof(PostProcessingPipeline)); g_ppInstance = NULL; // <-- сбрасываем } int PostProcessingRecreate(PostProcessingPipeline* pp, int newWidth, int newHeight) { if (!pp || !pp->initialized) return -1; // Сохраняем состояние эффектов PostProcessingUniforms savedUniforms = pp->uniforms; // Уничтожаем старые ресурсы PostProcessingDestroy(pp); // Создаём заново if (PostProcessingCreate(pp, newWidth, newHeight) != 0) { return -1; } // Восстанавливаем параметры эффектов pp->uniforms = savedUniforms; pp->fullscreenQuad.go.self.flags |= TIGOR_GAME_OBJECT_FLAG_RECREATED; return 0; } void PostProcessingUpdateUniforms(PostProcessingPipeline* pp) { if (!pp || !pp->initialized) return; // Обновляем время и разрешение pp->uniforms.uTime = (float)TEngineGetTime(); pp->uniforms.uScreenWidth = (float)pp->sceneRender.width; pp->uniforms.uScreenHeight = (float)pp->sceneRender.height; // Uniform-буфер будет обновлён через колбэк PostProcessingQuadUpdate, // который копирует pp->uniforms в буфер при каждом вызове. } RenderTexture* PostProcessingGetRenderTarget(PostProcessingPipeline* pp) { return pp ? &pp->sceneRender : NULL; } ShapeObject* PostProcessingGetQuad(PostProcessingPipeline* pp) { return pp ? &pp->fullscreenQuad : NULL; } // ============================================================================ // Управление эффектами // ============================================================================ void PostProcessingSetEffectEnabled(PostProcessingPipeline* pp, PostProcessingEffectType effect, bool enabled) { if (!pp || !pp->initialized) return; if (enabled) { pp->uniforms.uEffectFlags |= effect; } else { pp->uniforms.uEffectFlags &= ~effect; } } bool PostProcessingIsEffectEnabled(PostProcessingPipeline* pp, PostProcessingEffectType effect) { if (!pp || !pp->initialized) return false; return (pp->uniforms.uEffectFlags & effect) != 0; } void PostProcessingSetBloomParams(PostProcessingPipeline* pp, float threshold, float strength, float radius, int iterations) { if (!pp || !pp->initialized) return; pp->uniforms.uThreshold = threshold; pp->uniforms.uStrength = strength; pp->uniforms.uRadius = radius; pp->uniforms.uIterations = iterations; PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_BLOOM, true); } void PostProcessingSetBlurParams(PostProcessingPipeline* pp, float blurSize, int blurPasses, float blurDirection) { if (!pp || !pp->initialized) return; pp->uniforms.uBlurSize = blurSize; pp->uniforms.uBlurPasses = blurPasses; pp->uniforms.uBlurDirectionX = cosf(blurDirection * (float)M_PI / 180.0f); pp->uniforms.uBlurDirectionY = sinf(blurDirection * (float)M_PI / 180.0f); PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_BLUR, true); } void PostProcessingSetColorGradingParams(PostProcessingPipeline* pp, float brightness, float contrast, float saturation, float hueShift) { if (!pp || !pp->initialized) return; pp->uniforms.uBrightness = brightness; pp->uniforms.uContrast = contrast; pp->uniforms.uSaturation = saturation; pp->uniforms.uHueShift = hueShift; PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_COLOR_GRADING, true); } void PostProcessingSetTonemapParams(PostProcessingPipeline* pp, float exposure, float whitePoint, float blackPoint, float gamma) { if (!pp || !pp->initialized) return; pp->uniforms.uExposure = exposure; pp->uniforms.uWhitePoint = whitePoint; pp->uniforms.uBlackPoint = blackPoint; pp->uniforms.uGamma = gamma; PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_TONEMAP, true); } void PostProcessingSetPixelateParams(PostProcessingPipeline* pp, float pixelSize) { if (!pp || !pp->initialized) return; pp->uniforms.uPixelSize = pixelSize; // было pixelSize PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_PIXELATE, true); } void PostProcessingSetGrainParams(PostProcessingPipeline* pp, float intensity) { if (!pp || !pp->initialized) return; pp->uniforms.uGrainIntensity = intensity; // было grainIntensity PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_GRAIN, true); } void PostProcessingSetSepiaParams(PostProcessingPipeline* pp, float intensity) { if (!pp || !pp->initialized) return; pp->uniforms.uSepiaIntensity = intensity; // было sepiaIntensity PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_SEPIA, true); } void PostProcessingSetEdgeParams(PostProcessingPipeline* pp, float threshold) { if (!pp || !pp->initialized) return; pp->uniforms.uEdgeThreshold = threshold; // было edgeThreshold PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_EDGE, true); } void PostProcessingSetChromaticParams(PostProcessingPipeline* pp, float amount) { if (!pp || !pp->initialized) return; pp->uniforms.uChromaticAberration = amount; // было chromaticAmount PostProcessingSetEffectEnabled(pp, POST_PROCESSING_EFFECT_CHROMATIC, true); }