23.06.2025

Что такое препроцессинг в GLSL?

Препроцессинг в GLSL (OpenGL Shading Language) – это механизм обработки кода перед его компиляцией. Он позволяет управлять кодом шейдера с помощью макросов, условных конструкций и подключения расширений. Это особенно полезно для оптимизации, кроссплатформенности и гибкой настройки шейдеров.

В этой статье мы разберем, как работает препроцессинг в GLSL, какие директивы доступны и где его можно применять.

Основные директивы препроцессора

Препроцессор в GLSL работает по принципу текстовой подстановки: он заменяет или удаляет части кода перед тем, как шейдер будет скомпилирован. Рассмотрим основные директивы, доступные в GLSL.

1. #version – указание версии GLSL
Каждый шейдер должен начинаться с указания версии языка:

#version 450

Эта строка сообщает компилятору, какую версию GLSL использовать. Если версия не указана, компилятор может выбрать устаревший стандарт, который не поддерживает современные возможности.

2. #define – макросы
Позволяет объявлять константы или заменять выражения перед компиляцией.

#define PI 3.14159265359

float getCircleArea(float radius) {
return PI * radius * radius;
}

Также можно использовать #define для создания “псевдо-функций”:

#define SQR(x) ((x) * (x))

float lengthSquared(vec3 v) {
return SQR(v.x) + SQR(v.y) + SQR(v.z);
}

3. #undef – удаление макроса
Если нужно отменить ранее объявленный #define, можно использовать #undef:

#define USE_TEXTURE
#undef USE_TEXTURE

После #undef USE_TEXTURE код больше не будет считать USE_TEXTURE определенным.

4. #ifdef, #ifndef, #endif – условная компиляция
Позволяет включать или исключать части кода в зависимости от наличия определенного макроса.

#define USE_LIGHTING

#ifdef USE_LIGHTING
uniform vec3 lightColor;
#endif

Если USE_LIGHTING определен, переменная lightColor будет объявлена. Если нет – этот код игнорируется.

Аналогично работает #ifndef, но наоборот – блок кода компилируется только если макрос НЕ определен

#ifndef DEBUG_MODE
// Код, который выполняется, только если DEBUG_MODE не включен
#endif

5. #if, #elif, #else, #endif – условия на основе значений
Позволяют проверять значения макросов и менять код в зависимости от их значений.

#define QUALITY 2

#if QUALITY == 1
vec3 color = vec3(0.5);
#elif QUALITY == 2
vec3 color = vec3(1.0);
#else
vec3 color = vec3(0.0);
#endif

Если QUALITY == 1, будет использован один вариант кода, если QUALITY == 2 – другой, а в остальных случаях – третий.

6. #error – вызов ошибки
Если нужно прервать компиляцию с сообщением об ошибке, используется #error:

#ifndef MAX_LIGHTS
#error "Макрос MAX_LIGHTS не определен!"
#endif

Компилятор выдаст сообщение “Макрос MAX_LIGHTS не определен!”, если он не был объявлен.

7. #pragma – директивы компилятора
Эта директива используется для управления поведением компилятора, но в стандартном GLSL редко встречается. Пример из OpenGL ES:

#pragma optimize(on)

8. #extension – подключение расширений
Некоторые возможности OpenGL требуют включения расширений. Это делается с помощью `#extension`:

#extension GL_ARB_shader_image_load_store : enable

Синтаксис:

#extension <имя_расширения> : <режим>

Режимы:

Где используется препроцессинг?

Препроцессор в GLSL применяется в разных сценариях:

#define DEBUG_MODE
#ifdef DEBUG_MODE
vec3 debugColor = vec3(1.0, 0.0, 0.0); // Красный цвет для отладки
#endif

Заключение

Препроцессинг в GLSL – мощный инструмент, который позволяет гибко управлять кодом шейдеров. С его помощью можно: