Я сделал именно это сам, и я вижу несколько вещей, которые можно было бы здесь оптимизировать.
Во-первых, я бы удалил условное выражение enableTexture
и вместо этого разделил ваш шейдер на две программы, одну для истинного состояния этого и одну для ложного. Условные выражения очень затратны в фрагментных шейдерах iOS, особенно в тех, в которых есть чтение текстуры.
Во-вторых, у вас есть девять зависимых считываний текстур. Это чтение текстуры, при котором координаты текстуры вычисляются в фрагментном шейдере. Зависимое чтение текстур очень затратно для графических процессоров PowerVR на устройствах iOS, потому что они не позволяют оборудованию оптимизировать чтение текстур с помощью кэширования и т. д. Поскольку вы производите выборку с фиксированным смещением для 8 окружающих пикселей и одного центрального, эти вычисления должны быть переместился в вершинный шейдер. Это также означает, что эти вычисления не нужно будет выполнять для каждого пикселя, а только один раз для каждой вершины, а затем аппаратная интерполяция сделает все остальное.
В-третьих, циклы for() до сих пор не очень хорошо обрабатывались компилятором шейдеров iOS, поэтому я стараюсь избегать тех, где могу.
Как я уже упоминал, я делал такие шейдеры свертки в своей среде iOS с открытым исходным кодом GPUImage. Для универсального фильтра свертки я использую следующий вершинный шейдер:
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
задает веса для различных выборок в вашей свертке.
На iPhone 4 это выполняется за 4-8 мс для кадра видео с камеры 640x480, что достаточно для рендеринга 60 кадров в секунду при таком размере изображения. Если вам просто нужно сделать что-то вроде обнаружения краев, вы можете упростить описанное выше, преобразовать изображение в яркость на предварительном проходе, а затем сэмплировать только из одного цветового канала. Это еще быстрее, около 2 мс на кадр на том же устройстве.
person
Brad Larson
schedule
18.09.2012