Last active
August 29, 2015 14:10
-
-
Save membersheep/bbf5f7d0b524913b592e to your computer and use it in GitHub Desktop.
SXRadialBlurFilter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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