Как разработать простую оболочку GLSL для шейдеров - PullRequest
10 голосов
/ 29 мая 2011

ОБНОВЛЕНИЕ: Поскольку мне нужно что-то сразу, я создал простую обертку шейдера, которая делает то, что мне нужно.Вы можете найти его здесь: ShaderManager на GitHub .Обратите внимание, что он предназначен для Objective-C / iOS, поэтому может быть полезен не всем.Если у вас есть предложения по улучшению дизайна, пожалуйста, дайте мне знать!

Исходная проблема:

Я новичок в использовании шейдеров GLSL.Я достаточно знаком с языком GLSL и интерфейсом OpenGL, но у меня возникают проблемы при разработке простого API, с помощью которого можно использовать шейдеры.

Интерфейс C OpenGL для взаимодействия с шейдерами кажется громоздким.Я не могу найти какие-либо учебники в сети, которые бы касались дизайна API таких вещей.

У меня такой вопрос: есть ли у кого-нибудь хороший, простой дизайн API или шаблон для переноса?API шейдерной программы OpenGL?

Возьмем следующий простой пример.Скажем, у меня есть один вершинный шейдер, который просто эмулирует фиксированную функциональность, и два фрагментных шейдера - один для рисования гладких прямоугольников и один для рисования гладких кругов.У меня есть следующие файлы:

Shader.vsh : Simple vertex shader, with the following inputs/outputs:
    -- Uniforms: mat4 Model, mat4 View, mat4 Projection
    -- Attributes: vec4 Vertex, vec2 TexCoord, vec4 Color
    -- Varying: vec4 vColor, vec2 vTexCoord

Square.fsh : Fragment shader for drawing squares based on tex coord / color
Circle.fsh : Fragment shader for drawing circles based on tex coord / color

Основные ссылки

Теперь, что является стандартным способом их использования?Связать ли вышеперечисленные шейдеры в две шейдерные программы OpenGL?То есть:

Shader.vsh + Square.fsh = SquareProgram
Shader.vsh + Circle.fsh = CircleProgram

Или я вместо этого создаю одну большую программу, где фрагментные шейдеры проверяют некоторые условные однородные переменные и вызывают функцию шейдера для генерации их результата.Например:

Shader.vsh + Square.fsh + Circle.fsh + Main.fsh = ShaderProgram
//Main.fsh here would simply check whether to call out to square or circle

С двумя отдельными программами мне, вероятно, потребуется вызвать

glUseProgram(CircleProgram); or glUseProgram(SquareProgram);

Перед каждым типом элемента, который я хочу нарисовать.Затем мне нужно будет установить униформу (модель / вид / проекция) и атрибуты каждой программы, прежде чем я ее использую.Это кажется таким громоздким.

С одной опцией ShaderProgram мне все равно нужно было бы установить какой-то логический переключатель (круг или квадрат) в фрагментном шейдере, который будет проверяться перед рисованием каждого пикселя.Это также кажется сложным.

В качестве примечания, мне разрешено связывать два фрагментных шейдера, каждый с функцией main (), в одну шейдерную программу?Как OpenGL узнает, какой из них вызывать?

Установка переменных

Вызовы:

glUniform*
glVertexAttribPointer

Используются для установки униформ и расположения указателей атрибутов на текущемprogram.

Разным классам и структурам может потребоваться доступ и установка переменных в текущем шейдере (или изменение текущего шейдера) из разных мест в коде.Я не могу придумать хороший способ сделать это, чтобы отделить код шейдера от кода, который хочет его использовать.

То есть каждая фигура, которую я хочу нарисовать, должна будет установить атрибуты координат вершины и текстуры- требуя маркеров для этих атрибутов, сгенерированных OpenGL.

Камера должна будет установить свою матрицу проекции как униформу в вершинном шейдере, в то время как класс, управляющий стеком матрицы модели, должен будет установить свою собственную униформу ввершинный шейдер.

Изменение шейдеров в процессе рисования сцены означало бы, что все эти классы должны будут снова установить свои униформы и атрибуты.

Как большинство людей проектируют вокруг этого?

Глобальный словарь шейдеров, доступ к которым осуществляется по дескриптору или имени, с геттерами и сеттерами для их параметров?

OO-дизайн с шейдерными объектами, каждый из которых имеет параметры?

Я посмотрел на следующееОбертки:

