BradLarson / GPUImage

An open source iOS framework for GPU-based image and video processing
http://www.sunsetlakesoftware.com/2012/02/12/introducing-gpuimage-framework
BSD 3-Clause "New" or "Revised" License
20.23k stars 4.61k forks source link

Uniform arrays always full of zeroes #847

Open GarrettAlbright opened 11 years ago

GarrettAlbright commented 11 years ago

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
GarrettAlbright commented 11 years ago

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.

bensmiley commented 10 years ago

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];
sagarthecoder commented 3 years ago

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 .