Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
SXRadialBlurFilter
//
// SXRadialBlurFilter.h
//
// Created by Alessandro Maroso on 27/11/14.
//
//
#import <Sparrow/Sparrow.h>
@interface SXRadialBlurFilter : SPFragmentFilter
/// --------------------
/// @name Initialization
/// --------------------
/// Factory method.
+ (instancetype)blurFilter;
/// ----------------
/// @name Properties
/// ----------------
@property (nonatomic, assign) float blur;
@property (nonatomic, assign) float distance;
@property (nonatomic, assign) float strength;
@property (nonatomic, strong) SPPoint *blurCenterPosition;
@end
//
// SXRadialBlurFilter.m
//
// Created by Alessandro Maroso on 27/11/14.
//
// based on shader program described here:
// http://www.gamerendering.com/2008/12/20/radial-blur-filter/
//
#import "SXRadialBlurFilter.h"
#pragma mark - SXRadialBlurProgram
@interface SXRadialBlurProgram : SPProgram
- (instancetype)initWithFragmentShader;
@property (nonatomic, readonly) int aPosition;
@property (nonatomic, readonly) int aTexCoords;
@property (nonatomic, readonly) int uMvpMatrix;
@property (nonatomic, readonly) int uBlurCenter;
@property (nonatomic, readonly) int uDistance;
@property (nonatomic, readonly) int uStrength;
@end
// --- blur implementation -------------------------------------------------------------------------
@implementation SXRadialBlurProgram
{
int _aPosition;
int _aTexCoords;
int _uMvpMatrix;
int _uBlurCenter;
int _uDistance;
int _uStrength;
}
#pragma mark Initialization
- (instancetype)initWithFragmentShader
{
if (self = [super initWithVertexShader:[self vertexShader]
fragmentShader:[self fragmentShader]])
{
_aPosition = [self attributeByName:@"aPosition"];
_aTexCoords = [self attributeByName:@"aTexCoords"];
_uMvpMatrix = [self uniformByName:@"uMvpMatrix"];
_uBlurCenter = [self uniformByName:@"uBlurCenter"];
_uDistance = [self uniformByName:@"uDistance"];
_uStrength = [self uniformByName:@"uStrength"];
}
return self;
}
#pragma mark Methods
- (NSString *)vertexShader
{
NSMutableString *vertSource = [NSMutableString string];
// attributes
// Current vertex position
[vertSource appendLine:@"attribute vec4 aPosition;"];
// Texture position connected to this vertex
[vertSource appendLine:@"attribute lowp vec2 aTexCoords;"];
// uniforms
// Projection and camera viewing matrices
[vertSource appendLine:@"uniform mat4 uMvpMatrix;"];
// varying
// Output vertex position
[vertSource appendLine:@"varying lowp vec2 vTexCoords;"];
// main
[vertSource appendLine:@"void main() {"];
// 4x4 matrix transform to output space
[vertSource appendLine:@" gl_Position = uMvpMatrix * aPosition;"];
// Send position to fragment shader without changing it
[vertSource appendLine:@" vTexCoords = aTexCoords;"];
[vertSource appendLine:@"}"];
return vertSource;
}
- (NSString *)fragmentShader
{
NSMutableString *fragSource = [NSMutableString string];
[fragSource appendLine:@"precision mediump float;"];
// variables
// Input vertex position
[fragSource appendLine:@"varying lowp vec2 vTexCoords;"];
// Samplers are types representing textures.
// They are used for texture sampling. Sampler types have to be uniform.
[fragSource appendLine:@"uniform sampler2D uTexture;"];
// The center of the radial blur effect
[fragSource appendLine:@"uniform lowp vec2 uBlurCenter;"];
// some const, tweak for best look
[fragSource appendLine:@"uniform float uDistance;"];
[fragSource appendLine:@"uniform float uStrength;"];
// main
[fragSource appendLine:@"void main() {"];
// some sample positions
[fragSource appendLine:@" float samples[10];"];
[fragSource appendLine:@" samples[0] = -0.08;"];
[fragSource appendLine:@" samples[1] = -0.05;"];
[fragSource appendLine:@" samples[2] = -0.03;"];
[fragSource appendLine:@" samples[3] = -0.02;"];
[fragSource appendLine:@" samples[4] = -0.01;"];
[fragSource appendLine:@" samples[5] = 0.01;"];
[fragSource appendLine:@" samples[6] = 0.02;"];
[fragSource appendLine:@" samples[7] = 0.03;"];
[fragSource appendLine:@" samples[8] = 0.05;"];
[fragSource appendLine:@" samples[9] = 0.08;"];
// substracting vTexCoords from the center will result in a vector pointing to the middle of the screen from the fragment
[fragSource appendLine:@" vec2 dir = vTexCoords - uBlurCenter;"];
// calculate the distance to the center of the screen
[fragSource appendLine:@" float dist = sqrt(dir.x*dir.x + dir.y*dir.y);"];
// normalize the direction (reuse the distance)
[fragSource appendLine:@" dir = dir/dist; "];
// this is the original colour of this fragment, using only this would result in a nonblurred version
[fragSource appendLine:@" vec4 color = texture2D(uTexture,vTexCoords);"];
[fragSource appendLine:@" vec4 sum = color;"];
// take 10 additional blur samples in the direction towards
// the center of the screen
[fragSource appendLine:@" for (int i = 0; i < 10; i++)"];
[fragSource appendLine:@" {sum += texture2D( uTexture, vTexCoords + dir * samples[i] * uDistance );}"];
// we have taken eleven samples, make the average value
[fragSource appendLine:@" sum *= 1.0/11.0;"];
// weighten the blur effect with the distance to the center of the screen ( further out is blurred more), this is optional
[fragSource appendLine:@" float t = dist * uStrength;"];
[fragSource appendLine:@" t = clamp( t ,0.0,1.0);"]; // constrain t between 0.0 and 1.0
// Blend the original color with the averaged pixels
// mix function linearly interpolates between two values
[fragSource appendLine:@" gl_FragColor = mix( color, sum, t );"];
// end framgent shader program
[fragSource appendLine:@"}"];
return fragSource;
}
+ (NSString *)programNameForTinting:(BOOL)tinting
{
return @"SXRadialBlurProgram#00";
}
@end
#pragma mark - SPBlurFilter
@interface SPBlurFilter ()
- (void)updateParamatersWithPass:(int)pass texWidth:(int)texWidth texHeight:(int)texHeight;
- (void)updateMarginsAndPasses;
@end
// --- class implementation ------------------------------------------------------------------------
@implementation SXRadialBlurFilter
{
float _blurCenter[2];
SXRadialBlurProgram *_program;
}
#pragma mark Initialization
- (instancetype)init
{
if ((self = [super initWithNumPasses:1 resolution:1.0f]))
{
_blur = 1.0f;
_distance = 1.0f;
_strength = 2.0f;
[self updateMarginsAndPasses];
}
return self;
}
- (void)dealloc
{
[_program release];
[super dealloc];
}
+ (instancetype)blurFilter
{
return [[[self alloc] init] autorelease];
}
#pragma mark SPFragmentFilter (Subclasses)
- (void)createPrograms
{
if (!_program)
{
NSString *programName = [SXRadialBlurProgram programNameForTinting:NO];
_program = (SXRadialBlurProgram *)[[[Sparrow currentController] programByName:programName] retain];
if (!_program)
{
_program = [[SXRadialBlurProgram alloc] initWithFragmentShader];
[[Sparrow currentController] registerProgram:_program name:programName];
}
}
self.vertexPosID = _program.aPosition;
self.texCoordsID = _program.aTexCoords;
}
- (void)activateWithPass:(int)pass texture:(SPTexture *)texture mvpMatrix:(SPMatrix *)matrix
{
[self updateParamatersWithPass:pass texWidth:texture.nativeWidth texHeight:texture.nativeHeight];
SXRadialBlurProgram *program = _program;
glUseProgram(program.name);
GLKMatrix4 mvp = [matrix convertToGLKMatrix4];
glUniformMatrix4fv(program.uMvpMatrix, 1, false, mvp.m);
glUniform2fv(program.uBlurCenter, 1, _blurCenter);
glUniform1f(program.uDistance, _distance);
glUniform1f(program.uStrength, _strength);
}
#pragma mark Properties
- (void)setBlur:(float)blur
{
_blur = blur;
[self updateMarginsAndPasses];
}
#pragma mark Private
- (void)updateParamatersWithPass:(int)pass texWidth:(float)texWidth texHeight:(float)texHeight
{
_blurCenter[0] = self.blurCenterPosition.x/texWidth;
_blurCenter[1] = self.blurCenterPosition.y/texHeight;
}
- (void)updateMarginsAndPasses
{
if (_blur == 0)
_blur = 0.001;
self.numPasses = ceilf(_blur);
self.marginX = (3.0f + ceilf(_blur)) / self.resolution;
self.marginY = (3.0f + ceilf(_blur)) / self.resolution;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.