Шейдеры

GLShader — OpenGL шейдерная программа

Управляет компиляцией, линковкой и использованием GLSL шейдеров.

Создание из исходника

// Исходник вершинного шейдера
const string vertexSource = @"
    #version 330 core
    layout(location = 0) in vec3 position;
    layout(location = 1) in vec2 texCoord;
    
    uniform mat4 uModel;
    uniform mat4 uView;
    uniform mat4 uProjection;
    
    out vec2 vTexCoord;
    
    void main()
    {
        gl_Position = uProjection * uView * uModel * vec4(position, 1.0);
        vTexCoord = texCoord;
    }
";

// Фрагментный шейдер
const string fragmentSource = @"
    #version 330 core
    in vec2 vTexCoord;
    uniform sampler2D uTexture;
    out vec4 FragColor;
    
    void main()
    {
        FragColor = texture(uTexture, vTexCoord);
    }
";

// Создание программы
var shader = context.CreateShader(new List<ShaderScript>
{
    new(ShaderType.Vertex, vertexSource),
    new(ShaderType.Fragment, fragmentSource)
});

Процесс компиляции

При создании из исходника:

  1. Создаётся программа glCreateProgram()
  2. Каждый шейдер компилируется glCompileShader()
  3. Если ошибка — информация выводится в консоль
  4. Шейдеры прикрепляются glAttachShader()
  5. Программа линкуется glLinkProgram()
  6. Объекты шейдеров удаляются glDeleteShader()

Создание из скомпилированного кода

// Загрузка из кеша (намного быстрее)
var compiled = GetCachedBinary(); // вы сохраняли это из Export()
var shader = context.CreateShader(new CompiledShaderProgram
{
    Binary = compiled.Binary,
    Format = compiled.Format
});

Кеширование шейдеров

// При первой загрузке: компилируем и экспортируем
var shader = context.CreateShader(shaderScripts);
var compiled = shader.Export();
File.WriteAllBytes("cache/shader.bin", compiled.Binary);
// File.WriteAllText("cache/shader.fmt", compiled.Format.ToString());

// При следующей загрузке: берём из кеша
var cachedBinary = File.ReadAllBytes("cache/shader.bin");
var shader = context.CreateShader(new CompiledShaderProgram
{
    Binary = cachedBinary,
    Format = 0x8F65 // посмотрите в коде Save
});

Uniform переменные

Установка значений

shader.Apply(); // сделать текущей программой

// Скалярные значения
shader.SetUniform("uTime", 1.5f);
shader.SetUniform("uCount", 42);
shader.SetUniform("uIsActive", 1u);

// Векторы
shader.SetUniform("uColor", new FVector3(1f, 0f, 0f));
shader.SetUniform("uOffset", new FVector2(10, 20));
shader.SetUniform("uLightPos", new FVector4(0, 5, 0, 1));

// Матрицы
var model = FMatrix4x4.Identity;
shader.SetUniform("uModel", model, transpose: false);
shader.SetUniform("uView", viewMatrix, transpose: false);
shader.SetUniform("uProjection", projMatrix, transpose: false);

Информация об uniforms

// Получить описание всех uniforms
var uniforms = shader.GetUniformsInfo();

foreach (var (name, info) in uniforms)
{
    Console.WriteLine($"{name}: {info.Type} (size={info.Size})");
    // Output: uModel: 35676 (size=1)
    //         uTime: 5126 (size=1)
    //         uColor: 35665 (size=1)
}

Атрибуты вершин

Получение индекса

shader.Apply();

// Получить индекс атрибута
uint posIndex = shader.GetAttributeLocation("position");  // обычно 0
uint uvIndex = shader.GetAttributeLocation("texCoord");   // обычно 1
uint normalIndex = shader.GetAttributeLocation("normal"); // обычно 2

// Использовать при описании VAO
vao.AddAttribute(posIndex, 3, VertexAttribPointerType.Float, false, 32, 0);
vao.AddAttribute(uvIndex, 2, VertexAttribPointerType.Float, false, 32, 12);
vao.AddAttribute(normalIndex, 3, VertexAttribPointerType.Float, false, 32, 20);

Типовый цикл рендера

Igdrasil.OnRender += (ctx, dt) =>
{
    // Очистка
    ctx.ClearColor(new FVector4(0.1f, 0.1f, 0.1f, 1f));
    ctx.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
    
    // Применяем шейдер
    shader.Apply();
    
    // Устанавливаем матрицы
    var model = FMatrix4x4.Translation(position);
    var view = FMatrix4x4.LookAt(eye, center, up);
    var proj = FMatrix4x4.Perspective(fov, aspect, near, far);
    
    shader.SetUniform("uModel", model, transpose: false);
    shader.SetUniform("uView", view, transpose: false);
    shader.SetUniform("uProjection", proj, transpose: false);
    
    // Устанавливаем параметры
    shader.SetUniform("uTime", (float)dt);
    shader.SetUniform("uColor", color);
    
    // Устанавливаем текстуру
    ctx.SetActiveTexture(TextureUnit.Texture0);
    texture.Bind();
    shader.SetUniform("uTexture", 0); // номер текстурного блока
    
    // Отрисовка
    vao.Bind();
    ctx.DrawElements(PrimitiveType.Triangles, 0, indexCount, DrawElementsType.UnsignedInt);
};

Очистка

// Освобождение GPU-ресурсов
shader.Dispose();

Версия: 1.0