iPhone Differences

3 replies [Last post]
vipersnake
User offline. Last seen 8 years 39 weeks ago. Offline
Joined: 06/15/2012
Posts:

So I wrote this really basic shader that does a box blur. Works great on the iPhone 4s but on the iPhone 3GS, iPhone 4, or iPad 1 I get this really bad banding after multiple passes (seems to compound)

I made sure that all my textures are less than the 2k limit required for those devices.

Both images are attached to see the issues.

The shader is super basic. It is nothing more than a box blur that has hard coded of 9 pixels (4 on the left and right plus the center) and a multiplier (so you can get a bigger radius). I run over this multiple times as needed for a larger radius.

I do do a a trick where I only have to get 2 pixels on the left and right because I count on the hardware bilinear filter to mix the two. So center + 1.5, center + 3.5 and center - 1.5 and center -3.5. Not sure if this is not supported on the older hardware or not or has anything to do with it.

This same issue popped its head up on all devices using the fast blur included with the framework until I changed the lowp fragment to highp for multiple passes (again to get a larger blur radius of say 25 or so). This same fix does not work on mine as all the variables are highp.

Brad Larson
Brad Larson's picture
User offline. Last seen 4 years 22 weeks ago. Offline
Joined: 05/14/2008
Posts:

Reducing the shader precision definitely can introduce rounding artifacts on the various devices. They all handle rounding slightly different from one another, and lowering the precision to 8 bits in the lowp case could be exposing some of these differences.

vipersnake
User offline. Last seen 8 years 39 weeks ago. Offline
Joined: 06/15/2012
Posts:

Can someone double check this code and see if there is something wrong with precision or rounding. It is just a super basic 8 radius box blur. On iPhone 4S it works flawlessly with 1 pass and radius of 1 (radius is actually a multiplier that reduces quality when higher than 1 and not really needed but I added it for convenience) however on 3GS you can see bands where it looks like it is skipping rows of pixels and it gets banded in gradients like blue sky. Bump it up to 2 passes and it compounds the banding (although it hides the striping)

Blurpasses is used to run through the filter multiple times.

@interface GPUImageBox8BlurFilter : GPUImageTwoPassTextureSamplingFilter
{
	GLint firstBlurSpreadUniform, secondBlurSpreadUniform;
}
 
@property(readwrite, nonatomic) NSUInteger blurPasses;
@property(readwrite, nonatomic) float blurSpread;
 
