Методы наиболее успешной практики для программ построения теней
Программы построения теней обеспечивают большую гибкость, но они могут также быть значительным узким местом, если Вы выполняете слишком много вычислений или выполняете их неэффективно.
Скомпилируйте и соедините программы построения теней во время инициализации
Создание программы программы построения теней является дорогой работой по сравнению с другим OpenGL изменения состояния ES. Скомпилируйте, соедините и проверьте свои программы, когда будет инициализировано Ваше приложение. Как только Вы создали все свои программы построения теней, приложение может эффективно переключиться между ними путем вызова glUseProgram
.
Проверьте на ошибки в программе программы построения теней при отладке
Чтение диагностической информации после компиляции или соединения программы программы построения теней не необходимо в Сборке конечных версий Вашего приложения и может сократить производительность. Используйте OpenGL , который функции ES для чтения компиляции программы построения теней или ссылки регистрируют только в сборках разработки приложения, как показано в Перечислении 10-1.
Компиляция/ссылка программы построения теней Чтения перечисления 10-1 регистрирует только в сборках разработки
// After calling glCompileShader, glLinkProgram, or similar |
#ifdef DEBUG |
// Check the status of the compile/link |
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLen); |
if(logLen > 0) { |
// Show any errors as appropriate |
glGetProgramInfoLog(prog, logLen, &logLen, log); |
fprintf(stderr, “Prog Info Log: %s\n”, log); |
} |
#endif |
Точно так же необходимо вызвать glValidateProgram
функционируйте только в сборках разработки. Можно использовать эту функцию для нахождения ошибок разработки, таких как то, чтобы не удаваться связать все модули текстуры, требуемые программой программы построения теней. Но потому что проверка программы проверяет его по всему OpenGL состояние контекста ES, это - дорогая работа. Так как результаты проверки программы только значимы во время разработки, Вы не должны вызывать эту функцию в Сборках конечных версий Вашего приложения.
Используйте отдельные объекты программы построения теней ускорить компиляцию и соединение
Многие OpenGL , приложения ES используют несколько вершин и программ построения теней фрагмента, и часто полезно снова использовать ту же программу построения теней фрагмента с различными вершинными шейдерами или наоборот. Поскольку базовый OpenGL , который спецификация ES требует, чтобы вершина и программа построения теней фрагмента были соединены в единственной программе программы построения теней, смешав и соответствуя результаты программ построения теней в большом количестве программ, увеличив общую компиляцию программы построения теней и время ссылки при инициализации приложения.
OpenGL ES 2.0 и 3,0 контекста на iOS поддерживают
расширение. Можно использовать функции, предоставленные этим расширением для компиляции вершины, и программы построения теней фрагмента отдельно, и к смешиванию и подгонке предварительно скомпилировали этапы программы построения теней во время отображения с помощью конвейерных объектов программы. Кроме того, это расширение обеспечивает упрощенный интерфейс для компиляции и использования программ построения теней, показанных в Перечислении 10-2.EXT_separate_shader_objects
Компиляция перечисления 10-2 и использование отдельных объектов программы построения теней
- (void)loadShaders |
{ |
const GLchar *vertexSourceText = " ... vertex shader GLSL source code ... "; |
const GLchar *fragmentSourceText = " ... fragment shader GLSL source code ... "; |
// Compile and link the separate vertex shader program, then read its uniform variable locations |
_vertexProgram = glCreateShaderProgramvEXT(GL_VERTEX_SHADER, 1, &vertexSourceText); |
_uniformModelViewProjectionMatrix = glGetUniformLocation(_vertexProgram, "modelViewProjectionMatrix"); |
_uniformNormalMatrix = glGetUniformLocation(_vertexProgram, "normalMatrix"); |
// Compile and link the separate fragment shader program (which uses no uniform variables) |
_fragmentProgram = glCreateShaderProgramvEXT(GL_FRAGMENT_SHADER, 1, &fragmentSourceText); |
// Construct a program pipeline object and configure it to use the shaders |
glGenProgramPipelinesEXT(1, &_ppo); |
glBindProgramPipelineEXT(_ppo); |
glUseProgramStagesEXT(_ppo, GL_VERTEX_SHADER_BIT_EXT, _vertexProgram); |
glUseProgramStagesEXT(_ppo, GL_FRAGMENT_SHADER_BIT_EXT, _fragmentProgram); |
} |
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect |
{ |
// Clear the framebuffer |
glClearColor(0.65f, 0.65f, 0.65f, 1.0f); |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
// Use the previously constructed program pipeline and set uniform contents in shader programs |
glBindProgramPipelineEXT(_ppo); |
glProgramUniformMatrix4fvEXT(_vertexProgram, _uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m); |
glProgramUniformMatrix3fvEXT(_vertexProgram, _uniformNormalMatrix, 1, 0, _normalMatrix.m); |
// Bind a VAO and render its contents |
glBindVertexArrayOES(_vertexArray); |
glDrawElements(GL_TRIANGLE_STRIP, _indexCount, GL_UNSIGNED_SHORT, 0); |
} |
Уважайте аппаратные пределы на программах построения теней
ES OpenGL ограничивает число каждого типа переменной, который можно использовать в программе построения теней фрагмента или вершине. Спецификация ES OpenGL не требует, чтобы реализации обеспечили нейтрализацию программного обеспечения, когда превышены эти пределы; вместо этого, программе построения теней просто не удается скомпилировать или соединиться. При разработке приложения необходимо гарантировать, чтобы никакие ошибки не происходили во время компиляции программы построения теней, как показано в Перечислении 10-1.
Используйте подсказки точности
Подсказки точности были добавлены к спецификации языка ES GLSL для удовлетворения потребности для компактных переменных программы построения теней, соответствующих меньшие аппаратные пределы встроенных устройств. Каждая программа построения теней должна указать точность по умолчанию; отдельные переменные программы построения теней могут переопределить эту точность для обеспечения подсказок для компилятора о том, как та переменная используется в приложении. OpenGL реализация ES не требуется, чтобы использовать информацию о подсказке, но может сделать так для генерации более эффективных программ построения теней. Спецификация ES GLSL перечисляет диапазон и точность для каждой подсказки.
Следуйте этим инструкциям:
Когда в сомнении, значении по умолчанию к высокой точности.
Раскрашивает
0.0
к1.0
диапазон может обычно представляться с помощью низких переменных точности.Данные позиции должны обычно храниться как высокая точность.
Normals и векторы, используемые в освещении вычислений, могут обычно быть сохранены как средняя точность.
После сокращения точности повторно протестируйте свое приложение, чтобы гарантировать, что результаты - то, что Вы ожидаете.
Значения по умолчанию перечисления 10-3 к переменным высокой точности, но вычисляет вывод цвета с помощью низких переменных точности, потому что более высокая точность не необходима.
Перечисление 10-3 Низкая точность приемлемо для цвета фрагмента
precision highp float; // Defines precision for float and float-derived (vector/matrix) types. |
uniform lowp sampler2D sampler; // Texture2D() result is lowp. |
varying lowp vec4 color; |
varying vec2 texCoord; // Uses default highp precision. |
void main() |
{ |
gl_FragColor = color * texture2D(sampler, texCoord); |
} |
Фактическая точность переменных программы построения теней может варьироваться между различными устройствами на iOS, как может производительность операций на каждом уровне точности. Обратитесь к Ссылке Совместимости устройства на iOS для специфичных для устройства соображений.
Выполните векторные вычисления лениво
Не все графические процессоры включают векторные процессоры; они могут выполнить векторные вычисления на скалярном процессоре. При выполнении вычислений в программе построения теней рассмотрите порядок операций гарантировать, что вычисления выполняются эффективно, даже если они выполняются на скалярном процессоре.
Если бы код в Перечислении 10-4 выполнялся на векторном процессоре, то каждое умножение было бы выполнено параллельно через все четыре из компонентов вектора. Однако из-за расположения круглой скобки, та же работа на скалярном процессоре взяла бы восемь умножения, даже при том, что два из этих трех параметров являются скалярными значениями.
Перечисление 10-4 Плохое использование векторных операторов
highp float f0, f1; |
highp vec4 v0, v1; |
v0 = (v1 * f0) * f1; |
То же вычисление может быть выполнено более эффективно путем смещения круглых скобок как показано в Перечислении 10-5. В этом примере скалярные значения умножаются вместе сначала, и результат, умноженный против векторного параметра; вся работа может быть вычислена с пятью умножением.
Надлежащее использование перечисления 10-5 векторных операций
highp float f0, f1; |
highp vec4 v0, v1; |
v0 = v1 * (f0 * f1); |
Точно так же Ваше приложение должно всегда указывать маску записи для векторной работы, если оно не использует все компоненты результата. На скалярном процессоре могут быть пропущены вычисления для компонентов, не указанных в маске. Перечисление 10-6 работает вдвое более быстро на скалярном процессоре, потому что оно указывает, что необходимы только два компонента.
Перечисление 10-6 , Указывающее маску записи
highp vec4 v0; |
highp vec4 v1; |
highp vec4 v2; |
v2.xz = v0 * v1; |
Используйте универсальные формы или константы вместо того, чтобы вычислить значения в программе построения теней
Каждый раз, когда значение может быть вычислено вне программы построения теней, передайте его в программу построения теней как универсальная форма или константа. Перевычисление динамических значений может потенциально быть очень дорогим в программе построения теней.
Используйте команды ветвления с осторожностью
Ответвлениям обескураживают в программах построения теней, поскольку они могут сократить возможность выполнить операции параллельно на 3D графических процессорах (несмотря на то, что эта стоимость производительности сокращена на OpenGL ES способные к 3.0 устройства).
Если Вы избегаете переходить полностью, Ваше приложение может выполнить лучше всего. Например, вместо того, чтобы создать большую программу построения теней со многими условными опциями, создайте меньшие программы построения теней, специализированные для определенных задач рендеринга. Существует компромисс между сокращением количества ответвлений в Ваших программах построения теней и увеличением числа программ построения теней, которые Вы создаете. Протестируйте различные варианты и выберите быстрое решение.
Если Ваши программы построения теней должны использовать ответвления, следовать этим рекомендациям:
Лучшая производительность: Ответвление по константе, известной, когда компилируется программа построения теней.
Приемлемый: Ответвление по универсальной переменной.
Потенциально медленный: Ответвление по значению, вычисленному в программе построения теней.
Устраните циклы
Можно устранить много циклов или разворачиванием цикла или использованием векторов для выполнения операций. Например, этот код очень неэффективен:
int i; |
float f; |
vec4 v; |
for(i = 0; i < 4; i++) |
v[i] += f; |
Та же работа может быть сделана непосредственно с помощью покомпонентного, добавьте:
float f; |
vec4 v; |
v += f; |
Когда Вы не можете устранить цикл, он предпочтен, что цикл имеет постоянный предел для предотвращения динамических ответвлений.
Избегите вычислять индексы массива в программах построения теней
Используя индексы, вычисленные в программе построения теней, является более дорогим, чем постоянный или универсальный индекс массива. Доступ к универсальным массивам является обычно более дешевым, чем доступ к временным массивам.
Знайте о динамических поисках текстуры
Когда программа построения теней фрагмента вычисляет координаты текстуры вместо того, чтобы использовать неизмененные координаты текстуры, переданные в программу построения теней, происходят динамические поиски текстуры, также известные как зависимые чтения текстуры. Зависимые чтения текстуры не поддерживаются ни по какой стоимости производительности на OpenGL ES способные к 3.0 аппаратные средства; на других устройствах зависимые чтения текстуры могут задержать загрузку данных элемента текстуры, сократив производительность. Когда программа построения теней не имеет никаких зависимых чтений текстуры, аппаратное обеспечение машинной графики может выбрать данные элемента текстуры с упреждением, прежде чем программа построения теней выполнится, скрывая часть задержки доступа к памяти.
Перечисление 10-7 показывает программу построения теней фрагмента, вычисляющую новые координаты текстуры. Вычисление в этом примере может легко быть выполнено в вершинном шейдере, вместо этого. Путем перемещения вычисления в вершинный шейдер и непосредственно использования вычисленных координат текстуры вершинного шейдера, Вы избегаете зависимого чтения текстуры.
Зависимое чтение текстуры перечисления 10-7
varying vec2 vTexCoord; |
uniform sampler2D textureSampler; |
void main() |
{ |
vec2 modifiedTexCoord = vec2(1.0 - vTexCoord.x, 1.0 - vTexCoord.y); |
gl_FragColor = texture2D(textureSampler, modifiedTexCoord); |
} |
Выберите данные кадрового буфера для программируемого смешивания
Традиционный OpenGL и OpenGL реализации ES обеспечивают этап смешивания стандартной функции, проиллюстрированный на рисунке 10-1. Прежде, чем издать приказ получения, Вы указываете смешивающуюся работу от фиксированного набора возможных параметров. После того, как Ваши выводы программы построения теней фрагмента окрашивают данные для пикселя, OpenGL, этап смешивания ES считывает цветные данные для соответствующего пикселя в целевом кадровом буфере, затем комбинирует два согласно указанной работе смешивания для создания цвета вывода.
В iOS 6.0 и позже, можно использовать
расширение для реализации программируемого смешивания и других эффектов. Вместо того, чтобы предоставить исходный цвет, который будет смешан OpenGL ES, Ваша программа построения теней фрагмента читает содержание целевого кадрового буфера, соответствующего обрабатываемому фрагменту. Ваша программа построения теней фрагмента может тогда использовать любой алгоритм, Вы принимаете решение произвести цвет вывода, как показано на рисунке 10-2.EXT_shader_framebuffer_fetch
Это расширение включает, многие усовершенствовали методы рендеринга:
Дополнительные режимы наложения. Путем определения собственного ES GLSL функционирует для объединения источника и целевых цветов, можно реализовать режимы наложения, не возможные с OpenGL этап смешивания стандартной функции ES. Например, Перечисление 10-8 реализует режимы наложения Наложения и Различия, найденные в популярном графическом программном обеспечении.
Эффекты последующей обработки. После рендеринга сцены можно нарисовать полноэкранную четверку с помощью программы построения теней фрагмента, читающей, текущий фрагмент окрашивают, и преобразовывает его для создания цвета вывода. Программа построения теней в Перечислении 10-9 может использоваться с этим методом для преобразования сцены в шкалу полутонов.
Нецветные операции фрагмента. Кадровые буферы могут содержать нецветные данные. Например, задержанные алгоритмы штриховки используют многократные цели рендеринга для хранения глубины и нормальной информации. Ваша программа построения теней фрагмента может считать такие данные из одного (или больше) цели рендеринга и использовать их для создания цвета вывода в другой цели рендеринга.
Эти эффекты возможны без расширения выборки кадрового буфера — например, полутоновое преобразование может быть сделано путем рендеринга сцены в текстуру, затем рисования полноэкранной четверки с помощью той текстуры и программы построения теней фрагмента, преобразовывающей цвета элемента текстуры в шкалу полутонов. Однако использование этого расширения обычно приводит к лучшей производительности.
Для активации этой опции программа построения теней фрагмента должна объявить, что это требует EXT_shader_framebuffer_fetch
расширение, как показано в Перечислении 10-8 и Перечислении 10-9. Код программы построения теней для реализования этой опции отличается между версиями OpenGL Язык Штриховки ES (GLSL ES).
Используя выборку кадрового буфера в GLSL ES 1.0
Для контекстов OpenGL ES 2.0 и контекстов OpenGL ES 3.0, не используя #version 300 es
программы построения теней, Вы используете gl_FragColor
встроенная переменная для вывода программы построения теней фрагмента и gl_LastFragData
встроенная переменная для чтения данных кадрового буфера, как проиллюстрировано в Перечислении 10-8.
Программа построения теней Фрагмента перечисления 10-8 для программируемого, смешивающего GLSL ES 1.0
#extension GL_EXT_shader_framebuffer_fetch : require |
#define kBlendModeDifference 1 |
#define kBlendModeOverlay 2 |
#define BlendOverlay(a, b) ( (b<0.5) ? (2.0*b*a) : (1.0-2.0*(1.0-a)*(1.0-b)) ) |
uniform int blendMode; |
varying lowp vec4 sourceColor; |
void main() |
{ |
lowp vec4 destColor = gl_LastFragData[0]; |
if (blendMode == kBlendModeDifference) { |
gl_FragColor = abs( destColor - sourceColor ); |
} else if (blendMode == kBlendModeOverlay) { |
gl_FragColor.r = BlendOverlay(sourceColor.r, destColor.r); |
gl_FragColor.g = BlendOverlay(sourceColor.g, destColor.g); |
gl_FragColor.b = BlendOverlay(sourceColor.b, destColor.b); |
gl_FragColor.a = sourceColor.a; |
} else { // normal blending |
gl_FragColor = sourceColor; |
} |
} |
Используя выборку кадрового буфера в GLSL ES 3.0
В GLSL ES 3.0 Вы используете определяемые пользователем переменные, объявленные с out
спецификатор для выводов программы построения теней фрагмента. Если Вы объявляете выходную переменную программы построения теней фрагмента с inout
когда программа построения теней фрагмента выполнится, спецификатор, это будет содержать данные кадрового буфера. Перечисление 10-9 иллюстрирует полутоновый метод последующей обработки с помощью inout
переменная.
Программа построения теней Фрагмента перечисления 10-9 для цветной последующей обработки в GLSL ES 3.0
#version 300 es |
#extension GL_EXT_shader_framebuffer_fetch : require |
layout(location = 0) inout lowp vec4 destColor; |
void main() |
{ |
lowp float luminance = dot(vec3(0.3, 0.59, 0.11), destColor.rgb); |
destColor.rgb = vec3(luminance); |
} |
Используйте текстуры для больших буферов памяти в вершинных шейдерах
В iOS 7.0 и позже, вершинные шейдеры могут читать из в настоящее время связанных модулей текстуры. Используя этот метод можно получить доступ к намного большим буферам памяти во время обработки вершины, включив высокую производительность для некоторых усовершенствованные методы рендеринга. Например:
Отображение смещения. Нарисуйте сетку с позициями вершины по умолчанию, затем читайте из текстуры в вершинном шейдере для изменения позиции каждой вершины. Перечисление 10-10 демонстрирует использование этого метода для генерации трехмерной геометрии от полутоновой текстуры карты высоты.
Инстанцированное получение. Как описано в Использовании Инстанцированное Получение для Минимизации Рисует Вызовы, инстанцированное получение может существенно сократить CPU наверху при рендеринге сцены, содержащей много подобных объектов. Однако предоставление информации на экземпляр вершинному шейдеру может быть проблемой. Текстура может хранить обширную информацию для многих экземпляров. Например, Вы могли представить обширный городской пейзаж путем рисования сотен экземпляров из данных вершины, описывающих только простой куб. Для каждого экземпляра вершинный шейдер мог использовать
gl_InstanceID
переменная к выборке от текстуры, получая матрицу преобразования, окрашивает изменение, смещение координаты текстуры и изменение высоты для применения к каждому зданию.
Вершинный шейдер перечисления 10-10 для рендеринга из карты высоты
attribute vec2 xzPos; |
uniform mat4 modelViewProjectionMatrix; |
uniform sampler2D heightMap; |
void main() |
{ |
// Use the vertex X and Z values to look up a Y value in the texture. |
vec4 position = texture2D(heightMap, xzPos); |
// Put the X and Z values into their places in the position vector. |
position.xz = xzPos; |
// Transform the position vector from model to clip space. |
gl_Position = modelViewProjectionMatrix * position; |
} |
Можно также использовать универсальные массивы и универсальные буферные объекты (в OpenGL ES 3.0) для предоставления объемных данных вершинному шейдеру, но доступ текстуры вершины предлагает несколько потенциальных преимуществ. Можно хранить намного больше данных в текстуре, чем или в универсальном массиве или в универсальном буферном объекте, и можно использовать текстуру переносящиеся и фильтрующие опции интерполировать данные, хранившие в текстуре. Кроме того, можно представить к текстуре, использовав в своих интересах GPU для создания данных для использования в более поздней стадии обработки вершины.
Чтобы определить, доступна ли выборка текстуры вершины на устройстве (и число модулей текстуры, доступных вершинным шейдерам), проверьте значение MAX_VERTEX_TEXTURE_IMAGE_UNITS
предел во время выполнения. (См. Проверку OpenGL Возможности ES.)