Uniform arrays always full of zeroes
I'm working on implementing ordered dithering using a Bayer matrix in a filter. I got it working just fine using a mat4, but now I'm trying to re-implement it to use an array instead because I want to see if I can get better quality using an 8x8 array instead of a 4x4, and I don't need any of the special matrix handling functionality anyway (though at this point I'm just trying to reproduce the 4x4 array result). However, try as I might, all that seems to happen is I end up with an array full of zeroes inside my GLSL script, or effectively so. Am I doing something wrong?
Compounding the frustration is the fact that if I build a simpler example which passes an array like GLfloat values[1] = {1.0} and draw a green pixel if values[0] >= 0 or a red one otherwise, I get a green box - so things work as expected in a uselessly simple example, but not in my actual code. Argh!
The below code is where I am now. It works as expected if you uncomment out the mat4-using code and comment out the array code, but as is it just draws white.
BFBWOrderedDither.h
#import "GPUImageFilter.h"
@interface BFBWOrderedDither : GPUImageFilter {
GLint texelWidthUniform, texelHeightUniform;
}
@end
BFBWOrderedDither.m
#import "BFBWOrderedDither.h"
#import "GPUImageFilter.h"
NSString *const kBFBWOrderedDither = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
// uniform lowp mat4 bayerMatrix;
uniform lowp float bayerArray[16];
uniform lowp float texelWidth;
uniform lowp float texelHeight;
void main()
{
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
mediump int x = int(mod(textureCoordinate.x / texelWidth, 4.0));
mediump int y = int(mod(textureCoordinate.y / texelHeight, 4.0));
mediump int arrayIndex = (y * 4) + x;
// if (textureColor.r >= bayerMatrix[x][y]) {
if (textureColor.r >= bayerArray[arrayIndex]) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
else {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
}
);
@implementation BFBWOrderedDither
-(id)init {
self = [self initWithFragmentShaderFromString:kBFBWOrderedDither];
if (self) {
texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
float multiplier = 1.0/17.0;
// GPUMatrix4x4 bayerMatrix = {
// {1 * multiplier, 9 * multiplier, 3 * multiplier, 11 * multiplier},
// {13 * multiplier, 5 * multiplier, 15 * multiplier, 7 * multiplier},
// {4 * multiplier, 12 * multiplier, 2 * multiplier, 10 * multiplier},
// {16 * multiplier, 8 * multiplier, 14 * multiplier, 6 * multiplier}
// };
// GLint bayerMatrixUniform = [filterProgram uniformIndex:@"bayerMatrix"];
// [self setMatrix4f:bayerMatrix forUniform:bayerMatrixUniform program:filterProgram];
GLfloat matrixAsArray[16] = {
1 * multiplier, 9 * multiplier, 3 * multiplier, 11 * multiplier,
13 * multiplier, 5 * multiplier, 15 * multiplier, 7 * multiplier,
4 * multiplier, 12 * multiplier, 2 * multiplier, 10 * multiplier,
16 * multiplier, 8 * multiplier, 14 * multiplier, 6 * multiplier
};
GLsizei length = 16;
GLint bayerArrayUniform = [filterProgram uniformIndex:@"bayerArray"];
[self setFloatArray:matrixAsArray length:length forUniform:bayerArrayUniform program:filterProgram];
}
return self;
}
- (void)setupFilterForSize:(CGSize)filterFrameSize
{
[super setupFilterForSize:filterFrameSize];
CGFloat texelWidth = 1.0 / filterFrameSize.width;
CGFloat texelHeight = 1.0 / filterFrameSize.height;
runSynchronouslyOnVideoProcessingQueue(^{
[self setFloat:texelWidth forUniform:texelWidthUniform program:filterProgram];
[self setFloat:texelHeight forUniform:texelHeightUniform program:filterProgram];
});
}
@end
I set this project aside for a while to work on another one; having finished that one, I'm coming back to this one.
I think what I may be looking at is a race condition somewhere. If I change the if clause in the main() of the shader to simply be
if (bayerArray[0] > 0.0) {
…then sometimes it paints a white rectangle, and sometimes a black one. It should always paint a white one. So I'm wondering if at times the array is not being passed the proper values before the filter runs, resulting in the black rectangle.
I tried changing runSynchronouslyOnVideoProcessingQueue() to runAsynchronouslyOnVideoProcessingQueue(), but it didn't seem to have any effect.
I think there's a slight error in your code. The setFloatArray function takes a pointer as the first argument. Something like this worked for me:
GLsizei length = 4;
GLfloat * matrixAsArray = calloc(length, sizeof(GLfloat));
matrixAsArray[0] = 1.0;
GLint smudgeArrayUniform = [filterProgram uniformIndex:@"smudgeArray"];
[self setFloatArray:matrixAsArray length:length forUniform:smudgeArrayUniform program:filterProgram];
Hi, I run your code for 4x4 matrix. But it produce a White filter. No Image is shown. Can you help me? I need to use 4x4 ordered dithering filter to Images .