Ответ 1
Я сделал эту самую вещь сам, и я вижу несколько вещей, которые можно было бы оптимизировать здесь.
Во-первых, я бы удалил условный enableTexture
и вместо этого разделил ваш шейдер на две программы, один для истинного состояния этого и один для false. Условные обозначения очень дороги в шейдерах фрагментов iOS, особенно в тех, которые имеют текстуры в них.
Во-вторых, здесь есть девять зависимых текстур. Это текстуры, в которых координаты текстуры вычисляются внутри шейдера фрагмента. Зависимые чтения текстур очень велики на графических процессорах PowerVR в устройствах iOS, поскольку они не позволяют аппаратным средствам оптимизировать считывание текстуры с использованием кеширования и т.д. Поскольку вы взяли выборку из фиксированного смещения для 8 окружающих пикселей и одного центрального, эти вычисления должны быть переместился в вершинный шейдер. Это также означает, что эти вычисления не должны выполняться для каждого пикселя, только один раз для каждой вершины, а затем аппаратная интерполяция будет обрабатывать остальные.
В-третьих, для циклов for() все обработанные шейдерным компилятором iOS не были обработаны до настоящего времени, поэтому я стараюсь избегать тех, где могу.
Как я уже упоминал, я сделал сверточные шейдеры, как это, в моем open-source iOS GPUImage framework. Для общего фильтра свертки я использую следующий вершинный шейдер:
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
uniform highp float texelWidth;
uniform highp float texelHeight;
varying vec2 textureCoordinate;
varying vec2 leftTextureCoordinate;
varying vec2 rightTextureCoordinate;
varying vec2 topTextureCoordinate;
varying vec2 topLeftTextureCoordinate;
varying vec2 topRightTextureCoordinate;
varying vec2 bottomTextureCoordinate;
varying vec2 bottomLeftTextureCoordinate;
varying vec2 bottomRightTextureCoordinate;
void main()
{
gl_Position = position;
vec2 widthStep = vec2(texelWidth, 0.0);
vec2 heightStep = vec2(0.0, texelHeight);
vec2 widthHeightStep = vec2(texelWidth, texelHeight);
vec2 widthNegativeHeightStep = vec2(texelWidth, -texelHeight);
textureCoordinate = inputTextureCoordinate.xy;
leftTextureCoordinate = inputTextureCoordinate.xy - widthStep;
rightTextureCoordinate = inputTextureCoordinate.xy + widthStep;
topTextureCoordinate = inputTextureCoordinate.xy - heightStep;
topLeftTextureCoordinate = inputTextureCoordinate.xy - widthHeightStep;
topRightTextureCoordinate = inputTextureCoordinate.xy + widthNegativeHeightStep;
bottomTextureCoordinate = inputTextureCoordinate.xy + heightStep;
bottomLeftTextureCoordinate = inputTextureCoordinate.xy - widthNegativeHeightStep;
bottomRightTextureCoordinate = inputTextureCoordinate.xy + widthHeightStep;
}
и следующий шейдер фрагмента:
precision highp float;
uniform sampler2D inputImageTexture;
uniform mediump mat3 convolutionMatrix;
varying vec2 textureCoordinate;
varying vec2 leftTextureCoordinate;
varying vec2 rightTextureCoordinate;
varying vec2 topTextureCoordinate;
varying vec2 topLeftTextureCoordinate;
varying vec2 topRightTextureCoordinate;
varying vec2 bottomTextureCoordinate;
varying vec2 bottomLeftTextureCoordinate;
varying vec2 bottomRightTextureCoordinate;
void main()
{
mediump vec4 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate);
mediump vec4 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate);
mediump vec4 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate);
mediump vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
mediump vec4 leftColor = texture2D(inputImageTexture, leftTextureCoordinate);
mediump vec4 rightColor = texture2D(inputImageTexture, rightTextureCoordinate);
mediump vec4 topColor = texture2D(inputImageTexture, topTextureCoordinate);
mediump vec4 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate);
mediump vec4 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate);
mediump vec4 resultColor = topLeftColor * convolutionMatrix[0][0] + topColor * convolutionMatrix[0][1] + topRightColor * convolutionMatrix[0][2];
resultColor += leftColor * convolutionMatrix[1][0] + centerColor * convolutionMatrix[1][1] + rightColor * convolutionMatrix[1][2];
resultColor += bottomLeftColor * convolutionMatrix[2][0] + bottomColor * convolutionMatrix[2][1] + bottomRightColor * convolutionMatrix[2][2];
gl_FragColor = resultColor;
}
Формы texelWidth
и texelHeight
являются инверсией ширины и высоты входного изображения, а convolutionMatrix
uniform задает весы для различных образцов в вашей свертке.
На iPhone 4 это работает в 4-8 мс для кадра видеоизображения 640x480, что достаточно для 60 кадров в секунду при этом размере изображения. Если вам просто нужно сделать что-то вроде обнаружения края, вы можете упростить это, преобразовать изображение в яркость в предварительный проход, а затем только образец из одного цветового канала. Это еще быстрее, примерно на 2 мс на кадр на одном устройстве.