- (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
 
 
@end
 
 
NSString *const kBox8BlurFilterVertexShaderString = SHADER_STRING
(
 attribute highp vec4 position;
 attribute highp vec2 inputTextureCoordinate;
 
 uniform highp float texelWidthOffset; 
 uniform highp float texelHeightOffset; 
 
 uniform highp float blurSpread;
 
 varying highp vec2 centerTextureCoordinate;
 varying highp vec2 oneStepLeftTextureCoordinate;
 varying highp vec2 oneStepRightTextureCoordinate;
 varying highp vec2 twoStepsLeftTextureCoordinate;
 varying highp vec2 twoStepsRightTextureCoordinate;
 varying highp vec2 threeStepsLeftTextureCoordinate;
 varying highp vec2 threeStepsRightTextureCoordinate;
 varying highp vec2 fourStepsLeftTextureCoordinate;
 varying highp vec2 fourStepsRightTextureCoordinate;
 
 void main()
 {
     gl_Position = position;
 
     vec2 firstOffset = vec2(1.5 * texelWidthOffset, 1.5 * texelHeightOffset) * blurSpread;
     vec2 secondOffset = vec2(3.5 * texelWidthOffset, 3.5 * texelHeightOffset) * blurSpread;
     vec2 thirdOffset = vec2(5.5 * texelWidthOffset, 5.5 * texelHeightOffset) * blurSpread;
     vec2 fourthOffset = vec2(7.5 * texelWidthOffset, 7.5 * texelHeightOffset) * blurSpread;
 
     centerTextureCoordinate = inputTextureCoordinate;
     oneStepLeftTextureCoordinate = inputTextureCoordinate - firstOffset;
     oneStepRightTextureCoordinate = inputTextureCoordinate + firstOffset;
     twoStepsLeftTextureCoordinate = inputTextureCoordinate - secondOffset;
     twoStepsRightTextureCoordinate = inputTextureCoordinate + secondOffset;
 
	 threeStepsLeftTextureCoordinate = inputTextureCoordinate - thirdOffset;
     threeStepsRightTextureCoordinate = inputTextureCoordinate + thirdOffset;
     fourStepsLeftTextureCoordinate = inputTextureCoordinate - fourthOffset;
     fourStepsRightTextureCoordinate = inputTextureCoordinate + fourthOffset;
 
 }
 );
 
 
NSString *const kBox8BlurFilterFragmentShaderString = SHADER_STRING
(
 precision highp float;
 
 uniform sampler2D inputImageTexture;
 
 varying highp vec2 centerTextureCoordinate;
 varying highp vec2 oneStepLeftTextureCoordinate;
 varying highp vec2 oneStepRightTextureCoordinate;
 varying highp vec2 twoStepsLeftTextureCoordinate;
 varying highp vec2 twoStepsRightTextureCoordinate;
 varying highp vec2 threeStepsLeftTextureCoordinate;
 varying highp vec2 threeStepsRightTextureCoordinate;
 varying highp vec2 fourStepsLeftTextureCoordinate;
 varying highp vec2 fourStepsRightTextureCoordinate;
 
 void main()
 {
     highp vec4 fragmentColor = texture2D(inputImageTexture, centerTextureCoordinate) * 0.06;
     fragmentColor += texture2D(inputImageTexture, oneStepLeftTextureCoordinate) * 0.1175;
     fragmentColor += texture2D(inputImageTexture, oneStepRightTextureCoordinate) * 0.1175;
	 fragmentColor += texture2D(inputImageTexture, twoStepsLeftTextureCoordinate) * 0.1175;
     fragmentColor += texture2D(inputImageTexture, twoStepsRightTextureCoordinate) * 0.1175;
 
	 fragmentColor += texture2D(inputImageTexture, threeStepsLeftTextureCoordinate) * 0.1175;
     fragmentColor += texture2D(inputImageTexture, threeStepsRightTextureCoordinate) * 0.1175;
	 fragmentColor += texture2D(inputImageTexture, fourStepsLeftTextureCoordinate) * 0.1175;
     fragmentColor += texture2D(inputImageTexture, fourStepsRightTextureCoordinate) * 0.1175;
 
     gl_FragColor = fragmentColor;
 }
 );
 
 
@implementation GPUImageBox8BlurFilter
 
@synthesize blurPasses = _blurPasses;
@synthesize blurSpread = _blurSpread;
 
- (id)init;
{
    if (!(self = [self initWithFragmentShaderFromString:kBox8BlurFilterFragmentShaderString]))
    {
		return nil;
    }
 
 
 
    return self;
}
 
- (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
{
    if (!(self = [super initWithFirstStageVertexShaderFromString:kBox8BlurFilterVertexShaderString firstStageFragmentShaderFromString:fragmentShaderString secondStageVertexShaderFromString:kBox8BlurFilterVertexShaderString secondStageFragmentShaderFromString:fragmentShaderString]))
    {
		return nil;
    }
 
	firstBlurSpreadUniform = [filterProgram uniformIndex:@"blurSpread"];    
    secondBlurSpreadUniform = [secondFilterProgram uniformIndex:@"blurSpread"];
 
    self.blurSpread = 1.0;
 
 
    return self;
}
 
 
- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates sourceTexture:(GLuint)sourceTexture;
{
    [super renderToTextureWithVertices:vertices textureCoordinates:textureCoordinates sourceTexture:sourceTexture];
 
    for (NSUInteger currentAdditionalBlurPass = 1; currentAdditionalBlurPass < _blurPasses; currentAdditionalBlurPass++)
    {
        [super renderToTextureWithVertices:vertices textureCoordinates:[[self class] textureCoordinatesForRotation:kGPUImageNoRotation] sourceTexture:secondFilterOutputTexture];
    }
}
 
- (void)setBlurSpread:(CGFloat)newValue;
{
    _blurSpread = newValue;
 
    [GPUImageOpenGLESContext useImageProcessingContext];
    [filterProgram use];
    glUniform1f(firstBlurSpreadUniform, _blurSpread);
 
    [secondFilterProgram use];
    glUniform1f(secondBlurSpreadUniform, _blurSpread);
}
 
 
@end

vipersnake
User offline. Last seen 8 years 39 weeks ago. Offline
Joined: 06/15/2012
Posts:

Here is a corrected fragment shader that works on iPhone 3GS and iPad1

NSString *const kBox8BlurFilterFragmentShaderString = SHADER_STRING
(
 precision highp float;
 
 uniform sampler2D inputImageTexture;
 
 varying highp vec2 centerTextureCoordinate;
 varying highp vec2 oneStepLeftTextureCoordinate;
 varying highp vec2 oneStepRightTextureCoordinate;
 varying highp vec2 twoStepsLeftTextureCoordinate;
 varying highp vec2 twoStepsRightTextureCoordinate;
 varying highp vec2 threeStepsLeftTextureCoordinate;
 varying highp vec2 threeStepsRightTextureCoordinate;
 varying highp vec2 fourStepsLeftTextureCoordinate;
 varying highp vec2 fourStepsRightTextureCoordinate;
 
 void main()
 {
     highp vec4 fragmentColor = texture2D(inputImageTexture, centerTextureCoordinate) * 0.5;
     fragmentColor += texture2D(inputImageTexture, oneStepLeftTextureCoordinate);
     fragmentColor += texture2D(inputImageTexture, oneStepRightTextureCoordinate);
	 fragmentColor += texture2D(inputImageTexture, twoStepsLeftTextureCoordinate);
     fragmentColor += texture2D(inputImageTexture, twoStepsRightTextureCoordinate);
 
	 fragmentColor += texture2D(inputImageTexture, threeStepsLeftTextureCoordinate);
     fragmentColor += texture2D(inputImageTexture, threeStepsRightTextureCoordinate);
	 fragmentColor += texture2D(inputImageTexture, fourStepsLeftTextureCoordinate);
     fragmentColor += texture2D(inputImageTexture, fourStepsRightTextureCoordinate);
 
     gl_FragColor = fragmentColor / 8.5;
 }
 );

Syndicate content