Чайник Джона: GLSL Shader Manager - Оборачивает шейдеры в классах C ++.Кажется, это всего лишь оболочка, которая обеспечивает соблюдение принципов ОО в C API, в результате чего API C ++ почти такой же.

Я предпочитаю любой вид проектирования, который упрощает использование программ Shader, именя не волнует конкретная используемая парадигма (ОО, процедурная и т. д.)

Ответы [ 2 ]

8 голосов
/ 29 мая 2011

Я вижу, это помечено iOS, поэтому, если вы неравнодушны к Objective-C, я бы внимательно посмотрел на класс-оболочку Джеффа Ламарша GLProgram, который он описывает здесь и имеет доступный источник здесь . Я использовал его в своих собственных приложениях, чтобы упростить настройку некоторых программ шейдеров и сделать код немного чище.

Например, вы можете настроить шейдер, его атрибуты и униформу, используя код, подобный следующему:

sphereDepthProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"SphereDepth" fragmentShaderFilename:@"SphereDepth"];
[sphereDepthProgram addAttribute:@"position"];
[sphereDepthProgram addAttribute:@"inputImpostorSpaceCoordinate"];
if (![sphereDepthProgram link])
{
    NSLog(@"Depth shader link failed");
    NSString *progLog = [sphereDepthProgram programLog];
    NSLog(@"Program Log: %@", progLog); 
    NSString *fragLog = [sphereDepthProgram fragmentShaderLog];
    NSLog(@"Frag Log: %@", fragLog);
    NSString *vertLog = [sphereDepthProgram vertexShaderLog];
    NSLog(@"Vert Log: %@", vertLog);
    [sphereDepthProgram release];
    sphereDepthProgram = nil;
}

sphereDepthPositionAttribute = [sphereDepthProgram attributeIndex:@"position"];
sphereDepthImpostorSpaceAttribute = [sphereDepthProgram attributeIndex:@"inputImpostorSpaceCoordinate"];
sphereDepthModelViewMatrix = [sphereDepthProgram uniformIndex:@"modelViewProjMatrix"];
sphereDepthRadius = [sphereDepthProgram uniformIndex:@"sphereRadius"];

Когда вам нужно использовать программу шейдера, вы делаете что-то вроде следующего:

[sphereDepthProgram use];

Это не относится к проблемам ветвления и отдельных шейдеров, которые вы затронули выше, но реализация Джеффа обеспечивает хорошую инкапсуляцию некоторого кода настройки стандартного шейдера OpenGL ES.

5 голосов
/ 29 мая 2011

Базовая ссылка:

Здесь нет стандартного пути. Существует как минимум 2 общих подхода:

  1. Монолитный - один шейдер покрывает многие случаи, используя однородные логические переключатели. Эти ветви не влияют на производительность, потому что результат условия является постоянным для любой группы фрагментов (фактически для всех фрагментов).

  2. Компоновка многообъектных программ - основной шейдер объявляет набор точек входа (таких как 'get_diffuse', 'get_specular' и т. Д.), Которые реализованы в отдельных прикрепленных объектах шейдера. Это подразумевает индивидуальный шейдер для каждого объекта, но любой вид кэширования помогает.

Задание переменных: униформа

Я просто опишу разработанный мной подход.

Каждая шейдерная программа имеет список единообразных словарей. Он используется для заполнения единого списка источников при программном (ре) связывании. Когда программа активирована, она проходит через единый список, получает значения из их источников и загружает их в GL. В результате данные не связаны напрямую с пользовательской шейдерной программой, и все, что им управляет, не заботится о программе, использующей их.

Одним из этих словарей может быть, например, основной, содержащий модель, преобразования вида, проекцию камеры и, возможно, что-то еще.

Задание переменных: атрибуты

Прежде всего, шейдерная программа является потребителем атрибутов, поэтому именно она должна извлекать эти атрибуты из сетки (или любого другого хранилища данных) и загружать их в GL так, как нужно. Также следует убедиться, что типы предоставленных атрибутов соответствуют запрашиваемым типам.

При использовании с монолитным шейдерным подходом возможна неприятная ситуация, когда один из отключенных путей ветвления требует атрибута вершины, который не предоставляется. Я бы посоветовал использовать данные другого атрибута для предоставления отсутствующего, потому что в данном случае нам нет дела до фактических значений.

P.S. Вы можете найти актуальную реализацию этих идей здесь: http://code.google.com/p/kri/

...